Coverage Report

Created: 2026-07-21 15:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/format/table/iceberg_reader_mixin.h
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#pragma once
19
20
#include <gen_cpp/ExternalTableSchema_types.h>
21
22
#include <cstddef>
23
#include <cstdint>
24
#include <memory>
25
#include <ranges>
26
#include <string>
27
#include <unordered_map>
28
#include <vector>
29
30
#include "common/consts.h"
31
#include "common/status.h"
32
#include "core/block/block.h"
33
#include "core/column/column_dictionary.h"
34
#include "core/column/column_nullable.h"
35
#include "core/column/column_string.h"
36
#include "core/column/column_struct.h"
37
#include "core/data_type/data_type_number.h"
38
#include "core/data_type/data_type_string.h"
39
#include "core/data_type/primitive_type.h"
40
#include "format/generic_reader.h"
41
#include "format/table/equality_delete.h"
42
#include "format/table/iceberg_delete_file_reader_helper.h"
43
#include "format/table/iceberg_scan_semantics.h"
44
#include "format/table/table_schema_change_helper.h"
45
#include "runtime/runtime_profile.h"
46
#include "runtime/runtime_state.h"
47
#include "storage/olap_common.h"
48
#include "util/url_coding.h"
49
50
namespace doris {
51
class TIcebergDeleteFileDesc;
52
} // namespace doris
53
54
namespace doris {
55
56
class ShardedKVCache;
57
58
// CRTP mixin for Iceberg reader functionality.
59
// BaseReader should be ParquetReader or OrcReader.
60
// Inherits BaseReader + TableSchemaChangeHelper, providing shared Iceberg logic
61
// (delete files, deletion vectors, equality delete, $row_id synthesis).
62
//
63
// Inheritance chain:
64
//   IcebergParquetReader -> IcebergReaderMixin<ParquetReader> -> ParquetReader -> GenericReader
65
//   IcebergOrcReader     -> IcebergReaderMixin<OrcReader>     -> OrcReader     -> GenericReader
66
template <typename BaseReader>
67
class IcebergReaderMixin : public BaseReader, public TableSchemaChangeHelper {
68
public:
69
    struct PositionDeleteRange {
70
        std::vector<std::string> data_file_path;
71
        std::vector<std::pair<int, int>> range;
72
    };
73
74
    // Forward BaseReader constructor arguments + Iceberg-specific kv_cache
75
    template <typename... Args>
76
    IcebergReaderMixin(ShardedKVCache* kv_cache, Args&&... args)
77
28
            : BaseReader(std::forward<Args>(args)...), _kv_cache(kv_cache) {
78
28
        static const char* iceberg_profile = "IcebergProfile";
79
28
        ADD_TIMER(this->get_profile(), iceberg_profile);
80
28
        _iceberg_profile.num_delete_files = ADD_CHILD_COUNTER(this->get_profile(), "NumDeleteFiles",
81
28
                                                              TUnit::UNIT, iceberg_profile);
82
28
        _iceberg_profile.num_delete_rows = ADD_CHILD_COUNTER(this->get_profile(), "NumDeleteRows",
83
28
                                                             TUnit::UNIT, iceberg_profile);
84
28
        _iceberg_profile.delete_files_read_time =
85
28
                ADD_CHILD_TIMER(this->get_profile(), "DeleteFileReadTime", iceberg_profile);
86
28
        _iceberg_profile.delete_rows_sort_time =
87
28
                ADD_CHILD_TIMER(this->get_profile(), "DeleteRowsSortTime", iceberg_profile);
88
28
        _iceberg_profile.parse_delete_file_time =
89
28
                ADD_CHILD_TIMER(this->get_profile(), "ParseDeleteFileTime", iceberg_profile);
90
28
        _iceberg_profile.decoded_cache_hit_count =
91
28
                ADD_CHILD_COUNTER(this->get_profile(), "DeletionVectorDecodedCacheHitCount",
92
28
                                  TUnit::UNIT, iceberg_profile);
93
28
        _iceberg_profile.decoded_cache_miss_count =
94
28
                ADD_CHILD_COUNTER(this->get_profile(), "DeletionVectorDecodedCacheMissCount",
95
28
                                  TUnit::UNIT, iceberg_profile);
96
28
        _iceberg_profile.file_cache_hit_count =
97
28
                ADD_CHILD_COUNTER(this->get_profile(), "DeletionVectorFileCacheHitCount",
98
28
                                  TUnit::UNIT, iceberg_profile);
99
28
        _iceberg_profile.file_cache_miss_count =
100
28
                ADD_CHILD_COUNTER(this->get_profile(), "DeletionVectorFileCacheMissCount",
101
28
                                  TUnit::UNIT, iceberg_profile);
102
28
        _iceberg_profile.file_cache_peer_read_count =
103
28
                ADD_CHILD_COUNTER(this->get_profile(), "DeletionVectorFileCachePeerReadCount",
104
28
                                  TUnit::UNIT, iceberg_profile);
105
28
    }
_ZN5doris18IcebergReaderMixinINS_13ParquetReaderEEC2IJRPNS_14RuntimeProfileERKNS_20TFileScanRangeParamsERKNS_14TFileRangeDescERmRPKN4cctz9time_zoneERPNS_2io9IOContextERPNS_12RuntimeStateERPNS_13FileMetaCacheEEEEPNS_14ShardedKVCacheEDpOT_
Line
Count
Source
77
18
            : BaseReader(std::forward<Args>(args)...), _kv_cache(kv_cache) {
78
18
        static const char* iceberg_profile = "IcebergProfile";
79
18
        ADD_TIMER(this->get_profile(), iceberg_profile);
80
18
        _iceberg_profile.num_delete_files = ADD_CHILD_COUNTER(this->get_profile(), "NumDeleteFiles",
81
18
                                                              TUnit::UNIT, iceberg_profile);
82
18
        _iceberg_profile.num_delete_rows = ADD_CHILD_COUNTER(this->get_profile(), "NumDeleteRows",
83
18
                                                             TUnit::UNIT, iceberg_profile);
84
18
        _iceberg_profile.delete_files_read_time =
85
18
                ADD_CHILD_TIMER(this->get_profile(), "DeleteFileReadTime", iceberg_profile);
86
18
        _iceberg_profile.delete_rows_sort_time =
87
18
                ADD_CHILD_TIMER(this->get_profile(), "DeleteRowsSortTime", iceberg_profile);
88
18
        _iceberg_profile.parse_delete_file_time =
89
18
                ADD_CHILD_TIMER(this->get_profile(), "ParseDeleteFileTime", iceberg_profile);
90
18
        _iceberg_profile.decoded_cache_hit_count =
91
18
                ADD_CHILD_COUNTER(this->get_profile(), "DeletionVectorDecodedCacheHitCount",
92
18
                                  TUnit::UNIT, iceberg_profile);
93
18
        _iceberg_profile.decoded_cache_miss_count =
94
18
                ADD_CHILD_COUNTER(this->get_profile(), "DeletionVectorDecodedCacheMissCount",
95
18
                                  TUnit::UNIT, iceberg_profile);
96
18
        _iceberg_profile.file_cache_hit_count =
97
18
                ADD_CHILD_COUNTER(this->get_profile(), "DeletionVectorFileCacheHitCount",
98
18
                                  TUnit::UNIT, iceberg_profile);
99
18
        _iceberg_profile.file_cache_miss_count =
100
18
                ADD_CHILD_COUNTER(this->get_profile(), "DeletionVectorFileCacheMissCount",
101
18
                                  TUnit::UNIT, iceberg_profile);
102
18
        _iceberg_profile.file_cache_peer_read_count =
103
18
                ADD_CHILD_COUNTER(this->get_profile(), "DeletionVectorFileCachePeerReadCount",
104
18
                                  TUnit::UNIT, iceberg_profile);
105
18
    }
_ZN5doris18IcebergReaderMixinINS_9OrcReaderEEC2IJRPNS_14RuntimeProfileERPNS_12RuntimeStateERKNS_20TFileScanRangeParamsERKNS_14TFileRangeDescERmRKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERPNS_2io9IOContextERPNS_13FileMetaCacheEEEEPNS_14ShardedKVCacheEDpOT_
Line
Count
Source
77
10
            : BaseReader(std::forward<Args>(args)...), _kv_cache(kv_cache) {
78
10
        static const char* iceberg_profile = "IcebergProfile";
79
10
        ADD_TIMER(this->get_profile(), iceberg_profile);
80
10
        _iceberg_profile.num_delete_files = ADD_CHILD_COUNTER(this->get_profile(), "NumDeleteFiles",
81
10
                                                              TUnit::UNIT, iceberg_profile);
82
10
        _iceberg_profile.num_delete_rows = ADD_CHILD_COUNTER(this->get_profile(), "NumDeleteRows",
83
10
                                                             TUnit::UNIT, iceberg_profile);
84
10
        _iceberg_profile.delete_files_read_time =
85
10
                ADD_CHILD_TIMER(this->get_profile(), "DeleteFileReadTime", iceberg_profile);
86
10
        _iceberg_profile.delete_rows_sort_time =
87
10
                ADD_CHILD_TIMER(this->get_profile(), "DeleteRowsSortTime", iceberg_profile);
88
10
        _iceberg_profile.parse_delete_file_time =
89
10
                ADD_CHILD_TIMER(this->get_profile(), "ParseDeleteFileTime", iceberg_profile);
90
10
        _iceberg_profile.decoded_cache_hit_count =
91
10
                ADD_CHILD_COUNTER(this->get_profile(), "DeletionVectorDecodedCacheHitCount",
92
10
                                  TUnit::UNIT, iceberg_profile);
93
10
        _iceberg_profile.decoded_cache_miss_count =
94
10
                ADD_CHILD_COUNTER(this->get_profile(), "DeletionVectorDecodedCacheMissCount",
95
10
                                  TUnit::UNIT, iceberg_profile);
96
10
        _iceberg_profile.file_cache_hit_count =
97
10
                ADD_CHILD_COUNTER(this->get_profile(), "DeletionVectorFileCacheHitCount",
98
10
                                  TUnit::UNIT, iceberg_profile);
99
10
        _iceberg_profile.file_cache_miss_count =
100
10
                ADD_CHILD_COUNTER(this->get_profile(), "DeletionVectorFileCacheMissCount",
101
10
                                  TUnit::UNIT, iceberg_profile);
102
10
        _iceberg_profile.file_cache_peer_read_count =
103
10
                ADD_CHILD_COUNTER(this->get_profile(), "DeletionVectorFileCachePeerReadCount",
104
10
                                  TUnit::UNIT, iceberg_profile);
105
10
    }
Unexecuted instantiation: _ZN5doris18IcebergReaderMixinINS_13ParquetReaderEEC2IJRPNS_14RuntimeProfileERKNS_20TFileScanRangeParamsERKNS_14TFileRangeDescERmRPKN4cctz9time_zoneESt10shared_ptrINS_2io9IOContextEERPNS_12RuntimeStateERPNS_13FileMetaCacheEEEEPNS_14ShardedKVCacheEDpOT_
Unexecuted instantiation: _ZN5doris18IcebergReaderMixinINS_9OrcReaderEEC2IJRPNS_14RuntimeProfileERPNS_12RuntimeStateERKNS_20TFileScanRangeParamsERKNS_14TFileRangeDescERmRKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESt10shared_ptrINS_2io9IOContextEERPNS_13FileMetaCacheEEEEPNS_14ShardedKVCacheEDpOT_
106
107
28
    ~IcebergReaderMixin() override = default;
_ZN5doris18IcebergReaderMixinINS_13ParquetReaderEED2Ev
Line
Count
Source
107
18
    ~IcebergReaderMixin() override = default;
_ZN5doris18IcebergReaderMixinINS_9OrcReaderEED2Ev
Line
Count
Source
107
10
    ~IcebergReaderMixin() override = default;
108
109
    void set_current_file_info(const std::string& file_path, int32_t partition_spec_id,
110
0
                               const std::string& partition_data_json) {
111
0
        _current_file_path = file_path;
112
0
        _partition_spec_id = partition_spec_id;
113
0
        _partition_data_json = partition_data_json;
114
0
    }
Unexecuted instantiation: _ZN5doris18IcebergReaderMixinINS_13ParquetReaderEE21set_current_file_infoERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEiSA_
Unexecuted instantiation: _ZN5doris18IcebergReaderMixinINS_9OrcReaderEE21set_current_file_infoERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEiSA_
115
116
    enum { DATA, POSITION_DELETE, EQUALITY_DELETE, DELETION_VECTOR };
117
    enum Fileformat { NONE, PARQUET, ORC, AVRO };
118
119
    virtual void set_delete_rows() = 0;
120
    virtual void set_deletion_vector() = 0;
121
122
    // Table-level COUNT(*) is handled by CountReader (created by FileScanner after
123
    // init_reader). If _do_get_next_block is called, COUNT must have been resolved.
124
9
    Status _do_get_next_block(Block* block, size_t* read_rows, bool* eof) override {
125
9
        DCHECK(this->_push_down_agg_type != TPushAggOp::type::COUNT);
126
9
        return BaseReader::_do_get_next_block(block, read_rows, eof);
127
9
    }
_ZN5doris18IcebergReaderMixinINS_13ParquetReaderEE18_do_get_next_blockEPNS_5BlockEPmPb
Line
Count
Source
124
5
    Status _do_get_next_block(Block* block, size_t* read_rows, bool* eof) override {
125
        DCHECK(this->_push_down_agg_type != TPushAggOp::type::COUNT);
126
5
        return BaseReader::_do_get_next_block(block, read_rows, eof);
127
5
    }
_ZN5doris18IcebergReaderMixinINS_9OrcReaderEE18_do_get_next_blockEPNS_5BlockEPmPb
Line
Count
Source
124
4
    Status _do_get_next_block(Block* block, size_t* read_rows, bool* eof) override {
125
        DCHECK(this->_push_down_agg_type != TPushAggOp::type::COUNT);
126
4
        return BaseReader::_do_get_next_block(block, read_rows, eof);
127
4
    }
128
129
    void set_create_row_id_column_iterator_func(
130
0
            std::function<std::shared_ptr<segment_v2::RowIdColumnIteratorV2>()> create_func) {
131
0
        _create_topn_row_id_column_iterator = create_func;
132
0
    }
Unexecuted instantiation: _ZN5doris18IcebergReaderMixinINS_13ParquetReaderEE38set_create_row_id_column_iterator_funcESt8functionIFSt10shared_ptrINS_10segment_v221RowIdColumnIteratorV2EEvEE
Unexecuted instantiation: _ZN5doris18IcebergReaderMixinINS_9OrcReaderEE38set_create_row_id_column_iterator_funcESt8functionIFSt10shared_ptrINS_10segment_v221RowIdColumnIteratorV2EEvEE
133
134
    Status TEST_read_deletion_vector(const std::string& data_file_path,
135
1
                                     const TIcebergDeleteFileDesc& delete_file_desc) {
136
1
        return read_deletion_vector(data_file_path, delete_file_desc);
137
1
    }
138
139
    Status TEST_position_delete_base(const std::string& data_file_path,
140
1
                                     const std::vector<TIcebergDeleteFileDesc>& delete_files) {
141
1
        return _position_delete_base(data_file_path, delete_files);
142
1
    }
143
144
    void TEST_set_column_name_to_block_index(
145
4
            std::unordered_map<std::string, uint32_t>* column_name_to_block_index) {
146
4
        this->col_name_to_block_idx_ref() = column_name_to_block_index;
147
4
    }
148
149
    Status TEST_register_missing_equality_delete_column(int32_t field_id, const std::string& name,
150
5
                                                        const DataTypePtr& delete_key_type) {
151
5
        return _register_missing_equality_delete_column(field_id, name, delete_key_type);
152
5
    }
153
154
2
    Status TEST_materialize_missing_equality_delete_columns(Block* block, size_t rows) {
155
2
        return _materialize_missing_equality_delete_columns(block, rows);
156
2
    }
157
158
protected:
159
    // ---- Hook implementations ----
160
161
    Status on_fill_missing_columns(Block* block, size_t rows,
162
11
                                   const std::vector<std::string>& cols) override {
163
11
        if (!supports_iceberg_scan_semantics_v1(&this->get_scan_params())) {
164
4
            return BaseReader::on_fill_missing_columns(block, rows, cols);
165
4
        }
166
7
        std::vector<std::string> generic_columns;
167
7
        for (const auto& name : cols) {
168
1
            const auto* field = _find_current_schema_field(name);
169
1
            if (field == nullptr || !field->__isset.initial_default_value) {
170
0
                generic_columns.push_back(name);
171
0
                continue;
172
0
            }
173
1
            if (!this->col_name_to_block_idx_ref()->contains(name)) {
174
0
                return Status::InternalError("Missing column: {} not found in block {}", name,
175
0
                                             block->dump_structure());
176
0
            }
177
1
            const auto position = this->col_name_to_block_idx_ref()->at(name);
178
1
            ColumnPtr value;
179
1
            RETURN_IF_ERROR(_build_owned_initial_default_column(
180
1
                    *field, block->get_by_position(position).type, rows, &value));
181
            // Iceberg metadata is authoritative for pre-add rows; a generic FE expression may be
182
            // the Base64 carrier rather than the logical BINARY/FIXED/UUID value.
183
1
            block->get_by_position(position).column = value->convert_to_full_column_if_const();
184
1
        }
185
7
        return BaseReader::on_fill_missing_columns(block, rows, generic_columns);
186
7
    }
_ZN5doris18IcebergReaderMixinINS_13ParquetReaderEE23on_fill_missing_columnsEPNS_5BlockEmRKSt6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaISB_EE
Line
Count
Source
162
7
                                   const std::vector<std::string>& cols) override {
163
7
        if (!supports_iceberg_scan_semantics_v1(&this->get_scan_params())) {
164
3
            return BaseReader::on_fill_missing_columns(block, rows, cols);
165
3
        }
166
4
        std::vector<std::string> generic_columns;
167
4
        for (const auto& name : cols) {
168
1
            const auto* field = _find_current_schema_field(name);
169
1
            if (field == nullptr || !field->__isset.initial_default_value) {
170
0
                generic_columns.push_back(name);
171
0
                continue;
172
0
            }
173
1
            if (!this->col_name_to_block_idx_ref()->contains(name)) {
174
0
                return Status::InternalError("Missing column: {} not found in block {}", name,
175
0
                                             block->dump_structure());
176
0
            }
177
1
            const auto position = this->col_name_to_block_idx_ref()->at(name);
178
1
            ColumnPtr value;
179
1
            RETURN_IF_ERROR(_build_owned_initial_default_column(
180
1
                    *field, block->get_by_position(position).type, rows, &value));
181
            // Iceberg metadata is authoritative for pre-add rows; a generic FE expression may be
182
            // the Base64 carrier rather than the logical BINARY/FIXED/UUID value.
183
1
            block->get_by_position(position).column = value->convert_to_full_column_if_const();
184
1
        }
185
4
        return BaseReader::on_fill_missing_columns(block, rows, generic_columns);
186
4
    }
_ZN5doris18IcebergReaderMixinINS_9OrcReaderEE23on_fill_missing_columnsEPNS_5BlockEmRKSt6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaISB_EE
Line
Count
Source
162
4
                                   const std::vector<std::string>& cols) override {
163
4
        if (!supports_iceberg_scan_semantics_v1(&this->get_scan_params())) {
164
1
            return BaseReader::on_fill_missing_columns(block, rows, cols);
165
1
        }
166
3
        std::vector<std::string> generic_columns;
167
3
        for (const auto& name : cols) {
168
0
            const auto* field = _find_current_schema_field(name);
169
0
            if (field == nullptr || !field->__isset.initial_default_value) {
170
0
                generic_columns.push_back(name);
171
0
                continue;
172
0
            }
173
0
            if (!this->col_name_to_block_idx_ref()->contains(name)) {
174
0
                return Status::InternalError("Missing column: {} not found in block {}", name,
175
0
                                             block->dump_structure());
176
0
            }
177
0
            const auto position = this->col_name_to_block_idx_ref()->at(name);
178
0
            ColumnPtr value;
179
0
            RETURN_IF_ERROR(_build_owned_initial_default_column(
180
0
                    *field, block->get_by_position(position).type, rows, &value));
181
            // Iceberg metadata is authoritative for pre-add rows; a generic FE expression may be
182
            // the Base64 carrier rather than the logical BINARY/FIXED/UUID value.
183
0
            block->get_by_position(position).column = value->convert_to_full_column_if_const();
184
0
        }
185
3
        return BaseReader::on_fill_missing_columns(block, rows, generic_columns);
186
3
    }
187
188
    // Called before reading a block: expand block for equality delete columns + detect row_id
189
9
    Status on_before_read_block(Block* block) override {
190
9
        RETURN_IF_ERROR(_expand_block_if_need(block));
191
9
        return Status::OK();
192
9
    }
_ZN5doris18IcebergReaderMixinINS_13ParquetReaderEE20on_before_read_blockEPNS_5BlockE
Line
Count
Source
189
5
    Status on_before_read_block(Block* block) override {
190
5
        RETURN_IF_ERROR(_expand_block_if_need(block));
191
5
        return Status::OK();
192
5
    }
_ZN5doris18IcebergReaderMixinINS_9OrcReaderEE20on_before_read_blockEPNS_5BlockE
Line
Count
Source
189
4
    Status on_before_read_block(Block* block) override {
190
4
        RETURN_IF_ERROR(_expand_block_if_need(block));
191
4
        return Status::OK();
192
4
    }
193
194
    /// Fill Iceberg $row_id synthesized column. Registered as handler during init.
195
0
    Status _fill_iceberg_row_id(Block* block, size_t rows) {
196
0
        int row_id_pos = block->get_position_by_name(BeConsts::ICEBERG_ROWID_COL);
197
0
        DORIS_CHECK(row_id_pos >= 0);
198
199
        // Lazy-init file info: only set when $row_id is actually needed.
200
0
        const auto& table_desc = this->get_scan_range().table_format_params.iceberg_params;
201
0
        std::string file_path = table_desc.original_file_path;
202
0
        int32_t partition_spec_id =
203
0
                table_desc.__isset.partition_spec_id ? table_desc.partition_spec_id : 0;
204
0
        std::string partition_data_json;
205
0
        if (table_desc.__isset.partition_data_json) {
206
0
            partition_data_json = table_desc.partition_data_json;
207
0
        }
208
0
        set_current_file_info(file_path, partition_spec_id, partition_data_json);
209
210
0
        const auto& row_ids = this->current_batch_row_positions();
211
0
        auto& col_with_type = block->get_by_position(static_cast<size_t>(row_id_pos));
212
0
        MutableColumnPtr row_id_column;
213
0
        RETURN_IF_ERROR(_build_iceberg_rowid_column(col_with_type.type, _current_file_path, row_ids,
214
0
                                                    _partition_spec_id, _partition_data_json,
215
0
                                                    &row_id_column));
216
0
        col_with_type.column = std::move(row_id_column);
217
0
        return Status::OK();
218
0
    }
Unexecuted instantiation: _ZN5doris18IcebergReaderMixinINS_13ParquetReaderEE20_fill_iceberg_row_idEPNS_5BlockEm
Unexecuted instantiation: _ZN5doris18IcebergReaderMixinINS_9OrcReaderEE20_fill_iceberg_row_idEPNS_5BlockEm
219
220
0
    void _init_row_lineage_columns() {
221
0
        const auto& table_desc = this->get_scan_range().table_format_params.iceberg_params;
222
0
        if (table_desc.__isset.first_row_id) {
223
0
            _row_lineage_columns.first_row_id = table_desc.first_row_id;
224
0
        }
225
0
        if (table_desc.__isset.last_updated_sequence_number) {
226
0
            _row_lineage_columns.last_updated_sequence_number =
227
0
                    table_desc.last_updated_sequence_number;
228
0
        }
229
0
    }
Unexecuted instantiation: _ZN5doris18IcebergReaderMixinINS_13ParquetReaderEE25_init_row_lineage_columnsEv
Unexecuted instantiation: _ZN5doris18IcebergReaderMixinINS_9OrcReaderEE25_init_row_lineage_columnsEv
230
231
0
    Status _fill_row_lineage_row_id(Block* block, size_t rows) {
232
0
        int col_pos = block->get_position_by_name(ROW_LINEAGE_ROW_ID);
233
0
        DORIS_CHECK(col_pos >= 0);
234
235
0
        if (_row_lineage_columns.first_row_id >= 0) {
236
0
            auto column_guard = block->mutate_column_scoped(col_pos);
237
0
            auto* nullable_column =
238
0
                    assert_cast<ColumnNullable*>(column_guard.mutable_column().get());
239
0
            auto& null_map = nullable_column->get_null_map_data();
240
0
            auto& data =
241
0
                    assert_cast<ColumnInt64&>(*nullable_column->get_nested_column_ptr()).get_data();
242
0
            const auto& row_ids = this->current_batch_row_positions();
243
0
            for (size_t i = 0; i < rows; ++i) {
244
0
                if (null_map[i] != 0) {
245
0
                    null_map[i] = 0;
246
0
                    data[i] = _row_lineage_columns.first_row_id + static_cast<int64_t>(row_ids[i]);
247
0
                }
248
0
            }
249
0
        }
250
0
        return Status::OK();
251
0
    }
Unexecuted instantiation: _ZN5doris18IcebergReaderMixinINS_13ParquetReaderEE24_fill_row_lineage_row_idEPNS_5BlockEm
Unexecuted instantiation: _ZN5doris18IcebergReaderMixinINS_9OrcReaderEE24_fill_row_lineage_row_idEPNS_5BlockEm
252
253
0
    Status _fill_row_lineage_last_updated_sequence_number(Block* block, size_t rows) {
254
0
        int col_pos = block->get_position_by_name(ROW_LINEAGE_LAST_UPDATED_SEQ_NUMBER);
255
0
        DORIS_CHECK(col_pos >= 0);
256
257
0
        if (_row_lineage_columns.last_updated_sequence_number >= 0) {
258
0
            auto column_guard = block->mutate_column_scoped(col_pos);
259
0
            auto* nullable_column =
260
0
                    assert_cast<ColumnNullable*>(column_guard.mutable_column().get());
261
0
            auto& null_map = nullable_column->get_null_map_data();
262
0
            auto& data =
263
0
                    assert_cast<ColumnInt64&>(*nullable_column->get_nested_column_ptr()).get_data();
264
0
            for (size_t i = 0; i < rows; ++i) {
265
0
                if (null_map[i] != 0) {
266
0
                    null_map[i] = 0;
267
0
                    data[i] = _row_lineage_columns.last_updated_sequence_number;
268
0
                }
269
0
            }
270
0
        }
271
0
        return Status::OK();
272
0
    }
Unexecuted instantiation: _ZN5doris18IcebergReaderMixinINS_13ParquetReaderEE46_fill_row_lineage_last_updated_sequence_numberEPNS_5BlockEm
Unexecuted instantiation: _ZN5doris18IcebergReaderMixinINS_9OrcReaderEE46_fill_row_lineage_last_updated_sequence_numberEPNS_5BlockEm
273
274
    // Called after reading a block: apply equality delete filter + shrink block
275
9
    Status on_after_read_block(Block* block, size_t* read_rows) override {
276
9
        if (!_equality_delete_impls.empty()) {
277
4
            std::unique_ptr<IColumn::Filter> filter =
278
4
                    std::make_unique<IColumn::Filter>(block->rows(), 1);
279
4
            for (auto& equality_delete_impl : _equality_delete_impls) {
280
4
                RETURN_IF_ERROR(equality_delete_impl->filter_data_block(
281
4
                        block, this->col_name_to_block_idx_ref(), _id_to_block_column_name,
282
4
                        *filter));
283
4
            }
284
4
            Block::filter_block_internal(block, *filter, block->columns());
285
4
            *read_rows = block->rows();
286
4
        }
287
9
        return _shrink_block_if_need(block);
288
9
    }
_ZN5doris18IcebergReaderMixinINS_13ParquetReaderEE19on_after_read_blockEPNS_5BlockEPm
Line
Count
Source
275
5
    Status on_after_read_block(Block* block, size_t* read_rows) override {
276
5
        if (!_equality_delete_impls.empty()) {
277
2
            std::unique_ptr<IColumn::Filter> filter =
278
2
                    std::make_unique<IColumn::Filter>(block->rows(), 1);
279
2
            for (auto& equality_delete_impl : _equality_delete_impls) {
280
2
                RETURN_IF_ERROR(equality_delete_impl->filter_data_block(
281
2
                        block, this->col_name_to_block_idx_ref(), _id_to_block_column_name,
282
2
                        *filter));
283
2
            }
284
2
            Block::filter_block_internal(block, *filter, block->columns());
285
2
            *read_rows = block->rows();
286
2
        }
287
5
        return _shrink_block_if_need(block);
288
5
    }
_ZN5doris18IcebergReaderMixinINS_9OrcReaderEE19on_after_read_blockEPNS_5BlockEPm
Line
Count
Source
275
4
    Status on_after_read_block(Block* block, size_t* read_rows) override {
276
4
        if (!_equality_delete_impls.empty()) {
277
2
            std::unique_ptr<IColumn::Filter> filter =
278
2
                    std::make_unique<IColumn::Filter>(block->rows(), 1);
279
2
            for (auto& equality_delete_impl : _equality_delete_impls) {
280
2
                RETURN_IF_ERROR(equality_delete_impl->filter_data_block(
281
2
                        block, this->col_name_to_block_idx_ref(), _id_to_block_column_name,
282
2
                        *filter));
283
2
            }
284
2
            Block::filter_block_internal(block, *filter, block->columns());
285
2
            *read_rows = block->rows();
286
2
        }
287
4
        return _shrink_block_if_need(block);
288
4
    }
289
290
    // ---- Shared Iceberg methods ----
291
292
    Status _init_row_filters();
293
    Status _position_delete_base(const std::string data_file_path,
294
                                 const std::vector<TIcebergDeleteFileDesc>& delete_files);
295
    Status _equality_delete_base(const std::vector<TIcebergDeleteFileDesc>& delete_files);
296
    Status read_deletion_vector(const std::string& data_file_path,
297
                                const TIcebergDeleteFileDesc& delete_file_desc);
298
299
    Status _expand_block_if_need(Block* block);
300
    Status _shrink_block_if_need(Block* block);
301
    const schema::external::TStructField* _current_schema_root() const;
302
    const schema::external::TField* _find_current_schema_field(const std::string& name) const;
303
    const schema::external::TField* _find_current_schema_field(int32_t field_id) const;
304
    Status _build_owned_initial_default_column(const schema::external::TField& field,
305
                                               const DataTypePtr& type, size_t rows,
306
                                               ColumnPtr* column) const;
307
    Status _register_missing_equality_delete_column(int32_t field_id, const std::string& name,
308
                                                    const DataTypePtr& delete_key_type);
309
    Status _materialize_missing_equality_delete_column(Block* block, const std::string& name,
310
                                                       const ColumnPtr& value, size_t rows);
311
    Status _materialize_missing_equality_delete_columns(Block* block, size_t rows);
312
313
    // Type aliases — must be defined before member function declarations that use them.
314
    using DeleteRows = std::vector<int64_t>;
315
    using DeleteFile = phmap::parallel_flat_hash_map<
316
            std::string, std::unique_ptr<DeleteRows>, std::hash<std::string>, std::equal_to<>,
317
            std::allocator<std::pair<const std::string, std::unique_ptr<DeleteRows>>>, 8,
318
            std::mutex>;
319
320
    PositionDeleteRange _get_range(const ColumnDictI32& file_path_column);
321
    PositionDeleteRange _get_range(const ColumnString& file_path_column);
322
    static void _sort_delete_rows(const std::vector<std::vector<int64_t>*>& delete_rows_array,
323
                                  int64_t num_delete_rows, std::vector<int64_t>& result);
324
    Status _gen_position_delete_file_range(Block& block, DeleteFile* position_delete,
325
                                           size_t read_rows,
326
                                           bool file_path_column_dictionary_coded);
327
    void _generate_equality_delete_block(Block* block,
328
                                         const std::vector<std::string>& equality_delete_col_names,
329
                                         const std::vector<DataTypePtr>& equality_delete_col_types);
330
331
    // Pure virtual: format-specific delete file reading
332
    virtual Status _read_position_delete_file(const TFileRangeDesc*, DeleteFile*) = 0;
333
    virtual std::unique_ptr<GenericReader> _create_equality_reader(
334
            const TFileRangeDesc& delete_desc) = 0;
335
336
1
    static std::string _delet_file_cache_key(const std::string& path) { return "delete_" + path; }
_ZN5doris18IcebergReaderMixinINS_13ParquetReaderEE21_delet_file_cache_keyERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
Line
Count
Source
336
1
    static std::string _delet_file_cache_key(const std::string& path) { return "delete_" + path; }
Unexecuted instantiation: _ZN5doris18IcebergReaderMixinINS_9OrcReaderEE21_delet_file_cache_keyERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
337
338
    /// Build the Iceberg V2 row-id struct column.
339
    static Status _build_iceberg_rowid_column(const DataTypePtr& type, const std::string& file_path,
340
                                              const std::vector<rowid_t>& row_ids,
341
                                              int32_t partition_spec_id,
342
                                              const std::string& partition_data_json,
343
0
                                              MutableColumnPtr* column_out) {
344
0
        if (type == nullptr || column_out == nullptr) {
345
0
            return Status::InvalidArgument("Invalid iceberg rowid column type or output column");
346
0
        }
347
0
        MutableColumnPtr column = type->create_column();
348
0
        ColumnNullable* nullable_col = check_and_get_column<ColumnNullable>(column.get());
349
0
        ColumnStruct* struct_col = nullptr;
350
0
        if (nullable_col != nullptr) {
351
0
            struct_col =
352
0
                    check_and_get_column<ColumnStruct>(nullable_col->get_nested_column_ptr().get());
353
0
        } else {
354
0
            struct_col = check_and_get_column<ColumnStruct>(column.get());
355
0
        }
356
0
        if (struct_col == nullptr || struct_col->tuple_size() < 4) {
357
0
            return Status::InternalError("Invalid iceberg rowid column structure");
358
0
        }
359
0
        size_t num_rows = row_ids.size();
360
0
        auto& file_path_col = struct_col->get_column(0);
361
0
        auto& row_pos_col = struct_col->get_column(1);
362
0
        auto& spec_id_col = struct_col->get_column(2);
363
0
        auto& partition_data_col = struct_col->get_column(3);
364
0
        file_path_col.reserve(num_rows);
365
0
        row_pos_col.reserve(num_rows);
366
0
        spec_id_col.reserve(num_rows);
367
0
        partition_data_col.reserve(num_rows);
368
0
        for (size_t i = 0; i < num_rows; ++i) {
369
0
            file_path_col.insert_data(file_path.data(), file_path.size());
370
0
        }
371
0
        for (size_t i = 0; i < num_rows; ++i) {
372
0
            int64_t row_pos = static_cast<int64_t>(row_ids[i]);
373
0
            row_pos_col.insert_data(reinterpret_cast<const char*>(&row_pos), sizeof(row_pos));
374
0
        }
375
0
        for (size_t i = 0; i < num_rows; ++i) {
376
0
            int32_t spec_id = partition_spec_id;
377
0
            spec_id_col.insert_data(reinterpret_cast<const char*>(&spec_id), sizeof(spec_id));
378
0
        }
379
0
        for (size_t i = 0; i < num_rows; ++i) {
380
0
            partition_data_col.insert_data(partition_data_json.data(), partition_data_json.size());
381
0
        }
382
0
        if (nullable_col != nullptr) {
383
0
            nullable_col->get_null_map_data().resize_fill(num_rows, 0);
384
0
        }
385
0
        *column_out = std::move(column);
386
0
        return Status::OK();
387
0
    }
Unexecuted instantiation: _ZN5doris18IcebergReaderMixinINS_13ParquetReaderEE27_build_iceberg_rowid_columnERKSt10shared_ptrIKNS_9IDataTypeEERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt6vectorIjSaIjEEiSG_PNS_3COWINS_7IColumnEE11mutable_ptrISN_EE
Unexecuted instantiation: _ZN5doris18IcebergReaderMixinINS_9OrcReaderEE27_build_iceberg_rowid_columnERKSt10shared_ptrIKNS_9IDataTypeEERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt6vectorIjSaIjEEiSG_PNS_3COWINS_7IColumnEE11mutable_ptrISN_EE
388
389
    struct IcebergProfile {
390
        RuntimeProfile::Counter* num_delete_files;
391
        RuntimeProfile::Counter* num_delete_rows;
392
        RuntimeProfile::Counter* delete_files_read_time;
393
        RuntimeProfile::Counter* delete_rows_sort_time;
394
        RuntimeProfile::Counter* parse_delete_file_time;
395
        RuntimeProfile::Counter* decoded_cache_hit_count;
396
        RuntimeProfile::Counter* decoded_cache_miss_count;
397
        RuntimeProfile::Counter* file_cache_hit_count;
398
        RuntimeProfile::Counter* file_cache_miss_count;
399
        RuntimeProfile::Counter* file_cache_peer_read_count;
400
    };
401
402
    bool _need_row_id_column = false;
403
    std::string _current_file_path;
404
    int32_t _partition_spec_id = 0;
405
    std::string _partition_data_json;
406
407
    ShardedKVCache* _kv_cache;
408
    IcebergProfile _iceberg_profile;
409
    const std::vector<int64_t>* _iceberg_delete_rows = nullptr;
410
    const DeletionVector* _iceberg_deletion_vector = nullptr;
411
    std::vector<std::string> _expand_col_names;
412
    std::vector<ColumnWithTypeAndName> _expand_columns;
413
    std::unordered_map<std::string, ColumnPtr> _missing_equality_delete_values;
414
    std::vector<std::string> _all_required_col_names;
415
    Fileformat _file_format = Fileformat::NONE;
416
417
    const int64_t MIN_SUPPORT_DELETE_FILES_VERSION = 2;
418
    const std::string ICEBERG_FILE_PATH = "file_path";
419
    const std::string ICEBERG_ROW_POS = "pos";
420
    const std::vector<std::string> delete_file_col_names {ICEBERG_FILE_PATH, ICEBERG_ROW_POS};
421
    const std::unordered_map<std::string, uint32_t> DELETE_COL_NAME_TO_BLOCK_IDX = {
422
            {ICEBERG_FILE_PATH, 0}, {ICEBERG_ROW_POS, 1}};
423
    const int ICEBERG_FILE_PATH_INDEX = 0;
424
    const int ICEBERG_FILE_POS_INDEX = 1;
425
    const int READ_DELETE_FILE_BATCH_SIZE = 102400;
426
427
    // all ids that need read for eq delete (from all eq delete files)
428
    std::set<int> _equality_delete_col_ids;
429
    // eq delete column ids -> location of _equality_delete_blocks / _equality_delete_impls
430
    std::map<std::vector<int>, int> _equality_delete_block_map;
431
    // EqualityDeleteBase stores raw pointers to these blocks, so do not modify this vector after
432
    // creating entries in _equality_delete_impls.
433
    std::vector<Block> _equality_delete_blocks;
434
    std::vector<std::unique_ptr<EqualityDeleteBase>> _equality_delete_impls;
435
436
    // id -> block column name
437
    std::unordered_map<int, std::string> _id_to_block_column_name;
438
439
    std::function<std::shared_ptr<segment_v2::RowIdColumnIteratorV2>()>
440
            _create_topn_row_id_column_iterator;
441
442
    static constexpr const char* ROW_LINEAGE_ROW_ID = "_row_id";
443
    static constexpr const char* ROW_LINEAGE_LAST_UPDATED_SEQ_NUMBER =
444
            "_last_updated_sequence_number";
445
    struct RowLineageColumns {
446
        int64_t first_row_id = -1;
447
        int64_t last_updated_sequence_number = -1;
448
    };
449
    RowLineageColumns _row_lineage_columns;
450
};
451
452
// ============================================================================
453
// Template method implementations (must be in header for templates)
454
// ============================================================================
455
456
template <typename BaseReader>
457
9
Status IcebergReaderMixin<BaseReader>::_init_row_filters() {
458
    // COUNT(*) short-circuit. A table-level row count of 0 (e.g. an all-deleted table read with
459
    // ignore_iceberg_dangling_delete, where total-records == total-position-deletes) is still a
460
    // valid pushed-down count, so accept >= 0 -- matching FileScanner and the Paimon readers. FE
461
    // sends -1 when there is no table-level count; using > 0 here would drop a genuine 0 into the
462
    // delete-applying path below and never produce the intended CountReader(0).
463
9
    if (this->_push_down_agg_type == TPushAggOp::type::COUNT &&
464
9
        this->get_scan_range().table_format_params.__isset.table_level_row_count &&
465
9
        this->get_scan_range().table_format_params.table_level_row_count >= 0) {
466
0
        return Status::OK();
467
0
    }
468
469
9
    const auto& table_desc = this->get_scan_range().table_format_params.iceberg_params;
470
9
    const auto& version = table_desc.format_version;
471
9
    if (version < MIN_SUPPORT_DELETE_FILES_VERSION) {
472
5
        return Status::OK();
473
5
    }
474
475
4
    std::vector<TIcebergDeleteFileDesc> position_delete_files;
476
4
    std::vector<TIcebergDeleteFileDesc> equality_delete_files;
477
4
    std::vector<TIcebergDeleteFileDesc> deletion_vector_files;
478
4
    for (const TIcebergDeleteFileDesc& desc : table_desc.delete_files) {
479
4
        if (desc.content == POSITION_DELETE) {
480
0
            position_delete_files.emplace_back(desc);
481
4
        } else if (desc.content == EQUALITY_DELETE) {
482
4
            equality_delete_files.emplace_back(desc);
483
4
        } else if (desc.content == DELETION_VECTOR) {
484
0
            deletion_vector_files.emplace_back(desc);
485
0
        }
486
4
    }
487
488
4
    if (!equality_delete_files.empty()) {
489
4
        RETURN_IF_ERROR(_equality_delete_base(equality_delete_files));
490
4
        this->set_push_down_agg_type(TPushAggOp::NONE);
491
4
    }
492
493
4
    if (!deletion_vector_files.empty()) {
494
0
        if (deletion_vector_files.size() != 1) [[unlikely]] {
495
            /*
496
             * Deletion vectors are a binary representation of deletes for a single data file that is more efficient
497
             * at execution time than position delete files. Unlike equality or position delete files, there can be
498
             * at most one deletion vector for a given data file in a snapshot.
499
             */
500
0
            return Status::DataQualityError("This iceberg data file has multiple DVs.");
501
0
        }
502
0
        RETURN_IF_ERROR(
503
0
                read_deletion_vector(table_desc.original_file_path, deletion_vector_files[0]));
504
0
        this->set_push_down_agg_type(TPushAggOp::NONE);
505
4
    } else if (!position_delete_files.empty()) {
506
0
        RETURN_IF_ERROR(
507
0
                _position_delete_base(table_desc.original_file_path, position_delete_files));
508
0
        this->set_push_down_agg_type(TPushAggOp::NONE);
509
0
    }
510
511
4
    COUNTER_UPDATE(_iceberg_profile.num_delete_files, table_desc.delete_files.size());
512
4
    return Status::OK();
513
4
}
_ZN5doris18IcebergReaderMixinINS_13ParquetReaderEE17_init_row_filtersEv
Line
Count
Source
457
5
Status IcebergReaderMixin<BaseReader>::_init_row_filters() {
458
    // COUNT(*) short-circuit. A table-level row count of 0 (e.g. an all-deleted table read with
459
    // ignore_iceberg_dangling_delete, where total-records == total-position-deletes) is still a
460
    // valid pushed-down count, so accept >= 0 -- matching FileScanner and the Paimon readers. FE
461
    // sends -1 when there is no table-level count; using > 0 here would drop a genuine 0 into the
462
    // delete-applying path below and never produce the intended CountReader(0).
463
5
    if (this->_push_down_agg_type == TPushAggOp::type::COUNT &&
464
5
        this->get_scan_range().table_format_params.__isset.table_level_row_count &&
465
5
        this->get_scan_range().table_format_params.table_level_row_count >= 0) {
466
0
        return Status::OK();
467
0
    }
468
469
5
    const auto& table_desc = this->get_scan_range().table_format_params.iceberg_params;
470
5
    const auto& version = table_desc.format_version;
471
5
    if (version < MIN_SUPPORT_DELETE_FILES_VERSION) {
472
3
        return Status::OK();
473
3
    }
474
475
2
    std::vector<TIcebergDeleteFileDesc> position_delete_files;
476
2
    std::vector<TIcebergDeleteFileDesc> equality_delete_files;
477
2
    std::vector<TIcebergDeleteFileDesc> deletion_vector_files;
478
2
    for (const TIcebergDeleteFileDesc& desc : table_desc.delete_files) {
479
2
        if (desc.content == POSITION_DELETE) {
480
0
            position_delete_files.emplace_back(desc);
481
2
        } else if (desc.content == EQUALITY_DELETE) {
482
2
            equality_delete_files.emplace_back(desc);
483
2
        } else if (desc.content == DELETION_VECTOR) {
484
0
            deletion_vector_files.emplace_back(desc);
485
0
        }
486
2
    }
487
488
2
    if (!equality_delete_files.empty()) {
489
2
        RETURN_IF_ERROR(_equality_delete_base(equality_delete_files));
490
2
        this->set_push_down_agg_type(TPushAggOp::NONE);
491
2
    }
492
493
2
    if (!deletion_vector_files.empty()) {
494
0
        if (deletion_vector_files.size() != 1) [[unlikely]] {
495
            /*
496
             * Deletion vectors are a binary representation of deletes for a single data file that is more efficient
497
             * at execution time than position delete files. Unlike equality or position delete files, there can be
498
             * at most one deletion vector for a given data file in a snapshot.
499
             */
500
0
            return Status::DataQualityError("This iceberg data file has multiple DVs.");
501
0
        }
502
0
        RETURN_IF_ERROR(
503
0
                read_deletion_vector(table_desc.original_file_path, deletion_vector_files[0]));
504
0
        this->set_push_down_agg_type(TPushAggOp::NONE);
505
2
    } else if (!position_delete_files.empty()) {
506
0
        RETURN_IF_ERROR(
507
0
                _position_delete_base(table_desc.original_file_path, position_delete_files));
508
0
        this->set_push_down_agg_type(TPushAggOp::NONE);
509
0
    }
510
511
2
    COUNTER_UPDATE(_iceberg_profile.num_delete_files, table_desc.delete_files.size());
512
2
    return Status::OK();
513
2
}
_ZN5doris18IcebergReaderMixinINS_9OrcReaderEE17_init_row_filtersEv
Line
Count
Source
457
4
Status IcebergReaderMixin<BaseReader>::_init_row_filters() {
458
    // COUNT(*) short-circuit. A table-level row count of 0 (e.g. an all-deleted table read with
459
    // ignore_iceberg_dangling_delete, where total-records == total-position-deletes) is still a
460
    // valid pushed-down count, so accept >= 0 -- matching FileScanner and the Paimon readers. FE
461
    // sends -1 when there is no table-level count; using > 0 here would drop a genuine 0 into the
462
    // delete-applying path below and never produce the intended CountReader(0).
463
4
    if (this->_push_down_agg_type == TPushAggOp::type::COUNT &&
464
4
        this->get_scan_range().table_format_params.__isset.table_level_row_count &&
465
4
        this->get_scan_range().table_format_params.table_level_row_count >= 0) {
466
0
        return Status::OK();
467
0
    }
468
469
4
    const auto& table_desc = this->get_scan_range().table_format_params.iceberg_params;
470
4
    const auto& version = table_desc.format_version;
471
4
    if (version < MIN_SUPPORT_DELETE_FILES_VERSION) {
472
2
        return Status::OK();
473
2
    }
474
475
2
    std::vector<TIcebergDeleteFileDesc> position_delete_files;
476
2
    std::vector<TIcebergDeleteFileDesc> equality_delete_files;
477
2
    std::vector<TIcebergDeleteFileDesc> deletion_vector_files;
478
2
    for (const TIcebergDeleteFileDesc& desc : table_desc.delete_files) {
479
2
        if (desc.content == POSITION_DELETE) {
480
0
            position_delete_files.emplace_back(desc);
481
2
        } else if (desc.content == EQUALITY_DELETE) {
482
2
            equality_delete_files.emplace_back(desc);
483
2
        } else if (desc.content == DELETION_VECTOR) {
484
0
            deletion_vector_files.emplace_back(desc);
485
0
        }
486
2
    }
487
488
2
    if (!equality_delete_files.empty()) {
489
2
        RETURN_IF_ERROR(_equality_delete_base(equality_delete_files));
490
2
        this->set_push_down_agg_type(TPushAggOp::NONE);
491
2
    }
492
493
2
    if (!deletion_vector_files.empty()) {
494
0
        if (deletion_vector_files.size() != 1) [[unlikely]] {
495
            /*
496
             * Deletion vectors are a binary representation of deletes for a single data file that is more efficient
497
             * at execution time than position delete files. Unlike equality or position delete files, there can be
498
             * at most one deletion vector for a given data file in a snapshot.
499
             */
500
0
            return Status::DataQualityError("This iceberg data file has multiple DVs.");
501
0
        }
502
0
        RETURN_IF_ERROR(
503
0
                read_deletion_vector(table_desc.original_file_path, deletion_vector_files[0]));
504
0
        this->set_push_down_agg_type(TPushAggOp::NONE);
505
2
    } else if (!position_delete_files.empty()) {
506
0
        RETURN_IF_ERROR(
507
0
                _position_delete_base(table_desc.original_file_path, position_delete_files));
508
0
        this->set_push_down_agg_type(TPushAggOp::NONE);
509
0
    }
510
511
2
    COUNTER_UPDATE(_iceberg_profile.num_delete_files, table_desc.delete_files.size());
512
2
    return Status::OK();
513
2
}
514
515
template <typename BaseReader>
516
Status IcebergReaderMixin<BaseReader>::_equality_delete_base(
517
4
        const std::vector<TIcebergDeleteFileDesc>& delete_files) {
518
4
    std::unordered_map<std::string, std::tuple<std::string, const SlotDescriptor*>>
519
4
            partition_columns;
520
4
    std::unordered_map<std::string, VExprContextSPtr> missing_columns;
521
522
4
    for (const auto& delete_file : delete_files) {
523
4
        TFileRangeDesc delete_desc;
524
4
        delete_desc.__set_fs_name(this->get_scan_range().fs_name);
525
4
        delete_desc.path = delete_file.path;
526
4
        delete_desc.start_offset = 0;
527
4
        delete_desc.size = -1;
528
4
        delete_desc.file_size = -1;
529
530
4
        if (!delete_file.__isset.field_ids) [[unlikely]] {
531
0
            return Status::InternalError(
532
0
                    "missing delete field ids when reading equality delete file");
533
0
        }
534
4
        auto& read_column_field_ids = delete_file.field_ids;
535
4
        std::set<int> read_column_field_ids_set;
536
4
        for (const auto& field_id : read_column_field_ids) {
537
4
            read_column_field_ids_set.insert(field_id);
538
4
            _equality_delete_col_ids.insert(field_id);
539
4
        }
540
541
4
        std::unique_ptr<GenericReader> delete_reader = _create_equality_reader(delete_desc);
542
4
        RETURN_IF_ERROR(delete_reader->init_schema_reader());
543
544
4
        std::vector<std::string> equality_delete_col_names;
545
4
        std::vector<DataTypePtr> equality_delete_col_types;
546
547
        // Build delete col names/types/ids by matching field_ids from delete file schema.
548
        // Master iterates delete file's FieldDescriptor and uses field_id to match,
549
        // NOT idx-based pairing (get_parsed_schema order != field_ids order).
550
4
        std::vector<std::string> delete_col_names;
551
4
        std::vector<DataTypePtr> delete_col_types;
552
4
        std::vector<int> delete_col_ids;
553
4
        std::unordered_map<std::string, uint32_t> delete_col_name_to_block_idx;
554
555
4
        if (auto* parquet_reader = typeid_cast<ParquetReader*>(delete_reader.get())) {
556
2
            const FieldDescriptor* delete_field_desc = nullptr;
557
2
            RETURN_IF_ERROR(parquet_reader->get_file_metadata_schema(&delete_field_desc));
558
2
            DCHECK(delete_field_desc != nullptr);
559
560
2
            for (const auto& delete_file_field : delete_field_desc->get_fields_schema()) {
561
2
                if (delete_file_field.field_id == -1) [[unlikely]] {
562
0
                    return Status::DataQualityError(
563
0
                            "missing field id when reading equality delete file");
564
0
                }
565
2
                if (!read_column_field_ids_set.contains(delete_file_field.field_id)) {
566
0
                    continue;
567
0
                }
568
2
                if (delete_file_field.children.size() > 0) [[unlikely]] {
569
0
                    return Status::InternalError(
570
0
                            "can not support read complex column in equality delete file");
571
0
                }
572
573
2
                delete_col_ids.emplace_back(delete_file_field.field_id);
574
2
                delete_col_names.emplace_back(delete_file_field.name);
575
2
                delete_col_types.emplace_back(make_nullable(delete_file_field.data_type));
576
577
2
                int field_id = delete_file_field.field_id;
578
2
                if (!_id_to_block_column_name.contains(field_id)) {
579
2
                    _id_to_block_column_name.emplace(field_id, delete_file_field.name);
580
2
                    _expand_col_names.emplace_back(delete_file_field.name);
581
2
                    _expand_columns.emplace_back(
582
2
                            make_nullable(delete_file_field.data_type)->create_column(),
583
2
                            make_nullable(delete_file_field.data_type), delete_file_field.name);
584
2
                }
585
2
            }
586
4
            for (uint32_t idx = 0; idx < delete_col_names.size(); ++idx) {
587
2
                delete_col_name_to_block_idx[delete_col_names[idx]] = idx;
588
2
            }
589
            // Delete files have TFileRangeDesc.size=-1, which would cause
590
            // set_fill_columns to return EndOfFile("No row group to read")
591
            // when _filter_groups is true. Master passes filter_groups=false.
592
2
            ParquetInitContext eq_delete_ctx;
593
2
            eq_delete_ctx.filter_groups = false;
594
2
            eq_delete_ctx.column_names = delete_col_names;
595
2
            eq_delete_ctx.col_name_to_block_idx = &delete_col_name_to_block_idx;
596
2
            auto st2 = parquet_reader->init_reader(&eq_delete_ctx);
597
2
            if (!st2.ok()) {
598
0
                return st2;
599
0
            }
600
2
        } else if (auto* orc_reader = typeid_cast<OrcReader*>(delete_reader.get())) {
601
            // For ORC: use get_parsed_schema with field_ids from delete_file
602
            // ORC field_ids come from the Thrift descriptor, not from ORC metadata
603
2
            RETURN_IF_ERROR(delete_reader->get_parsed_schema(&equality_delete_col_names,
604
2
                                                             &equality_delete_col_types));
605
4
            for (uint32_t idx = 0; idx < equality_delete_col_names.size(); ++idx) {
606
2
                if (idx < read_column_field_ids.size()) {
607
2
                    int field_id = read_column_field_ids[idx];
608
2
                    if (!read_column_field_ids_set.contains(field_id)) continue;
609
2
                    delete_col_ids.emplace_back(field_id);
610
2
                    delete_col_names.emplace_back(equality_delete_col_names[idx]);
611
2
                    delete_col_types.emplace_back(make_nullable(equality_delete_col_types[idx]));
612
2
                    if (!_id_to_block_column_name.contains(field_id)) {
613
2
                        _id_to_block_column_name.emplace(field_id, equality_delete_col_names[idx]);
614
2
                        _expand_col_names.emplace_back(equality_delete_col_names[idx]);
615
2
                        _expand_columns.emplace_back(
616
2
                                make_nullable(equality_delete_col_types[idx])->create_column(),
617
2
                                make_nullable(equality_delete_col_types[idx]),
618
2
                                equality_delete_col_names[idx]);
619
2
                    }
620
2
                }
621
2
            }
622
4
            for (uint32_t idx = 0; idx < delete_col_names.size(); ++idx) {
623
2
                delete_col_name_to_block_idx[delete_col_names[idx]] = idx;
624
2
            }
625
2
            OrcInitContext eq_delete_ctx;
626
2
            eq_delete_ctx.column_names = delete_col_names;
627
2
            eq_delete_ctx.col_name_to_block_idx = &delete_col_name_to_block_idx;
628
2
            RETURN_IF_ERROR(orc_reader->init_reader(&eq_delete_ctx));
629
2
        } else {
630
0
            return Status::InternalError("Unsupported format of delete file");
631
0
        }
632
633
4
        if (!_equality_delete_block_map.contains(delete_col_ids)) {
634
4
            _equality_delete_block_map.emplace(delete_col_ids, _equality_delete_blocks.size());
635
4
            Block block;
636
4
            _generate_equality_delete_block(&block, delete_col_names, delete_col_types);
637
4
            _equality_delete_blocks.emplace_back(block);
638
4
        }
639
4
        Block& eq_file_block = _equality_delete_blocks[_equality_delete_block_map[delete_col_ids]];
640
641
4
        bool eof = false;
642
10
        while (!eof) {
643
6
            Block tmp_block;
644
6
            _generate_equality_delete_block(&tmp_block, delete_col_names, delete_col_types);
645
6
            size_t read_rows = 0;
646
6
            auto st = delete_reader->get_next_block(&tmp_block, &read_rows, &eof);
647
6
            if (!st.ok()) {
648
0
                return st;
649
0
            }
650
6
            if (read_rows > 0) {
651
4
                ScopedMutableBlock scoped_mutable_block(&eq_file_block);
652
4
                auto& mutable_block = scoped_mutable_block.mutable_block();
653
4
                RETURN_IF_ERROR(mutable_block.merge(tmp_block));
654
4
            }
655
6
        }
656
4
    }
657
658
4
    for (const auto& [delete_col_ids, block_idx] : _equality_delete_block_map) {
659
4
        auto& eq_file_block = _equality_delete_blocks[block_idx];
660
4
        auto equality_delete_impl =
661
4
                EqualityDeleteBase::get_delete_impl(&eq_file_block, delete_col_ids);
662
4
        RETURN_IF_ERROR(equality_delete_impl->init(this->get_profile()));
663
4
        _equality_delete_impls.emplace_back(std::move(equality_delete_impl));
664
4
    }
665
4
    return Status::OK();
666
4
}
_ZN5doris18IcebergReaderMixinINS_13ParquetReaderEE21_equality_delete_baseERKSt6vectorINS_22TIcebergDeleteFileDescESaIS4_EE
Line
Count
Source
517
2
        const std::vector<TIcebergDeleteFileDesc>& delete_files) {
518
2
    std::unordered_map<std::string, std::tuple<std::string, const SlotDescriptor*>>
519
2
            partition_columns;
520
2
    std::unordered_map<std::string, VExprContextSPtr> missing_columns;
521
522
2
    for (const auto& delete_file : delete_files) {
523
2
        TFileRangeDesc delete_desc;
524
2
        delete_desc.__set_fs_name(this->get_scan_range().fs_name);
525
2
        delete_desc.path = delete_file.path;
526
2
        delete_desc.start_offset = 0;
527
2
        delete_desc.size = -1;
528
2
        delete_desc.file_size = -1;
529
530
2
        if (!delete_file.__isset.field_ids) [[unlikely]] {
531
0
            return Status::InternalError(
532
0
                    "missing delete field ids when reading equality delete file");
533
0
        }
534
2
        auto& read_column_field_ids = delete_file.field_ids;
535
2
        std::set<int> read_column_field_ids_set;
536
2
        for (const auto& field_id : read_column_field_ids) {
537
2
            read_column_field_ids_set.insert(field_id);
538
2
            _equality_delete_col_ids.insert(field_id);
539
2
        }
540
541
2
        std::unique_ptr<GenericReader> delete_reader = _create_equality_reader(delete_desc);
542
2
        RETURN_IF_ERROR(delete_reader->init_schema_reader());
543
544
2
        std::vector<std::string> equality_delete_col_names;
545
2
        std::vector<DataTypePtr> equality_delete_col_types;
546
547
        // Build delete col names/types/ids by matching field_ids from delete file schema.
548
        // Master iterates delete file's FieldDescriptor and uses field_id to match,
549
        // NOT idx-based pairing (get_parsed_schema order != field_ids order).
550
2
        std::vector<std::string> delete_col_names;
551
2
        std::vector<DataTypePtr> delete_col_types;
552
2
        std::vector<int> delete_col_ids;
553
2
        std::unordered_map<std::string, uint32_t> delete_col_name_to_block_idx;
554
555
2
        if (auto* parquet_reader = typeid_cast<ParquetReader*>(delete_reader.get())) {
556
2
            const FieldDescriptor* delete_field_desc = nullptr;
557
2
            RETURN_IF_ERROR(parquet_reader->get_file_metadata_schema(&delete_field_desc));
558
2
            DCHECK(delete_field_desc != nullptr);
559
560
2
            for (const auto& delete_file_field : delete_field_desc->get_fields_schema()) {
561
2
                if (delete_file_field.field_id == -1) [[unlikely]] {
562
0
                    return Status::DataQualityError(
563
0
                            "missing field id when reading equality delete file");
564
0
                }
565
2
                if (!read_column_field_ids_set.contains(delete_file_field.field_id)) {
566
0
                    continue;
567
0
                }
568
2
                if (delete_file_field.children.size() > 0) [[unlikely]] {
569
0
                    return Status::InternalError(
570
0
                            "can not support read complex column in equality delete file");
571
0
                }
572
573
2
                delete_col_ids.emplace_back(delete_file_field.field_id);
574
2
                delete_col_names.emplace_back(delete_file_field.name);
575
2
                delete_col_types.emplace_back(make_nullable(delete_file_field.data_type));
576
577
2
                int field_id = delete_file_field.field_id;
578
2
                if (!_id_to_block_column_name.contains(field_id)) {
579
2
                    _id_to_block_column_name.emplace(field_id, delete_file_field.name);
580
2
                    _expand_col_names.emplace_back(delete_file_field.name);
581
2
                    _expand_columns.emplace_back(
582
2
                            make_nullable(delete_file_field.data_type)->create_column(),
583
2
                            make_nullable(delete_file_field.data_type), delete_file_field.name);
584
2
                }
585
2
            }
586
4
            for (uint32_t idx = 0; idx < delete_col_names.size(); ++idx) {
587
2
                delete_col_name_to_block_idx[delete_col_names[idx]] = idx;
588
2
            }
589
            // Delete files have TFileRangeDesc.size=-1, which would cause
590
            // set_fill_columns to return EndOfFile("No row group to read")
591
            // when _filter_groups is true. Master passes filter_groups=false.
592
2
            ParquetInitContext eq_delete_ctx;
593
2
            eq_delete_ctx.filter_groups = false;
594
2
            eq_delete_ctx.column_names = delete_col_names;
595
2
            eq_delete_ctx.col_name_to_block_idx = &delete_col_name_to_block_idx;
596
2
            auto st2 = parquet_reader->init_reader(&eq_delete_ctx);
597
2
            if (!st2.ok()) {
598
0
                return st2;
599
0
            }
600
2
        } else if (auto* orc_reader = typeid_cast<OrcReader*>(delete_reader.get())) {
601
            // For ORC: use get_parsed_schema with field_ids from delete_file
602
            // ORC field_ids come from the Thrift descriptor, not from ORC metadata
603
0
            RETURN_IF_ERROR(delete_reader->get_parsed_schema(&equality_delete_col_names,
604
0
                                                             &equality_delete_col_types));
605
0
            for (uint32_t idx = 0; idx < equality_delete_col_names.size(); ++idx) {
606
0
                if (idx < read_column_field_ids.size()) {
607
0
                    int field_id = read_column_field_ids[idx];
608
0
                    if (!read_column_field_ids_set.contains(field_id)) continue;
609
0
                    delete_col_ids.emplace_back(field_id);
610
0
                    delete_col_names.emplace_back(equality_delete_col_names[idx]);
611
0
                    delete_col_types.emplace_back(make_nullable(equality_delete_col_types[idx]));
612
0
                    if (!_id_to_block_column_name.contains(field_id)) {
613
0
                        _id_to_block_column_name.emplace(field_id, equality_delete_col_names[idx]);
614
0
                        _expand_col_names.emplace_back(equality_delete_col_names[idx]);
615
0
                        _expand_columns.emplace_back(
616
0
                                make_nullable(equality_delete_col_types[idx])->create_column(),
617
0
                                make_nullable(equality_delete_col_types[idx]),
618
0
                                equality_delete_col_names[idx]);
619
0
                    }
620
0
                }
621
0
            }
622
0
            for (uint32_t idx = 0; idx < delete_col_names.size(); ++idx) {
623
0
                delete_col_name_to_block_idx[delete_col_names[idx]] = idx;
624
0
            }
625
0
            OrcInitContext eq_delete_ctx;
626
0
            eq_delete_ctx.column_names = delete_col_names;
627
0
            eq_delete_ctx.col_name_to_block_idx = &delete_col_name_to_block_idx;
628
0
            RETURN_IF_ERROR(orc_reader->init_reader(&eq_delete_ctx));
629
0
        } else {
630
0
            return Status::InternalError("Unsupported format of delete file");
631
0
        }
632
633
2
        if (!_equality_delete_block_map.contains(delete_col_ids)) {
634
2
            _equality_delete_block_map.emplace(delete_col_ids, _equality_delete_blocks.size());
635
2
            Block block;
636
2
            _generate_equality_delete_block(&block, delete_col_names, delete_col_types);
637
2
            _equality_delete_blocks.emplace_back(block);
638
2
        }
639
2
        Block& eq_file_block = _equality_delete_blocks[_equality_delete_block_map[delete_col_ids]];
640
641
2
        bool eof = false;
642
4
        while (!eof) {
643
2
            Block tmp_block;
644
2
            _generate_equality_delete_block(&tmp_block, delete_col_names, delete_col_types);
645
2
            size_t read_rows = 0;
646
2
            auto st = delete_reader->get_next_block(&tmp_block, &read_rows, &eof);
647
2
            if (!st.ok()) {
648
0
                return st;
649
0
            }
650
2
            if (read_rows > 0) {
651
2
                ScopedMutableBlock scoped_mutable_block(&eq_file_block);
652
2
                auto& mutable_block = scoped_mutable_block.mutable_block();
653
2
                RETURN_IF_ERROR(mutable_block.merge(tmp_block));
654
2
            }
655
2
        }
656
2
    }
657
658
2
    for (const auto& [delete_col_ids, block_idx] : _equality_delete_block_map) {
659
2
        auto& eq_file_block = _equality_delete_blocks[block_idx];
660
2
        auto equality_delete_impl =
661
2
                EqualityDeleteBase::get_delete_impl(&eq_file_block, delete_col_ids);
662
2
        RETURN_IF_ERROR(equality_delete_impl->init(this->get_profile()));
663
2
        _equality_delete_impls.emplace_back(std::move(equality_delete_impl));
664
2
    }
665
2
    return Status::OK();
666
2
}
_ZN5doris18IcebergReaderMixinINS_9OrcReaderEE21_equality_delete_baseERKSt6vectorINS_22TIcebergDeleteFileDescESaIS4_EE
Line
Count
Source
517
2
        const std::vector<TIcebergDeleteFileDesc>& delete_files) {
518
2
    std::unordered_map<std::string, std::tuple<std::string, const SlotDescriptor*>>
519
2
            partition_columns;
520
2
    std::unordered_map<std::string, VExprContextSPtr> missing_columns;
521
522
2
    for (const auto& delete_file : delete_files) {
523
2
        TFileRangeDesc delete_desc;
524
2
        delete_desc.__set_fs_name(this->get_scan_range().fs_name);
525
2
        delete_desc.path = delete_file.path;
526
2
        delete_desc.start_offset = 0;
527
2
        delete_desc.size = -1;
528
2
        delete_desc.file_size = -1;
529
530
2
        if (!delete_file.__isset.field_ids) [[unlikely]] {
531
0
            return Status::InternalError(
532
0
                    "missing delete field ids when reading equality delete file");
533
0
        }
534
2
        auto& read_column_field_ids = delete_file.field_ids;
535
2
        std::set<int> read_column_field_ids_set;
536
2
        for (const auto& field_id : read_column_field_ids) {
537
2
            read_column_field_ids_set.insert(field_id);
538
2
            _equality_delete_col_ids.insert(field_id);
539
2
        }
540
541
2
        std::unique_ptr<GenericReader> delete_reader = _create_equality_reader(delete_desc);
542
2
        RETURN_IF_ERROR(delete_reader->init_schema_reader());
543
544
2
        std::vector<std::string> equality_delete_col_names;
545
2
        std::vector<DataTypePtr> equality_delete_col_types;
546
547
        // Build delete col names/types/ids by matching field_ids from delete file schema.
548
        // Master iterates delete file's FieldDescriptor and uses field_id to match,
549
        // NOT idx-based pairing (get_parsed_schema order != field_ids order).
550
2
        std::vector<std::string> delete_col_names;
551
2
        std::vector<DataTypePtr> delete_col_types;
552
2
        std::vector<int> delete_col_ids;
553
2
        std::unordered_map<std::string, uint32_t> delete_col_name_to_block_idx;
554
555
2
        if (auto* parquet_reader = typeid_cast<ParquetReader*>(delete_reader.get())) {
556
0
            const FieldDescriptor* delete_field_desc = nullptr;
557
0
            RETURN_IF_ERROR(parquet_reader->get_file_metadata_schema(&delete_field_desc));
558
0
            DCHECK(delete_field_desc != nullptr);
559
560
0
            for (const auto& delete_file_field : delete_field_desc->get_fields_schema()) {
561
0
                if (delete_file_field.field_id == -1) [[unlikely]] {
562
0
                    return Status::DataQualityError(
563
0
                            "missing field id when reading equality delete file");
564
0
                }
565
0
                if (!read_column_field_ids_set.contains(delete_file_field.field_id)) {
566
0
                    continue;
567
0
                }
568
0
                if (delete_file_field.children.size() > 0) [[unlikely]] {
569
0
                    return Status::InternalError(
570
0
                            "can not support read complex column in equality delete file");
571
0
                }
572
573
0
                delete_col_ids.emplace_back(delete_file_field.field_id);
574
0
                delete_col_names.emplace_back(delete_file_field.name);
575
0
                delete_col_types.emplace_back(make_nullable(delete_file_field.data_type));
576
577
0
                int field_id = delete_file_field.field_id;
578
0
                if (!_id_to_block_column_name.contains(field_id)) {
579
0
                    _id_to_block_column_name.emplace(field_id, delete_file_field.name);
580
0
                    _expand_col_names.emplace_back(delete_file_field.name);
581
0
                    _expand_columns.emplace_back(
582
0
                            make_nullable(delete_file_field.data_type)->create_column(),
583
0
                            make_nullable(delete_file_field.data_type), delete_file_field.name);
584
0
                }
585
0
            }
586
0
            for (uint32_t idx = 0; idx < delete_col_names.size(); ++idx) {
587
0
                delete_col_name_to_block_idx[delete_col_names[idx]] = idx;
588
0
            }
589
            // Delete files have TFileRangeDesc.size=-1, which would cause
590
            // set_fill_columns to return EndOfFile("No row group to read")
591
            // when _filter_groups is true. Master passes filter_groups=false.
592
0
            ParquetInitContext eq_delete_ctx;
593
0
            eq_delete_ctx.filter_groups = false;
594
0
            eq_delete_ctx.column_names = delete_col_names;
595
0
            eq_delete_ctx.col_name_to_block_idx = &delete_col_name_to_block_idx;
596
0
            auto st2 = parquet_reader->init_reader(&eq_delete_ctx);
597
0
            if (!st2.ok()) {
598
0
                return st2;
599
0
            }
600
2
        } else if (auto* orc_reader = typeid_cast<OrcReader*>(delete_reader.get())) {
601
            // For ORC: use get_parsed_schema with field_ids from delete_file
602
            // ORC field_ids come from the Thrift descriptor, not from ORC metadata
603
2
            RETURN_IF_ERROR(delete_reader->get_parsed_schema(&equality_delete_col_names,
604
2
                                                             &equality_delete_col_types));
605
4
            for (uint32_t idx = 0; idx < equality_delete_col_names.size(); ++idx) {
606
2
                if (idx < read_column_field_ids.size()) {
607
2
                    int field_id = read_column_field_ids[idx];
608
2
                    if (!read_column_field_ids_set.contains(field_id)) continue;
609
2
                    delete_col_ids.emplace_back(field_id);
610
2
                    delete_col_names.emplace_back(equality_delete_col_names[idx]);
611
2
                    delete_col_types.emplace_back(make_nullable(equality_delete_col_types[idx]));
612
2
                    if (!_id_to_block_column_name.contains(field_id)) {
613
2
                        _id_to_block_column_name.emplace(field_id, equality_delete_col_names[idx]);
614
2
                        _expand_col_names.emplace_back(equality_delete_col_names[idx]);
615
2
                        _expand_columns.emplace_back(
616
2
                                make_nullable(equality_delete_col_types[idx])->create_column(),
617
2
                                make_nullable(equality_delete_col_types[idx]),
618
2
                                equality_delete_col_names[idx]);
619
2
                    }
620
2
                }
621
2
            }
622
4
            for (uint32_t idx = 0; idx < delete_col_names.size(); ++idx) {
623
2
                delete_col_name_to_block_idx[delete_col_names[idx]] = idx;
624
2
            }
625
2
            OrcInitContext eq_delete_ctx;
626
2
            eq_delete_ctx.column_names = delete_col_names;
627
2
            eq_delete_ctx.col_name_to_block_idx = &delete_col_name_to_block_idx;
628
2
            RETURN_IF_ERROR(orc_reader->init_reader(&eq_delete_ctx));
629
2
        } else {
630
0
            return Status::InternalError("Unsupported format of delete file");
631
0
        }
632
633
2
        if (!_equality_delete_block_map.contains(delete_col_ids)) {
634
2
            _equality_delete_block_map.emplace(delete_col_ids, _equality_delete_blocks.size());
635
2
            Block block;
636
2
            _generate_equality_delete_block(&block, delete_col_names, delete_col_types);
637
2
            _equality_delete_blocks.emplace_back(block);
638
2
        }
639
2
        Block& eq_file_block = _equality_delete_blocks[_equality_delete_block_map[delete_col_ids]];
640
641
2
        bool eof = false;
642
6
        while (!eof) {
643
4
            Block tmp_block;
644
4
            _generate_equality_delete_block(&tmp_block, delete_col_names, delete_col_types);
645
4
            size_t read_rows = 0;
646
4
            auto st = delete_reader->get_next_block(&tmp_block, &read_rows, &eof);
647
4
            if (!st.ok()) {
648
0
                return st;
649
0
            }
650
4
            if (read_rows > 0) {
651
2
                ScopedMutableBlock scoped_mutable_block(&eq_file_block);
652
2
                auto& mutable_block = scoped_mutable_block.mutable_block();
653
2
                RETURN_IF_ERROR(mutable_block.merge(tmp_block));
654
2
            }
655
4
        }
656
2
    }
657
658
2
    for (const auto& [delete_col_ids, block_idx] : _equality_delete_block_map) {
659
2
        auto& eq_file_block = _equality_delete_blocks[block_idx];
660
2
        auto equality_delete_impl =
661
2
                EqualityDeleteBase::get_delete_impl(&eq_file_block, delete_col_ids);
662
2
        RETURN_IF_ERROR(equality_delete_impl->init(this->get_profile()));
663
2
        _equality_delete_impls.emplace_back(std::move(equality_delete_impl));
664
2
    }
665
2
    return Status::OK();
666
2
}
667
668
template <typename BaseReader>
669
void IcebergReaderMixin<BaseReader>::_generate_equality_delete_block(
670
        Block* block, const std::vector<std::string>& equality_delete_col_names,
671
10
        const std::vector<DataTypePtr>& equality_delete_col_types) {
672
20
    for (int i = 0; i < equality_delete_col_names.size(); ++i) {
673
10
        DataTypePtr data_type = make_nullable(equality_delete_col_types[i]);
674
10
        MutableColumnPtr data_column = data_type->create_column();
675
10
        block->insert(ColumnWithTypeAndName(std::move(data_column), data_type,
676
10
                                            equality_delete_col_names[i]));
677
10
    }
678
10
}
_ZN5doris18IcebergReaderMixinINS_13ParquetReaderEE31_generate_equality_delete_blockEPNS_5BlockERKSt6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaISB_EERKS5_ISt10shared_ptrIKNS_9IDataTypeEESaISJ_EE
Line
Count
Source
671
4
        const std::vector<DataTypePtr>& equality_delete_col_types) {
672
8
    for (int i = 0; i < equality_delete_col_names.size(); ++i) {
673
4
        DataTypePtr data_type = make_nullable(equality_delete_col_types[i]);
674
4
        MutableColumnPtr data_column = data_type->create_column();
675
4
        block->insert(ColumnWithTypeAndName(std::move(data_column), data_type,
676
4
                                            equality_delete_col_names[i]));
677
4
    }
678
4
}
_ZN5doris18IcebergReaderMixinINS_9OrcReaderEE31_generate_equality_delete_blockEPNS_5BlockERKSt6vectorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESaISB_EERKS5_ISt10shared_ptrIKNS_9IDataTypeEESaISJ_EE
Line
Count
Source
671
6
        const std::vector<DataTypePtr>& equality_delete_col_types) {
672
12
    for (int i = 0; i < equality_delete_col_names.size(); ++i) {
673
6
        DataTypePtr data_type = make_nullable(equality_delete_col_types[i]);
674
6
        MutableColumnPtr data_column = data_type->create_column();
675
6
        block->insert(ColumnWithTypeAndName(std::move(data_column), data_type,
676
6
                                            equality_delete_col_names[i]));
677
6
    }
678
6
}
679
680
template <typename BaseReader>
681
9
Status IcebergReaderMixin<BaseReader>::_expand_block_if_need(Block* block) {
682
9
    std::set<std::string> names;
683
9
    auto block_names = block->get_names();
684
9
    names.insert(block_names.begin(), block_names.end());
685
9
    for (auto& col : _expand_columns) {
686
4
        if (_missing_equality_delete_values.contains(col.name)) {
687
            // Missing equality keys are logical columns, not physical file columns. Add them only
688
            // after the base reader has established the batch row count.
689
1
            continue;
690
1
        }
691
3
        if (names.contains(col.name)) {
692
0
            return Status::InternalError("Wrong expand column '{}'", col.name);
693
0
        }
694
3
        names.insert(col.name);
695
3
        (*this->col_name_to_block_idx_ref())[col.name] = block->columns();
696
3
        block->insert({col.type->create_column(), col.type, col.name});
697
3
    }
698
9
    return Status::OK();
699
9
}
_ZN5doris18IcebergReaderMixinINS_13ParquetReaderEE21_expand_block_if_needEPNS_5BlockE
Line
Count
Source
681
5
Status IcebergReaderMixin<BaseReader>::_expand_block_if_need(Block* block) {
682
5
    std::set<std::string> names;
683
5
    auto block_names = block->get_names();
684
5
    names.insert(block_names.begin(), block_names.end());
685
5
    for (auto& col : _expand_columns) {
686
2
        if (_missing_equality_delete_values.contains(col.name)) {
687
            // Missing equality keys are logical columns, not physical file columns. Add them only
688
            // after the base reader has established the batch row count.
689
0
            continue;
690
0
        }
691
2
        if (names.contains(col.name)) {
692
0
            return Status::InternalError("Wrong expand column '{}'", col.name);
693
0
        }
694
2
        names.insert(col.name);
695
2
        (*this->col_name_to_block_idx_ref())[col.name] = block->columns();
696
2
        block->insert({col.type->create_column(), col.type, col.name});
697
2
    }
698
5
    return Status::OK();
699
5
}
_ZN5doris18IcebergReaderMixinINS_9OrcReaderEE21_expand_block_if_needEPNS_5BlockE
Line
Count
Source
681
4
Status IcebergReaderMixin<BaseReader>::_expand_block_if_need(Block* block) {
682
4
    std::set<std::string> names;
683
4
    auto block_names = block->get_names();
684
4
    names.insert(block_names.begin(), block_names.end());
685
4
    for (auto& col : _expand_columns) {
686
2
        if (_missing_equality_delete_values.contains(col.name)) {
687
            // Missing equality keys are logical columns, not physical file columns. Add them only
688
            // after the base reader has established the batch row count.
689
1
            continue;
690
1
        }
691
1
        if (names.contains(col.name)) {
692
0
            return Status::InternalError("Wrong expand column '{}'", col.name);
693
0
        }
694
1
        names.insert(col.name);
695
1
        (*this->col_name_to_block_idx_ref())[col.name] = block->columns();
696
1
        block->insert({col.type->create_column(), col.type, col.name});
697
1
    }
698
4
    return Status::OK();
699
4
}
700
701
template <typename BaseReader>
702
2
const schema::external::TStructField* IcebergReaderMixin<BaseReader>::_current_schema_root() const {
703
2
    const auto& scan_params = this->get_scan_params();
704
2
    if (!scan_params.__isset.history_schema_info || scan_params.history_schema_info.empty()) {
705
0
        return nullptr;
706
0
    }
707
2
    const schema::external::TSchema* current_schema = &scan_params.history_schema_info.front();
708
2
    if (scan_params.__isset.current_schema_id) {
709
2
        const auto schema_it = std::ranges::find_if(
710
2
                scan_params.history_schema_info, [&](const schema::external::TSchema& schema) {
711
2
                    return schema.__isset.schema_id &&
712
2
                           schema.schema_id == scan_params.current_schema_id;
713
2
                });
_ZZNK5doris18IcebergReaderMixinINS_13ParquetReaderEE20_current_schema_rootEvENKUlRKNS_6schema8external7TSchemaEE_clES7_
Line
Count
Source
710
2
                scan_params.history_schema_info, [&](const schema::external::TSchema& schema) {
711
2
                    return schema.__isset.schema_id &&
712
2
                           schema.schema_id == scan_params.current_schema_id;
713
2
                });
Unexecuted instantiation: _ZZNK5doris18IcebergReaderMixinINS_9OrcReaderEE20_current_schema_rootEvENKUlRKNS_6schema8external7TSchemaEE_clES7_
714
2
        if (schema_it == scan_params.history_schema_info.end()) {
715
0
            return nullptr;
716
0
        }
717
2
        current_schema = &*schema_it;
718
2
    }
719
2
    return current_schema->__isset.root_field ? &current_schema->root_field : nullptr;
720
2
}
_ZNK5doris18IcebergReaderMixinINS_13ParquetReaderEE20_current_schema_rootEv
Line
Count
Source
702
2
const schema::external::TStructField* IcebergReaderMixin<BaseReader>::_current_schema_root() const {
703
2
    const auto& scan_params = this->get_scan_params();
704
2
    if (!scan_params.__isset.history_schema_info || scan_params.history_schema_info.empty()) {
705
0
        return nullptr;
706
0
    }
707
2
    const schema::external::TSchema* current_schema = &scan_params.history_schema_info.front();
708
2
    if (scan_params.__isset.current_schema_id) {
709
2
        const auto schema_it = std::ranges::find_if(
710
2
                scan_params.history_schema_info, [&](const schema::external::TSchema& schema) {
711
2
                    return schema.__isset.schema_id &&
712
2
                           schema.schema_id == scan_params.current_schema_id;
713
2
                });
714
2
        if (schema_it == scan_params.history_schema_info.end()) {
715
0
            return nullptr;
716
0
        }
717
2
        current_schema = &*schema_it;
718
2
    }
719
2
    return current_schema->__isset.root_field ? &current_schema->root_field : nullptr;
720
2
}
Unexecuted instantiation: _ZNK5doris18IcebergReaderMixinINS_9OrcReaderEE20_current_schema_rootEv
721
722
template <typename BaseReader>
723
const schema::external::TField* IcebergReaderMixin<BaseReader>::_find_current_schema_field(
724
1
        const std::string& name) const {
725
1
    const auto* root = _current_schema_root();
726
1
    if (root == nullptr) {
727
0
        return nullptr;
728
0
    }
729
1
    const auto field = std::ranges::find_if(root->fields, [&](const auto& field_ptr) {
730
1
        return field_ptr.__isset.field_ptr && field_ptr.field_ptr != nullptr &&
731
1
               field_ptr.field_ptr->__isset.name && field_ptr.field_ptr->name == name;
732
1
    });
_ZZNK5doris18IcebergReaderMixinINS_13ParquetReaderEE26_find_current_schema_fieldERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEENKUlRKT_E_clINS_6schema8external9TFieldPtrEEEDaSD_
Line
Count
Source
729
1
    const auto field = std::ranges::find_if(root->fields, [&](const auto& field_ptr) {
730
1
        return field_ptr.__isset.field_ptr && field_ptr.field_ptr != nullptr &&
731
1
               field_ptr.field_ptr->__isset.name && field_ptr.field_ptr->name == name;
732
1
    });
Unexecuted instantiation: _ZZNK5doris18IcebergReaderMixinINS_9OrcReaderEE26_find_current_schema_fieldERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEENKUlRKT_E_clINS_6schema8external9TFieldPtrEEEDaSD_
733
1
    return field == root->fields.end() ? nullptr : field->field_ptr.get();
734
1
}
_ZNK5doris18IcebergReaderMixinINS_13ParquetReaderEE26_find_current_schema_fieldERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
Line
Count
Source
724
1
        const std::string& name) const {
725
1
    const auto* root = _current_schema_root();
726
1
    if (root == nullptr) {
727
0
        return nullptr;
728
0
    }
729
1
    const auto field = std::ranges::find_if(root->fields, [&](const auto& field_ptr) {
730
1
        return field_ptr.__isset.field_ptr && field_ptr.field_ptr != nullptr &&
731
1
               field_ptr.field_ptr->__isset.name && field_ptr.field_ptr->name == name;
732
1
    });
733
1
    return field == root->fields.end() ? nullptr : field->field_ptr.get();
734
1
}
Unexecuted instantiation: _ZNK5doris18IcebergReaderMixinINS_9OrcReaderEE26_find_current_schema_fieldERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
735
736
template <typename BaseReader>
737
const schema::external::TField* IcebergReaderMixin<BaseReader>::_find_current_schema_field(
738
1
        int32_t field_id) const {
739
1
    const auto* root = _current_schema_root();
740
1
    if (root == nullptr) {
741
0
        return nullptr;
742
0
    }
743
2
    const auto field = std::ranges::find_if(root->fields, [&](const auto& field_ptr) {
744
2
        return field_ptr.__isset.field_ptr && field_ptr.field_ptr != nullptr &&
745
2
               field_ptr.field_ptr->__isset.id && field_ptr.field_ptr->id == field_id;
746
2
    });
_ZZNK5doris18IcebergReaderMixinINS_13ParquetReaderEE26_find_current_schema_fieldEiENKUlRKT_E_clINS_6schema8external9TFieldPtrEEEDaS5_
Line
Count
Source
743
2
    const auto field = std::ranges::find_if(root->fields, [&](const auto& field_ptr) {
744
2
        return field_ptr.__isset.field_ptr && field_ptr.field_ptr != nullptr &&
745
2
               field_ptr.field_ptr->__isset.id && field_ptr.field_ptr->id == field_id;
746
2
    });
Unexecuted instantiation: _ZZNK5doris18IcebergReaderMixinINS_9OrcReaderEE26_find_current_schema_fieldEiENKUlRKT_E_clINS_6schema8external9TFieldPtrEEEDaS5_
747
1
    return field == root->fields.end() ? nullptr : field->field_ptr.get();
748
1
}
_ZNK5doris18IcebergReaderMixinINS_13ParquetReaderEE26_find_current_schema_fieldEi
Line
Count
Source
738
1
        int32_t field_id) const {
739
1
    const auto* root = _current_schema_root();
740
1
    if (root == nullptr) {
741
0
        return nullptr;
742
0
    }
743
1
    const auto field = std::ranges::find_if(root->fields, [&](const auto& field_ptr) {
744
1
        return field_ptr.__isset.field_ptr && field_ptr.field_ptr != nullptr &&
745
1
               field_ptr.field_ptr->__isset.id && field_ptr.field_ptr->id == field_id;
746
1
    });
747
1
    return field == root->fields.end() ? nullptr : field->field_ptr.get();
748
1
}
Unexecuted instantiation: _ZNK5doris18IcebergReaderMixinINS_9OrcReaderEE26_find_current_schema_fieldEi
749
750
template <typename BaseReader>
751
Status IcebergReaderMixin<BaseReader>::_build_owned_initial_default_column(
752
        const schema::external::TField& field, const DataTypePtr& type, size_t rows,
753
6
        ColumnPtr* column) const {
754
6
    DORIS_CHECK(type != nullptr);
755
6
    DORIS_CHECK(column != nullptr);
756
6
    DORIS_CHECK(field.__isset.initial_default_value);
757
6
    const auto nested_type = remove_nullable(type);
758
6
    const bool is_base64 =
759
6
            (field.__isset.initial_default_value_is_base64 &&
760
6
             field.initial_default_value_is_base64) ||
761
6
            (field.__isset.type && thrift_to_type(field.type.type) == TYPE_VARBINARY);
762
6
    Field value;
763
6
    if (is_base64) {
764
3
        std::string decoded;
765
3
        if (!base64_decode(field.initial_default_value, &decoded)) {
766
0
            return Status::InvalidArgument("Invalid Base64 Iceberg initial default for field {}",
767
0
                                           field.name);
768
0
        }
769
3
        if (nested_type->get_primitive_type() == TYPE_VARBINARY) {
770
2
            value = Field::create_field<TYPE_VARBINARY>(StringView(decoded));
771
2
        } else {
772
1
            DORIS_CHECK(is_string_type(nested_type->get_primitive_type()));
773
1
            value = Field::create_field<TYPE_STRING>(decoded);
774
1
        }
775
        // Variable-width Fields borrow decoded. Materialize before it leaves scope so every V1
776
        // missing-column and equality-delete boundary keeps UUID/FIXED payloads alive.
777
3
        *column = type->create_column_const(rows, value);
778
3
        return Status::OK();
779
3
    }
780
3
    RETURN_IF_ERROR(nested_type->get_serde()->from_fe_string(field.initial_default_value, value));
781
3
    *column = type->create_column_const(rows, value);
782
3
    return Status::OK();
783
3
}
_ZNK5doris18IcebergReaderMixinINS_13ParquetReaderEE35_build_owned_initial_default_columnERKNS_6schema8external6TFieldERKSt10shared_ptrIKNS_9IDataTypeEEmPNS_3COWINS_7IColumnEE13immutable_ptrISF_EE
Line
Count
Source
753
5
        ColumnPtr* column) const {
754
5
    DORIS_CHECK(type != nullptr);
755
5
    DORIS_CHECK(column != nullptr);
756
5
    DORIS_CHECK(field.__isset.initial_default_value);
757
5
    const auto nested_type = remove_nullable(type);
758
5
    const bool is_base64 =
759
5
            (field.__isset.initial_default_value_is_base64 &&
760
5
             field.initial_default_value_is_base64) ||
761
5
            (field.__isset.type && thrift_to_type(field.type.type) == TYPE_VARBINARY);
762
5
    Field value;
763
5
    if (is_base64) {
764
3
        std::string decoded;
765
3
        if (!base64_decode(field.initial_default_value, &decoded)) {
766
0
            return Status::InvalidArgument("Invalid Base64 Iceberg initial default for field {}",
767
0
                                           field.name);
768
0
        }
769
3
        if (nested_type->get_primitive_type() == TYPE_VARBINARY) {
770
2
            value = Field::create_field<TYPE_VARBINARY>(StringView(decoded));
771
2
        } else {
772
1
            DORIS_CHECK(is_string_type(nested_type->get_primitive_type()));
773
1
            value = Field::create_field<TYPE_STRING>(decoded);
774
1
        }
775
        // Variable-width Fields borrow decoded. Materialize before it leaves scope so every V1
776
        // missing-column and equality-delete boundary keeps UUID/FIXED payloads alive.
777
3
        *column = type->create_column_const(rows, value);
778
3
        return Status::OK();
779
3
    }
780
2
    RETURN_IF_ERROR(nested_type->get_serde()->from_fe_string(field.initial_default_value, value));
781
2
    *column = type->create_column_const(rows, value);
782
2
    return Status::OK();
783
2
}
_ZNK5doris18IcebergReaderMixinINS_9OrcReaderEE35_build_owned_initial_default_columnERKNS_6schema8external6TFieldERKSt10shared_ptrIKNS_9IDataTypeEEmPNS_3COWINS_7IColumnEE13immutable_ptrISF_EE
Line
Count
Source
753
1
        ColumnPtr* column) const {
754
1
    DORIS_CHECK(type != nullptr);
755
1
    DORIS_CHECK(column != nullptr);
756
1
    DORIS_CHECK(field.__isset.initial_default_value);
757
1
    const auto nested_type = remove_nullable(type);
758
1
    const bool is_base64 =
759
1
            (field.__isset.initial_default_value_is_base64 &&
760
1
             field.initial_default_value_is_base64) ||
761
1
            (field.__isset.type && thrift_to_type(field.type.type) == TYPE_VARBINARY);
762
1
    Field value;
763
1
    if (is_base64) {
764
0
        std::string decoded;
765
0
        if (!base64_decode(field.initial_default_value, &decoded)) {
766
0
            return Status::InvalidArgument("Invalid Base64 Iceberg initial default for field {}",
767
0
                                           field.name);
768
0
        }
769
0
        if (nested_type->get_primitive_type() == TYPE_VARBINARY) {
770
0
            value = Field::create_field<TYPE_VARBINARY>(StringView(decoded));
771
0
        } else {
772
0
            DORIS_CHECK(is_string_type(nested_type->get_primitive_type()));
773
0
            value = Field::create_field<TYPE_STRING>(decoded);
774
0
        }
775
        // Variable-width Fields borrow decoded. Materialize before it leaves scope so every V1
776
        // missing-column and equality-delete boundary keeps UUID/FIXED payloads alive.
777
0
        *column = type->create_column_const(rows, value);
778
0
        return Status::OK();
779
0
    }
780
1
    RETURN_IF_ERROR(nested_type->get_serde()->from_fe_string(field.initial_default_value, value));
781
1
    *column = type->create_column_const(rows, value);
782
1
    return Status::OK();
783
1
}
784
785
template <typename BaseReader>
786
Status IcebergReaderMixin<BaseReader>::_register_missing_equality_delete_column(
787
6
        int32_t field_id, const std::string& name, const DataTypePtr& delete_key_type) {
788
6
    DORIS_CHECK(delete_key_type != nullptr);
789
6
    const schema::external::TField* table_field = nullptr;
790
6
    const auto& scan_params = this->get_scan_params();
791
6
    const schema::external::TSchema* current_schema = nullptr;
792
6
    if (scan_params.__isset.history_schema_info && !scan_params.history_schema_info.empty()) {
793
6
        current_schema = &scan_params.history_schema_info.front();
794
6
    }
795
6
    if (current_schema != nullptr && scan_params.__isset.current_schema_id) {
796
6
        const auto schema_it = std::ranges::find_if(
797
6
                scan_params.history_schema_info, [&](const schema::external::TSchema& schema) {
798
6
                    return schema.__isset.schema_id &&
799
6
                           schema.schema_id == scan_params.current_schema_id;
800
6
                });
_ZZN5doris18IcebergReaderMixinINS_13ParquetReaderEE40_register_missing_equality_delete_columnEiRKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt10shared_ptrIKNS_9IDataTypeEEENKUlRKNS_6schema8external7TSchemaEE_clESL_
Line
Count
Source
797
5
                scan_params.history_schema_info, [&](const schema::external::TSchema& schema) {
798
5
                    return schema.__isset.schema_id &&
799
5
                           schema.schema_id == scan_params.current_schema_id;
800
5
                });
_ZZN5doris18IcebergReaderMixinINS_9OrcReaderEE40_register_missing_equality_delete_columnEiRKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt10shared_ptrIKNS_9IDataTypeEEENKUlRKNS_6schema8external7TSchemaEE_clESL_
Line
Count
Source
797
1
                scan_params.history_schema_info, [&](const schema::external::TSchema& schema) {
798
1
                    return schema.__isset.schema_id &&
799
1
                           schema.schema_id == scan_params.current_schema_id;
800
1
                });
801
6
        if (schema_it != scan_params.history_schema_info.end()) {
802
6
            current_schema = &*schema_it;
803
6
        } else {
804
0
            current_schema = nullptr;
805
0
        }
806
6
    }
807
6
    if (current_schema != nullptr && current_schema->__isset.root_field) {
808
11
        for (const auto& field_ptr : current_schema->root_field.fields) {
809
11
            if (field_ptr.__isset.field_ptr && field_ptr.field_ptr != nullptr &&
810
11
                field_ptr.field_ptr->__isset.id && field_ptr.field_ptr->id == field_id) {
811
5
                table_field = field_ptr.field_ptr.get();
812
5
                break;
813
5
            }
814
11
        }
815
6
    }
816
6
    if (table_field == nullptr) {
817
        // A projected descriptor from an older FE may omit a hidden equality key. Without the
818
        // current Iceberg field metadata BE cannot distinguish a true NULL initial default from a
819
        // non-NULL default, so continuing would risk silently keeping or deleting wrong rows.
820
1
        return Status::InternalError(
821
1
                "Missing Iceberg schema metadata for equality-delete field id {}", field_id);
822
1
    }
823
824
5
    ColumnPtr value_column;
825
5
    if (table_field->__isset.initial_default_value) {
826
5
        RETURN_IF_ERROR(_build_owned_initial_default_column(*table_field, delete_key_type, 1,
827
5
                                                            &value_column));
828
5
    } else {
829
0
        value_column = delete_key_type->create_column_const(1, Field());
830
0
    }
831
5
    const bool inserted =
832
5
            _missing_equality_delete_values.emplace(name, std::move(value_column)).second;
833
5
    DORIS_CHECK(inserted);
834
5
    this->register_synthesized_column_handler(
835
5
            name, [this, name](Block* block, size_t rows) -> Status {
836
1
                DORIS_CHECK(_missing_equality_delete_values.contains(name));
837
1
                return _materialize_missing_equality_delete_column(
838
1
                        block, name, _missing_equality_delete_values.at(name), rows);
839
1
            });
Unexecuted instantiation: _ZZN5doris18IcebergReaderMixinINS_13ParquetReaderEE40_register_missing_equality_delete_columnEiRKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt10shared_ptrIKNS_9IDataTypeEEENKUlPNS_5BlockEmE_clESI_m
_ZZN5doris18IcebergReaderMixinINS_9OrcReaderEE40_register_missing_equality_delete_columnEiRKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt10shared_ptrIKNS_9IDataTypeEEENKUlPNS_5BlockEmE_clESI_m
Line
Count
Source
835
1
            name, [this, name](Block* block, size_t rows) -> Status {
836
1
                DORIS_CHECK(_missing_equality_delete_values.contains(name));
837
1
                return _materialize_missing_equality_delete_column(
838
1
                        block, name, _missing_equality_delete_values.at(name), rows);
839
1
            });
840
5
    return Status::OK();
841
5
}
_ZN5doris18IcebergReaderMixinINS_13ParquetReaderEE40_register_missing_equality_delete_columnEiRKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt10shared_ptrIKNS_9IDataTypeEE
Line
Count
Source
787
5
        int32_t field_id, const std::string& name, const DataTypePtr& delete_key_type) {
788
5
    DORIS_CHECK(delete_key_type != nullptr);
789
5
    const schema::external::TField* table_field = nullptr;
790
5
    const auto& scan_params = this->get_scan_params();
791
5
    const schema::external::TSchema* current_schema = nullptr;
792
5
    if (scan_params.__isset.history_schema_info && !scan_params.history_schema_info.empty()) {
793
5
        current_schema = &scan_params.history_schema_info.front();
794
5
    }
795
5
    if (current_schema != nullptr && scan_params.__isset.current_schema_id) {
796
5
        const auto schema_it = std::ranges::find_if(
797
5
                scan_params.history_schema_info, [&](const schema::external::TSchema& schema) {
798
5
                    return schema.__isset.schema_id &&
799
5
                           schema.schema_id == scan_params.current_schema_id;
800
5
                });
801
5
        if (schema_it != scan_params.history_schema_info.end()) {
802
5
            current_schema = &*schema_it;
803
5
        } else {
804
0
            current_schema = nullptr;
805
0
        }
806
5
    }
807
5
    if (current_schema != nullptr && current_schema->__isset.root_field) {
808
9
        for (const auto& field_ptr : current_schema->root_field.fields) {
809
9
            if (field_ptr.__isset.field_ptr && field_ptr.field_ptr != nullptr &&
810
9
                field_ptr.field_ptr->__isset.id && field_ptr.field_ptr->id == field_id) {
811
4
                table_field = field_ptr.field_ptr.get();
812
4
                break;
813
4
            }
814
9
        }
815
5
    }
816
5
    if (table_field == nullptr) {
817
        // A projected descriptor from an older FE may omit a hidden equality key. Without the
818
        // current Iceberg field metadata BE cannot distinguish a true NULL initial default from a
819
        // non-NULL default, so continuing would risk silently keeping or deleting wrong rows.
820
1
        return Status::InternalError(
821
1
                "Missing Iceberg schema metadata for equality-delete field id {}", field_id);
822
1
    }
823
824
4
    ColumnPtr value_column;
825
4
    if (table_field->__isset.initial_default_value) {
826
4
        RETURN_IF_ERROR(_build_owned_initial_default_column(*table_field, delete_key_type, 1,
827
4
                                                            &value_column));
828
4
    } else {
829
0
        value_column = delete_key_type->create_column_const(1, Field());
830
0
    }
831
4
    const bool inserted =
832
4
            _missing_equality_delete_values.emplace(name, std::move(value_column)).second;
833
4
    DORIS_CHECK(inserted);
834
4
    this->register_synthesized_column_handler(
835
4
            name, [this, name](Block* block, size_t rows) -> Status {
836
4
                DORIS_CHECK(_missing_equality_delete_values.contains(name));
837
4
                return _materialize_missing_equality_delete_column(
838
4
                        block, name, _missing_equality_delete_values.at(name), rows);
839
4
            });
840
4
    return Status::OK();
841
4
}
_ZN5doris18IcebergReaderMixinINS_9OrcReaderEE40_register_missing_equality_delete_columnEiRKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt10shared_ptrIKNS_9IDataTypeEE
Line
Count
Source
787
1
        int32_t field_id, const std::string& name, const DataTypePtr& delete_key_type) {
788
1
    DORIS_CHECK(delete_key_type != nullptr);
789
1
    const schema::external::TField* table_field = nullptr;
790
1
    const auto& scan_params = this->get_scan_params();
791
1
    const schema::external::TSchema* current_schema = nullptr;
792
1
    if (scan_params.__isset.history_schema_info && !scan_params.history_schema_info.empty()) {
793
1
        current_schema = &scan_params.history_schema_info.front();
794
1
    }
795
1
    if (current_schema != nullptr && scan_params.__isset.current_schema_id) {
796
1
        const auto schema_it = std::ranges::find_if(
797
1
                scan_params.history_schema_info, [&](const schema::external::TSchema& schema) {
798
1
                    return schema.__isset.schema_id &&
799
1
                           schema.schema_id == scan_params.current_schema_id;
800
1
                });
801
1
        if (schema_it != scan_params.history_schema_info.end()) {
802
1
            current_schema = &*schema_it;
803
1
        } else {
804
0
            current_schema = nullptr;
805
0
        }
806
1
    }
807
1
    if (current_schema != nullptr && current_schema->__isset.root_field) {
808
2
        for (const auto& field_ptr : current_schema->root_field.fields) {
809
2
            if (field_ptr.__isset.field_ptr && field_ptr.field_ptr != nullptr &&
810
2
                field_ptr.field_ptr->__isset.id && field_ptr.field_ptr->id == field_id) {
811
1
                table_field = field_ptr.field_ptr.get();
812
1
                break;
813
1
            }
814
2
        }
815
1
    }
816
1
    if (table_field == nullptr) {
817
        // A projected descriptor from an older FE may omit a hidden equality key. Without the
818
        // current Iceberg field metadata BE cannot distinguish a true NULL initial default from a
819
        // non-NULL default, so continuing would risk silently keeping or deleting wrong rows.
820
0
        return Status::InternalError(
821
0
                "Missing Iceberg schema metadata for equality-delete field id {}", field_id);
822
0
    }
823
824
1
    ColumnPtr value_column;
825
1
    if (table_field->__isset.initial_default_value) {
826
1
        RETURN_IF_ERROR(_build_owned_initial_default_column(*table_field, delete_key_type, 1,
827
1
                                                            &value_column));
828
1
    } else {
829
0
        value_column = delete_key_type->create_column_const(1, Field());
830
0
    }
831
1
    const bool inserted =
832
1
            _missing_equality_delete_values.emplace(name, std::move(value_column)).second;
833
1
    DORIS_CHECK(inserted);
834
1
    this->register_synthesized_column_handler(
835
1
            name, [this, name](Block* block, size_t rows) -> Status {
836
1
                DORIS_CHECK(_missing_equality_delete_values.contains(name));
837
1
                return _materialize_missing_equality_delete_column(
838
1
                        block, name, _missing_equality_delete_values.at(name), rows);
839
1
            });
840
1
    return Status::OK();
841
1
}
842
843
template <typename BaseReader>
844
Status IcebergReaderMixin<BaseReader>::_materialize_missing_equality_delete_column(
845
5
        Block* block, const std::string& name, const ColumnPtr& value, size_t rows) {
846
5
    if (!this->col_name_to_block_idx_ref()->contains(name)) {
847
        // ORC must not register a key that is absent from the file as a physical child. In that
848
        // case the reader block has no slot for the synthesized key, so append one here before
849
        // equality-delete filtering. MultiEqualityDelete requires a full, batch-sized column.
850
1
        const auto expand_col = std::ranges::find_if(
851
1
                _expand_columns,
852
1
                [&](const ColumnWithTypeAndName& col) { return col.name == name; });
Unexecuted instantiation: _ZZN5doris18IcebergReaderMixinINS_13ParquetReaderEE43_materialize_missing_equality_delete_columnEPNS_5BlockERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKNS_3COWINS_7IColumnEE13immutable_ptrISE_EEmENKUlRKNS_21ColumnWithTypeAndNameEE_clESM_
_ZZN5doris18IcebergReaderMixinINS_9OrcReaderEE43_materialize_missing_equality_delete_columnEPNS_5BlockERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKNS_3COWINS_7IColumnEE13immutable_ptrISE_EEmENKUlRKNS_21ColumnWithTypeAndNameEE_clESM_
Line
Count
Source
852
1
                [&](const ColumnWithTypeAndName& col) { return col.name == name; });
853
1
        DORIS_CHECK(expand_col != _expand_columns.end());
854
1
        (*this->col_name_to_block_idx_ref())[name] = block->columns();
855
1
        block->insert({value->clone_resized(rows)->convert_to_full_column_if_const(),
856
1
                       expand_col->type, name});
857
1
        return Status::OK();
858
1
    }
859
4
    const auto position = this->col_name_to_block_idx_ref()->at(name);
860
4
    DORIS_CHECK(position < block->columns());
861
4
    DORIS_CHECK(block->get_by_position(position).column->empty());
862
    // MultiEqualityDelete hashes each key column directly. Materialize the repeated default so
863
    // every key has the batch row count; a ColumnConst keeps only one nested value and therefore
864
    // cannot participate in the row-wise multi-column hash contract.
865
4
    block->get_by_position(position).column =
866
4
            value->clone_resized(rows)->convert_to_full_column_if_const();
867
4
    return Status::OK();
868
5
}
_ZN5doris18IcebergReaderMixinINS_13ParquetReaderEE43_materialize_missing_equality_delete_columnEPNS_5BlockERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKNS_3COWINS_7IColumnEE13immutable_ptrISE_EEm
Line
Count
Source
845
4
        Block* block, const std::string& name, const ColumnPtr& value, size_t rows) {
846
4
    if (!this->col_name_to_block_idx_ref()->contains(name)) {
847
        // ORC must not register a key that is absent from the file as a physical child. In that
848
        // case the reader block has no slot for the synthesized key, so append one here before
849
        // equality-delete filtering. MultiEqualityDelete requires a full, batch-sized column.
850
0
        const auto expand_col = std::ranges::find_if(
851
0
                _expand_columns,
852
0
                [&](const ColumnWithTypeAndName& col) { return col.name == name; });
853
0
        DORIS_CHECK(expand_col != _expand_columns.end());
854
0
        (*this->col_name_to_block_idx_ref())[name] = block->columns();
855
0
        block->insert({value->clone_resized(rows)->convert_to_full_column_if_const(),
856
0
                       expand_col->type, name});
857
0
        return Status::OK();
858
0
    }
859
4
    const auto position = this->col_name_to_block_idx_ref()->at(name);
860
4
    DORIS_CHECK(position < block->columns());
861
4
    DORIS_CHECK(block->get_by_position(position).column->empty());
862
    // MultiEqualityDelete hashes each key column directly. Materialize the repeated default so
863
    // every key has the batch row count; a ColumnConst keeps only one nested value and therefore
864
    // cannot participate in the row-wise multi-column hash contract.
865
4
    block->get_by_position(position).column =
866
4
            value->clone_resized(rows)->convert_to_full_column_if_const();
867
4
    return Status::OK();
868
4
}
_ZN5doris18IcebergReaderMixinINS_9OrcReaderEE43_materialize_missing_equality_delete_columnEPNS_5BlockERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKNS_3COWINS_7IColumnEE13immutable_ptrISE_EEm
Line
Count
Source
845
1
        Block* block, const std::string& name, const ColumnPtr& value, size_t rows) {
846
1
    if (!this->col_name_to_block_idx_ref()->contains(name)) {
847
        // ORC must not register a key that is absent from the file as a physical child. In that
848
        // case the reader block has no slot for the synthesized key, so append one here before
849
        // equality-delete filtering. MultiEqualityDelete requires a full, batch-sized column.
850
1
        const auto expand_col = std::ranges::find_if(
851
1
                _expand_columns,
852
1
                [&](const ColumnWithTypeAndName& col) { return col.name == name; });
853
1
        DORIS_CHECK(expand_col != _expand_columns.end());
854
1
        (*this->col_name_to_block_idx_ref())[name] = block->columns();
855
1
        block->insert({value->clone_resized(rows)->convert_to_full_column_if_const(),
856
1
                       expand_col->type, name});
857
1
        return Status::OK();
858
1
    }
859
0
    const auto position = this->col_name_to_block_idx_ref()->at(name);
860
0
    DORIS_CHECK(position < block->columns());
861
0
    DORIS_CHECK(block->get_by_position(position).column->empty());
862
    // MultiEqualityDelete hashes each key column directly. Materialize the repeated default so
863
    // every key has the batch row count; a ColumnConst keeps only one nested value and therefore
864
    // cannot participate in the row-wise multi-column hash contract.
865
0
    block->get_by_position(position).column =
866
0
            value->clone_resized(rows)->convert_to_full_column_if_const();
867
0
    return Status::OK();
868
1
}
869
870
template <typename BaseReader>
871
Status IcebergReaderMixin<BaseReader>::_materialize_missing_equality_delete_columns(Block* block,
872
2
                                                                                    size_t rows) {
873
4
    for (const auto& [name, value] : _missing_equality_delete_values) {
874
4
        RETURN_IF_ERROR(_materialize_missing_equality_delete_column(block, name, value, rows));
875
4
    }
876
2
    return Status::OK();
877
2
}
878
879
template <typename BaseReader>
880
9
Status IcebergReaderMixin<BaseReader>::_shrink_block_if_need(Block* block) {
881
9
    std::set<size_t> positions_to_erase;
882
9
    for (const std::string& expand_col : _expand_col_names) {
883
4
        if (!this->col_name_to_block_idx_ref()->contains(expand_col)) {
884
0
            return Status::InternalError("Wrong erase column '{}', block: {}", expand_col,
885
0
                                         block->dump_names());
886
0
        }
887
4
        positions_to_erase.emplace((*this->col_name_to_block_idx_ref())[expand_col]);
888
4
    }
889
9
    block->erase(positions_to_erase);
890
9
    for (const std::string& expand_col : _expand_col_names) {
891
4
        this->col_name_to_block_idx_ref()->erase(expand_col);
892
4
    }
893
9
    return Status::OK();
894
9
}
_ZN5doris18IcebergReaderMixinINS_13ParquetReaderEE21_shrink_block_if_needEPNS_5BlockE
Line
Count
Source
880
5
Status IcebergReaderMixin<BaseReader>::_shrink_block_if_need(Block* block) {
881
5
    std::set<size_t> positions_to_erase;
882
5
    for (const std::string& expand_col : _expand_col_names) {
883
2
        if (!this->col_name_to_block_idx_ref()->contains(expand_col)) {
884
0
            return Status::InternalError("Wrong erase column '{}', block: {}", expand_col,
885
0
                                         block->dump_names());
886
0
        }
887
2
        positions_to_erase.emplace((*this->col_name_to_block_idx_ref())[expand_col]);
888
2
    }
889
5
    block->erase(positions_to_erase);
890
5
    for (const std::string& expand_col : _expand_col_names) {
891
2
        this->col_name_to_block_idx_ref()->erase(expand_col);
892
2
    }
893
5
    return Status::OK();
894
5
}
_ZN5doris18IcebergReaderMixinINS_9OrcReaderEE21_shrink_block_if_needEPNS_5BlockE
Line
Count
Source
880
4
Status IcebergReaderMixin<BaseReader>::_shrink_block_if_need(Block* block) {
881
4
    std::set<size_t> positions_to_erase;
882
4
    for (const std::string& expand_col : _expand_col_names) {
883
2
        if (!this->col_name_to_block_idx_ref()->contains(expand_col)) {
884
0
            return Status::InternalError("Wrong erase column '{}', block: {}", expand_col,
885
0
                                         block->dump_names());
886
0
        }
887
2
        positions_to_erase.emplace((*this->col_name_to_block_idx_ref())[expand_col]);
888
2
    }
889
4
    block->erase(positions_to_erase);
890
4
    for (const std::string& expand_col : _expand_col_names) {
891
2
        this->col_name_to_block_idx_ref()->erase(expand_col);
892
2
    }
893
4
    return Status::OK();
894
4
}
895
896
template <typename BaseReader>
897
Status IcebergReaderMixin<BaseReader>::_position_delete_base(
898
1
        const std::string data_file_path, const std::vector<TIcebergDeleteFileDesc>& delete_files) {
899
1
    std::vector<DeleteRows*> delete_rows_array;
900
1
    int64_t num_delete_rows = 0;
901
1
    for (const auto& delete_file : delete_files) {
902
1
        SCOPED_TIMER(_iceberg_profile.delete_files_read_time);
903
1
        Status create_status = Status::OK();
904
1
        auto* delete_file_cache = _kv_cache->template get<DeleteFile>(
905
1
                _delet_file_cache_key(delete_file.path), [&]() -> DeleteFile* {
906
1
                    auto position_delete = std::make_unique<DeleteFile>();
907
1
                    TFileRangeDesc delete_file_range;
908
1
                    delete_file_range.__set_fs_name(this->get_scan_range().fs_name);
909
1
                    delete_file_range.path = delete_file.path;
910
1
                    delete_file_range.start_offset = 0;
911
1
                    delete_file_range.size = -1;
912
1
                    delete_file_range.file_size = -1;
913
1
                    create_status =
914
1
                            _read_position_delete_file(&delete_file_range, position_delete.get());
915
1
                    if (!create_status) {
916
1
                        return nullptr;
917
1
                    }
918
0
                    return position_delete.release();
919
1
                });
_ZZN5doris18IcebergReaderMixinINS_13ParquetReaderEE21_position_delete_baseENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt6vectorINS_22TIcebergDeleteFileDescESaISA_EEENKUlvE_clB5cxx11Ev
Line
Count
Source
905
1
                _delet_file_cache_key(delete_file.path), [&]() -> DeleteFile* {
906
1
                    auto position_delete = std::make_unique<DeleteFile>();
907
1
                    TFileRangeDesc delete_file_range;
908
1
                    delete_file_range.__set_fs_name(this->get_scan_range().fs_name);
909
1
                    delete_file_range.path = delete_file.path;
910
1
                    delete_file_range.start_offset = 0;
911
1
                    delete_file_range.size = -1;
912
1
                    delete_file_range.file_size = -1;
913
1
                    create_status =
914
1
                            _read_position_delete_file(&delete_file_range, position_delete.get());
915
1
                    if (!create_status) {
916
1
                        return nullptr;
917
1
                    }
918
0
                    return position_delete.release();
919
1
                });
Unexecuted instantiation: _ZZN5doris18IcebergReaderMixinINS_9OrcReaderEE21_position_delete_baseENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt6vectorINS_22TIcebergDeleteFileDescESaISA_EEENKUlvE_clB5cxx11Ev
920
1
        if (create_status.is<ErrorCode::END_OF_FILE>()) {
921
0
            continue;
922
1
        } else if (!create_status.ok()) {
923
1
            return create_status;
924
1
        }
925
926
0
        DeleteFile& delete_file_map = *((DeleteFile*)delete_file_cache);
927
0
        auto get_value = [&](const auto& v) {
928
0
            DeleteRows* row_ids = v.second.get();
929
0
            if (!row_ids->empty()) {
930
0
                delete_rows_array.emplace_back(row_ids);
931
0
                num_delete_rows += row_ids->size();
932
0
            }
933
0
        };
Unexecuted instantiation: _ZZN5doris18IcebergReaderMixinINS_13ParquetReaderEE21_position_delete_baseENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt6vectorINS_22TIcebergDeleteFileDescESaISA_EEENKUlRKT_E_clISt4pairIKS8_St10unique_ptrIS9_IlSaIlEESt14default_deleteISO_EEEEEDaSH_
Unexecuted instantiation: _ZZN5doris18IcebergReaderMixinINS_9OrcReaderEE21_position_delete_baseENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt6vectorINS_22TIcebergDeleteFileDescESaISA_EEENKUlRKT_E_clISt4pairIKS8_St10unique_ptrIS9_IlSaIlEESt14default_deleteISO_EEEEEDaSH_
934
0
        delete_file_map.if_contains(data_file_path, get_value);
935
0
    }
936
0
    if (num_delete_rows > 0) {
937
0
        SCOPED_TIMER(_iceberg_profile.delete_rows_sort_time);
938
0
        _iceberg_delete_rows =
939
0
                _kv_cache->template get<DeleteRows>(data_file_path, [&]() -> DeleteRows* {
940
0
                    auto data_file_position_delete = std::make_unique<DeleteRows>();
941
0
                    _sort_delete_rows(delete_rows_array, num_delete_rows,
942
0
                                      *data_file_position_delete);
943
0
                    return data_file_position_delete.release();
944
0
                });
Unexecuted instantiation: _ZZN5doris18IcebergReaderMixinINS_13ParquetReaderEE21_position_delete_baseENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt6vectorINS_22TIcebergDeleteFileDescESaISA_EEENKUlvE0_clEv
Unexecuted instantiation: _ZZN5doris18IcebergReaderMixinINS_9OrcReaderEE21_position_delete_baseENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt6vectorINS_22TIcebergDeleteFileDescESaISA_EEENKUlvE0_clEv
945
0
        set_delete_rows();
946
0
        COUNTER_UPDATE(_iceberg_profile.num_delete_rows, num_delete_rows);
947
0
    }
948
0
    return Status::OK();
949
1
}
_ZN5doris18IcebergReaderMixinINS_13ParquetReaderEE21_position_delete_baseENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt6vectorINS_22TIcebergDeleteFileDescESaISA_EE
Line
Count
Source
898
1
        const std::string data_file_path, const std::vector<TIcebergDeleteFileDesc>& delete_files) {
899
1
    std::vector<DeleteRows*> delete_rows_array;
900
1
    int64_t num_delete_rows = 0;
901
1
    for (const auto& delete_file : delete_files) {
902
1
        SCOPED_TIMER(_iceberg_profile.delete_files_read_time);
903
1
        Status create_status = Status::OK();
904
1
        auto* delete_file_cache = _kv_cache->template get<DeleteFile>(
905
1
                _delet_file_cache_key(delete_file.path), [&]() -> DeleteFile* {
906
1
                    auto position_delete = std::make_unique<DeleteFile>();
907
1
                    TFileRangeDesc delete_file_range;
908
1
                    delete_file_range.__set_fs_name(this->get_scan_range().fs_name);
909
1
                    delete_file_range.path = delete_file.path;
910
1
                    delete_file_range.start_offset = 0;
911
1
                    delete_file_range.size = -1;
912
1
                    delete_file_range.file_size = -1;
913
1
                    create_status =
914
1
                            _read_position_delete_file(&delete_file_range, position_delete.get());
915
1
                    if (!create_status) {
916
1
                        return nullptr;
917
1
                    }
918
1
                    return position_delete.release();
919
1
                });
920
1
        if (create_status.is<ErrorCode::END_OF_FILE>()) {
921
0
            continue;
922
1
        } else if (!create_status.ok()) {
923
1
            return create_status;
924
1
        }
925
926
0
        DeleteFile& delete_file_map = *((DeleteFile*)delete_file_cache);
927
0
        auto get_value = [&](const auto& v) {
928
0
            DeleteRows* row_ids = v.second.get();
929
0
            if (!row_ids->empty()) {
930
0
                delete_rows_array.emplace_back(row_ids);
931
0
                num_delete_rows += row_ids->size();
932
0
            }
933
0
        };
934
0
        delete_file_map.if_contains(data_file_path, get_value);
935
0
    }
936
0
    if (num_delete_rows > 0) {
937
0
        SCOPED_TIMER(_iceberg_profile.delete_rows_sort_time);
938
0
        _iceberg_delete_rows =
939
0
                _kv_cache->template get<DeleteRows>(data_file_path, [&]() -> DeleteRows* {
940
0
                    auto data_file_position_delete = std::make_unique<DeleteRows>();
941
0
                    _sort_delete_rows(delete_rows_array, num_delete_rows,
942
0
                                      *data_file_position_delete);
943
0
                    return data_file_position_delete.release();
944
0
                });
945
0
        set_delete_rows();
946
0
        COUNTER_UPDATE(_iceberg_profile.num_delete_rows, num_delete_rows);
947
0
    }
948
0
    return Status::OK();
949
1
}
Unexecuted instantiation: _ZN5doris18IcebergReaderMixinINS_9OrcReaderEE21_position_delete_baseENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt6vectorINS_22TIcebergDeleteFileDescESaISA_EE
950
951
template <typename BaseReader>
952
typename IcebergReaderMixin<BaseReader>::PositionDeleteRange
953
0
IcebergReaderMixin<BaseReader>::_get_range(const ColumnDictI32& file_path_column) {
954
0
    PositionDeleteRange range;
955
0
    size_t read_rows = file_path_column.get_data().size();
956
0
    const int* code_path = file_path_column.get_data().data();
957
0
    const int* code_path_start = code_path;
958
0
    const int* code_path_end = code_path + read_rows;
959
0
    while (code_path < code_path_end) {
960
0
        int code = code_path[0];
961
0
        const int* code_end = std::upper_bound(code_path, code_path_end, code);
962
0
        range.data_file_path.emplace_back(file_path_column.get_value(code).to_string());
963
0
        range.range.emplace_back(code_path - code_path_start, code_end - code_path_start);
964
0
        code_path = code_end;
965
0
    }
966
0
    return range;
967
0
}
Unexecuted instantiation: _ZN5doris18IcebergReaderMixinINS_13ParquetReaderEE10_get_rangeERKNS_13ColumnDictI32E
Unexecuted instantiation: _ZN5doris18IcebergReaderMixinINS_9OrcReaderEE10_get_rangeERKNS_13ColumnDictI32E
968
969
template <typename BaseReader>
970
typename IcebergReaderMixin<BaseReader>::PositionDeleteRange
971
0
IcebergReaderMixin<BaseReader>::_get_range(const ColumnString& file_path_column) {
972
0
    PositionDeleteRange range;
973
0
    size_t read_rows = file_path_column.size();
974
0
    size_t index = 0;
975
0
    while (index < read_rows) {
976
0
        StringRef data_path = file_path_column.get_data_at(index);
977
0
        size_t left = index - 1;
978
0
        size_t right = read_rows;
979
0
        while (left + 1 != right) {
980
0
            size_t mid = left + (right - left) / 2;
981
0
            if (file_path_column.get_data_at(mid) > data_path) {
982
0
                right = mid;
983
0
            } else {
984
0
                left = mid;
985
0
            }
986
0
        }
987
0
        range.data_file_path.emplace_back(data_path.to_string());
988
0
        range.range.emplace_back(index, left + 1);
989
0
        index = left + 1;
990
0
    }
991
0
    return range;
992
0
}
Unexecuted instantiation: _ZN5doris18IcebergReaderMixinINS_13ParquetReaderEE10_get_rangeERKNS_9ColumnStrIjEE
Unexecuted instantiation: _ZN5doris18IcebergReaderMixinINS_9OrcReaderEE10_get_rangeERKNS_9ColumnStrIjEE
993
994
template <typename BaseReader>
995
void IcebergReaderMixin<BaseReader>::_sort_delete_rows(
996
        const std::vector<std::vector<int64_t>*>& delete_rows_array, int64_t num_delete_rows,
997
0
        std::vector<int64_t>& result) {
998
0
    if (delete_rows_array.empty()) {
999
0
        return;
1000
0
    }
1001
0
    if (delete_rows_array.size() == 1) {
1002
0
        result.resize(num_delete_rows);
1003
0
        memcpy(result.data(), delete_rows_array.front()->data(), sizeof(int64_t) * num_delete_rows);
1004
0
        return;
1005
0
    }
1006
0
    if (delete_rows_array.size() == 2) {
1007
0
        result.resize(num_delete_rows);
1008
0
        std::merge(delete_rows_array.front()->begin(), delete_rows_array.front()->end(),
1009
0
                   delete_rows_array.back()->begin(), delete_rows_array.back()->end(),
1010
0
                   result.begin());
1011
0
        return;
1012
0
    }
1013
1014
0
    using vec_pair = std::pair<std::vector<int64_t>::iterator, std::vector<int64_t>::iterator>;
1015
0
    result.resize(num_delete_rows);
1016
0
    auto row_id_iter = result.begin();
1017
0
    auto iter_end = result.end();
1018
0
    std::vector<vec_pair> rows_array;
1019
0
    for (auto* rows : delete_rows_array) {
1020
0
        if (!rows->empty()) {
1021
0
            rows_array.emplace_back(rows->begin(), rows->end());
1022
0
        }
1023
0
    }
1024
0
    size_t array_size = rows_array.size();
1025
0
    while (row_id_iter != iter_end) {
1026
0
        int64_t min_index = 0;
1027
0
        int64_t min = *rows_array[0].first;
1028
0
        for (size_t i = 0; i < array_size; ++i) {
1029
0
            if (*rows_array[i].first < min) {
1030
0
                min_index = i;
1031
0
                min = *rows_array[i].first;
1032
0
            }
1033
0
        }
1034
0
        *row_id_iter++ = min;
1035
0
        rows_array[min_index].first++;
1036
0
        if (UNLIKELY(rows_array[min_index].first == rows_array[min_index].second)) {
1037
0
            rows_array.erase(rows_array.begin() + min_index);
1038
0
            array_size--;
1039
0
        }
1040
0
    }
1041
0
}
Unexecuted instantiation: _ZN5doris18IcebergReaderMixinINS_13ParquetReaderEE17_sort_delete_rowsERKSt6vectorIPS3_IlSaIlEESaIS6_EElRS5_
Unexecuted instantiation: _ZN5doris18IcebergReaderMixinINS_9OrcReaderEE17_sort_delete_rowsERKSt6vectorIPS3_IlSaIlEESaIS6_EElRS5_
1042
1043
template <typename BaseReader>
1044
Status IcebergReaderMixin<BaseReader>::_gen_position_delete_file_range(
1045
        Block& block, DeleteFile* position_delete, size_t read_rows,
1046
0
        bool file_path_column_dictionary_coded) {
1047
0
    SCOPED_TIMER(_iceberg_profile.parse_delete_file_time);
1048
0
    auto name_to_pos_map = block.get_name_to_pos_map();
1049
0
    ColumnPtr path_column = block.get_by_position(name_to_pos_map[ICEBERG_FILE_PATH]).column;
1050
0
    DCHECK_EQ(path_column->size(), read_rows);
1051
0
    ColumnPtr pos_column = block.get_by_position(name_to_pos_map[ICEBERG_ROW_POS]).column;
1052
0
    if (const auto* nullable_col = check_and_get_column<ColumnNullable>(*path_column);
1053
0
        nullable_col != nullptr) {
1054
0
        if (nullable_col->has_null(0, read_rows)) {
1055
0
            return Status::Corruption(
1056
0
                    "Iceberg position delete column file_path contains null values");
1057
0
        }
1058
0
        path_column = remove_nullable(path_column);
1059
0
    }
1060
0
    if (const auto* nullable_col = check_and_get_column<ColumnNullable>(*pos_column);
1061
0
        nullable_col != nullptr) {
1062
0
        if (nullable_col->has_null(0, read_rows)) {
1063
0
            return Status::Corruption("Iceberg position delete column pos contains null values");
1064
0
        }
1065
0
        pos_column = remove_nullable(pos_column);
1066
0
    }
1067
0
    using ColumnType = typename PrimitiveTypeTraits<TYPE_BIGINT>::ColumnType;
1068
0
    const int64_t* src_data = assert_cast<const ColumnType&>(*pos_column).get_data().data();
1069
0
    PositionDeleteRange range;
1070
0
    if (file_path_column_dictionary_coded) {
1071
0
        range = _get_range(assert_cast<const ColumnDictI32&>(*path_column));
1072
0
    } else {
1073
0
        range = _get_range(assert_cast<const ColumnString&>(*path_column));
1074
0
    }
1075
0
    for (int i = 0; i < range.range.size(); ++i) {
1076
0
        std::string key = range.data_file_path[i];
1077
0
        auto iter = position_delete->find(key);
1078
0
        DeleteRows* delete_rows;
1079
0
        if (iter == position_delete->end()) {
1080
0
            delete_rows = new DeleteRows;
1081
0
            std::unique_ptr<DeleteRows> delete_rows_ptr(delete_rows);
1082
0
            (*position_delete)[key] = std::move(delete_rows_ptr);
1083
0
        } else {
1084
0
            delete_rows = iter->second.get();
1085
0
        }
1086
0
        const int64_t* cpy_start = src_data + range.range[i].first;
1087
0
        const int64_t cpy_count = range.range[i].second - range.range[i].first;
1088
0
        int64_t origin_size = delete_rows->size();
1089
0
        delete_rows->resize(origin_size + cpy_count);
1090
0
        int64_t* dest_position = &(*delete_rows)[origin_size];
1091
0
        memcpy(dest_position, cpy_start, cpy_count * sizeof(int64_t));
1092
0
    }
1093
0
    return Status::OK();
1094
0
}
Unexecuted instantiation: _ZN5doris18IcebergReaderMixinINS_13ParquetReaderEE31_gen_position_delete_file_rangeERNS_5BlockEPN5phmap22parallel_flat_hash_mapINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESt10unique_ptrISt6vectorIlSaIlEESt14default_deleteISG_EESt4hashISC_ESt8equal_toIvESaISt4pairIKSC_SJ_EELm8ESt5mutexEEmb
Unexecuted instantiation: _ZN5doris18IcebergReaderMixinINS_9OrcReaderEE31_gen_position_delete_file_rangeERNS_5BlockEPN5phmap22parallel_flat_hash_mapINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESt10unique_ptrISt6vectorIlSaIlEESt14default_deleteISG_EESt4hashISC_ESt8equal_toIvESaISt4pairIKSC_SJ_EELm8ESt5mutexEEmb
1095
1096
template <typename BaseReader>
1097
Status IcebergReaderMixin<BaseReader>::read_deletion_vector(
1098
1
        const std::string& data_file_path, const TIcebergDeleteFileDesc& delete_file_desc) {
1099
1
    size_t bytes_read = 0;
1100
1
    RETURN_IF_ERROR(validate_iceberg_deletion_vector_descriptor(delete_file_desc, bytes_read));
1101
1102
1
    Status create_status = Status::OK();
1103
1
    SCOPED_TIMER(_iceberg_profile.delete_files_read_time);
1104
1
    bool decoded_cache_hit = false;
1105
1
    _iceberg_deletion_vector = _kv_cache->template get<DeletionVector>(
1106
1
            build_iceberg_deletion_vector_cache_key(data_file_path, delete_file_desc),
1107
1
            [&]() -> DeletionVector* {
1108
1
                auto deletion_vector = std::make_unique<DeletionVector>();
1109
1110
1
                io::FileCacheStatistics file_cache_stats;
1111
1
                IcebergDeleteFileReaderOptions options;
1112
1
                options.state = this->get_state();
1113
1
                options.profile = this->get_profile();
1114
1
                options.scan_params = &this->get_scan_params();
1115
1
                options.io_ctx = this->get_io_ctx();
1116
1
                options.fs_name = &this->get_scan_range().fs_name;
1117
1
                options.deletion_vector_file_cache_stats = &file_cache_stats;
1118
1
                create_status = read_iceberg_deletion_vector(delete_file_desc, options,
1119
1
                                                             deletion_vector.get());
1120
1
                COUNTER_UPDATE(_iceberg_profile.file_cache_hit_count,
1121
1
                               file_cache_stats.num_local_io_total);
1122
1
                COUNTER_UPDATE(_iceberg_profile.file_cache_miss_count,
1123
1
                               file_cache_stats.num_remote_io_total);
1124
1
                COUNTER_UPDATE(_iceberg_profile.file_cache_peer_read_count,
1125
1
                               file_cache_stats.num_peer_io_total);
1126
1
                if (!create_status.ok()) [[unlikely]] {
1127
1
                    return nullptr;
1128
1
                }
1129
1130
0
                SCOPED_TIMER(_iceberg_profile.parse_delete_file_time);
1131
0
                COUNTER_UPDATE(_iceberg_profile.num_delete_rows, deletion_vector->cardinality());
1132
0
                return deletion_vector.release();
1133
1
            },
_ZZN5doris18IcebergReaderMixinINS_13ParquetReaderEE20read_deletion_vectorERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKNS_22TIcebergDeleteFileDescEENKUlvE_clEv
Line
Count
Source
1107
1
            [&]() -> DeletionVector* {
1108
1
                auto deletion_vector = std::make_unique<DeletionVector>();
1109
1110
1
                io::FileCacheStatistics file_cache_stats;
1111
1
                IcebergDeleteFileReaderOptions options;
1112
1
                options.state = this->get_state();
1113
1
                options.profile = this->get_profile();
1114
1
                options.scan_params = &this->get_scan_params();
1115
1
                options.io_ctx = this->get_io_ctx();
1116
1
                options.fs_name = &this->get_scan_range().fs_name;
1117
1
                options.deletion_vector_file_cache_stats = &file_cache_stats;
1118
1
                create_status = read_iceberg_deletion_vector(delete_file_desc, options,
1119
1
                                                             deletion_vector.get());
1120
1
                COUNTER_UPDATE(_iceberg_profile.file_cache_hit_count,
1121
1
                               file_cache_stats.num_local_io_total);
1122
1
                COUNTER_UPDATE(_iceberg_profile.file_cache_miss_count,
1123
1
                               file_cache_stats.num_remote_io_total);
1124
1
                COUNTER_UPDATE(_iceberg_profile.file_cache_peer_read_count,
1125
1
                               file_cache_stats.num_peer_io_total);
1126
1
                if (!create_status.ok()) [[unlikely]] {
1127
1
                    return nullptr;
1128
1
                }
1129
1130
0
                SCOPED_TIMER(_iceberg_profile.parse_delete_file_time);
1131
0
                COUNTER_UPDATE(_iceberg_profile.num_delete_rows, deletion_vector->cardinality());
1132
0
                return deletion_vector.release();
1133
1
            },
Unexecuted instantiation: _ZZN5doris18IcebergReaderMixinINS_9OrcReaderEE20read_deletion_vectorERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKNS_22TIcebergDeleteFileDescEENKUlvE_clEv
1134
1
            &decoded_cache_hit);
1135
1136
1
    RETURN_IF_ERROR(create_status);
1137
0
    COUNTER_UPDATE(decoded_cache_hit ? _iceberg_profile.decoded_cache_hit_count
1138
0
                                     : _iceberg_profile.decoded_cache_miss_count,
1139
0
                   1);
1140
0
    if (!_iceberg_deletion_vector->isEmpty()) [[likely]] {
1141
0
        set_deletion_vector();
1142
0
    }
1143
0
    return Status::OK();
1144
1
}
_ZN5doris18IcebergReaderMixinINS_13ParquetReaderEE20read_deletion_vectorERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKNS_22TIcebergDeleteFileDescE
Line
Count
Source
1098
1
        const std::string& data_file_path, const TIcebergDeleteFileDesc& delete_file_desc) {
1099
1
    size_t bytes_read = 0;
1100
1
    RETURN_IF_ERROR(validate_iceberg_deletion_vector_descriptor(delete_file_desc, bytes_read));
1101
1102
1
    Status create_status = Status::OK();
1103
1
    SCOPED_TIMER(_iceberg_profile.delete_files_read_time);
1104
1
    bool decoded_cache_hit = false;
1105
1
    _iceberg_deletion_vector = _kv_cache->template get<DeletionVector>(
1106
1
            build_iceberg_deletion_vector_cache_key(data_file_path, delete_file_desc),
1107
1
            [&]() -> DeletionVector* {
1108
1
                auto deletion_vector = std::make_unique<DeletionVector>();
1109
1110
1
                io::FileCacheStatistics file_cache_stats;
1111
1
                IcebergDeleteFileReaderOptions options;
1112
1
                options.state = this->get_state();
1113
1
                options.profile = this->get_profile();
1114
1
                options.scan_params = &this->get_scan_params();
1115
1
                options.io_ctx = this->get_io_ctx();
1116
1
                options.fs_name = &this->get_scan_range().fs_name;
1117
1
                options.deletion_vector_file_cache_stats = &file_cache_stats;
1118
1
                create_status = read_iceberg_deletion_vector(delete_file_desc, options,
1119
1
                                                             deletion_vector.get());
1120
1
                COUNTER_UPDATE(_iceberg_profile.file_cache_hit_count,
1121
1
                               file_cache_stats.num_local_io_total);
1122
1
                COUNTER_UPDATE(_iceberg_profile.file_cache_miss_count,
1123
1
                               file_cache_stats.num_remote_io_total);
1124
1
                COUNTER_UPDATE(_iceberg_profile.file_cache_peer_read_count,
1125
1
                               file_cache_stats.num_peer_io_total);
1126
1
                if (!create_status.ok()) [[unlikely]] {
1127
1
                    return nullptr;
1128
1
                }
1129
1130
1
                SCOPED_TIMER(_iceberg_profile.parse_delete_file_time);
1131
1
                COUNTER_UPDATE(_iceberg_profile.num_delete_rows, deletion_vector->cardinality());
1132
1
                return deletion_vector.release();
1133
1
            },
1134
1
            &decoded_cache_hit);
1135
1136
1
    RETURN_IF_ERROR(create_status);
1137
0
    COUNTER_UPDATE(decoded_cache_hit ? _iceberg_profile.decoded_cache_hit_count
1138
0
                                     : _iceberg_profile.decoded_cache_miss_count,
1139
0
                   1);
1140
0
    if (!_iceberg_deletion_vector->isEmpty()) [[likely]] {
1141
0
        set_deletion_vector();
1142
0
    }
1143
0
    return Status::OK();
1144
1
}
Unexecuted instantiation: _ZN5doris18IcebergReaderMixinINS_9OrcReaderEE20read_deletion_vectorERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKNS_22TIcebergDeleteFileDescE
1145
1146
} // namespace doris