Coverage Report

Created: 2026-07-20 18:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/storage/segment/column_reader.cpp
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
#include "storage/segment/column_reader.h"
19
20
#include <assert.h>
21
#include <gen_cpp/Descriptors_constants.h>
22
#include <gen_cpp/Descriptors_types.h>
23
#include <gen_cpp/segment_v2.pb.h>
24
#include <glog/logging.h>
25
26
#include <algorithm>
27
#include <memory>
28
#include <ostream>
29
#include <set>
30
#include <string>
31
#include <utility>
32
33
#include "common/compiler_util.h" // IWYU pragma: keep
34
#include "common/status.h"
35
#include "core/assert_cast.h"
36
#include "core/binary_cast.hpp"
37
#include "core/column/column.h"
38
#include "core/column/column_array.h"
39
#include "core/column/column_map.h"
40
#include "core/column/column_nullable.h"
41
#include "core/column/column_struct.h"
42
#include "core/column/column_vector.h"
43
#include "core/data_type/data_type_agg_state.h"
44
#include "core/data_type/data_type_factory.hpp"
45
#include "core/data_type/data_type_nullable.h"
46
#include "core/data_type/define_primitive_type.h"
47
#include "core/decimal12.h"
48
#include "core/string_ref.h"
49
#include "core/types.h"
50
#include "core/value/decimalv2_value.h"
51
#include "core/value/vdatetime_value.h" //for VecDateTime
52
#include "io/fs/file_reader.h"
53
#include "storage/index/ann/ann_index_reader.h"
54
#include "storage/index/bloom_filter/bloom_filter.h"
55
#include "storage/index/bloom_filter/bloom_filter_index_reader.h"
56
#include "storage/index/index_file_reader.h"
57
#include "storage/index/index_reader.h"
58
#include "storage/index/inverted/analyzer/analyzer.h"
59
#include "storage/index/inverted/inverted_index_reader.h"
60
#include "storage/index/zone_map/zone_map_index.h"
61
#include "storage/iterators.h"
62
#include "storage/olap_common.h"
63
#include "storage/predicate/block_column_predicate.h"
64
#include "storage/predicate/column_predicate.h"
65
#include "storage/segment/binary_dict_page.h" // for BinaryDictPageDecoder
66
#include "storage/segment/binary_plain_page.h"
67
#include "storage/segment/column_meta_accessor.h"
68
#include "storage/segment/encoding_info.h" // for EncodingInfo
69
#include "storage/segment/page_decoder.h"
70
#include "storage/segment/page_handle.h" // for PageHandle
71
#include "storage/segment/page_io.h"
72
#include "storage/segment/page_pointer.h" // for PagePointer
73
#include "storage/segment/row_ranges.h"
74
#include "storage/segment/segment.h"
75
#include "storage/segment/segment_prefetcher.h"
76
#include "storage/segment/variant/variant_column_reader.h"
77
#include "storage/tablet/tablet_schema.h"
78
#include "storage/types.h" // for TypeInfo
79
#include "util/bitmap.h"
80
#include "util/block_compression.h"
81
#include "util/concurrency_stats.h"
82
#include "util/defer_op.h"
83
#include "util/rle_encoding.h" // for RleDecoder
84
#include "util/slice.h"
85
86
namespace doris::segment_v2 {
87
#include "storage/segment/column_reader.h"
88
89
0
inline bool read_as_string(PrimitiveType type) {
90
0
    return type == PrimitiveType::TYPE_STRING || type == PrimitiveType::INVALID_TYPE ||
91
0
           type == PrimitiveType::TYPE_BITMAP || type == PrimitiveType::TYPE_FIXED_LENGTH_OBJECT;
92
0
}
93
94
121
bool is_meta_access_path_component(const std::string& component) {
95
121
    return StringCaseEqual()(component, ColumnIterator::ACCESS_OFFSET) ||
96
121
           StringCaseEqual()(component, ColumnIterator::ACCESS_NULL);
97
121
}
98
99
276
bool uses_legacy_access_path_encoding(const TColumnAccessPath& path) {
100
276
    return !path.__isset.version ||
101
276
           path.version == g_Descriptors_constants.TCOLUMN_ACCESS_PATH_VERSION_LEGACY;
102
276
}
103
104
namespace {
105
106
// Nested access paths are processed one container level at a time:
107
//
108
// 1. Each Map/Array/Struct iterator's set_access_paths() calls _prepare_nested_access_paths(). For
109
//    the all-path and predicate-path channels independently, _split_access_paths() validates the
110
//    wire encoding and type-selected payload, removes the current iterator name, and separates
111
//    requests consumed by the current iterator from paths that still address a data descendant. A
112
//    current DATA request requires all data children. Struct owns current-level NULL metadata;
113
//    Map and Array own NULL and OFFSET metadata. A supported metadata-only request can stop before
114
//    descendant routing and mark every data child SKIP.
115
// 2. This router interprets only the first remaining component and routes the path according to
116
//    the container topology:
117
//    - Struct components already name fields. Select the paths for each field without rewriting.
118
//    - Array `*` names its only item. Retarget `*` to the item iterator name.
119
//    - Map `KEYS` and `VALUES` directly name its logical children. Map `*` creates a complete DATA
120
//      path for `KEYS` and routes any trailing components through `VALUES`.
121
// 3. The container forwards the routed all-path and predicate-path channels to each selected
122
//    child's set_access_paths(), then finalizes that child's PREDICATE/LAZY_OUTPUT/SKIP
123
//    requirement. The child repeats the same flow, which handles arbitrary Map/Array/Struct
124
//    nesting.
125
//
126
// At this point versions have been validated, legacy paths have DATA type, and every payload
127
// selected by type is non-empty. Routing never interprets or rewrites an unselected compatibility
128
// payload.
129
class DescendantAccessPathRouter final {
130
public:
131
    DescendantAccessPathRouter() = delete;
132
133
    // Preserve the two set_access_paths() input channels. all_paths originates from the
134
    // all-access-path superset, while predicate_paths separately records predicate-phase paths.
135
    // Routing may omit all_paths when the parent already requires complete child data.
136
    struct ChildAccessPaths {
137
        TColumnAccessPaths all_paths;
138
        TColumnAccessPaths predicate_paths;
139
140
123
        bool empty() const { return all_paths.empty() && predicate_paths.empty(); }
141
    };
142
143
    struct MapChildAccessPaths {
144
        ChildAccessPaths key;
145
        ChildAccessPaths value;
146
    };
147
148
    // Map children use the logical KEYS/VALUES selectors as their access-path names, independent
149
    // of physical child column names. Expand a wildcard to complete keys and route its trailing
150
    // qualifiers to values.
151
    static Result<MapChildAccessPaths> route_map_paths_to_children(
152
28
            TColumnAccessPaths all_paths, TColumnAccessPaths predicate_paths) {
153
28
        MapChildAccessPaths child_paths;
154
28
        auto status = distribute_map_paths(std::move(all_paths), child_paths.key.all_paths,
155
28
                                           child_paths.value.all_paths);
156
28
        if (!status.ok()) {
157
0
            return ResultError(std::move(status));
158
0
        }
159
28
        status = distribute_map_paths(std::move(predicate_paths), child_paths.key.predicate_paths,
160
28
                                      child_paths.value.predicate_paths);
161
28
        if (!status.ok()) {
162
0
            return ResultError(std::move(status));
163
0
        }
164
28
        return child_paths;
165
28
    }
166
167
    // Array has one data child. Retarget its logical wildcard selector to the item iterator name.
168
    static Result<ChildAccessPaths> route_array_paths_to_item(TColumnAccessPaths all_paths,
169
                                                              TColumnAccessPaths predicate_paths,
170
22
                                                              const std::string& item_name) {
171
22
        ChildAccessPaths child_paths {.all_paths = std::move(all_paths),
172
22
                                      .predicate_paths = std::move(predicate_paths)};
173
44
        auto retarget_wildcard_paths_to_item = [&](TColumnAccessPaths& paths) -> Status {
174
44
            for (auto& path : paths) {
175
30
                const bool is_wildcard = DORIS_TRY(selected_payload_head_matches(
176
30
                        path, ColumnIterator::ACCESS_ALL, PathHeadMatchMode::EXACT));
177
30
                if (is_wildcard) {
178
29
                    RETURN_IF_ERROR(replace_selected_payload_head(path, item_name));
179
29
                }
180
30
            }
181
44
            return Status::OK();
182
44
        };
183
184
22
        auto status = retarget_wildcard_paths_to_item(child_paths.all_paths);
185
22
        if (!status.ok()) {
186
0
            return ResultError(std::move(status));
187
0
        }
188
22
        status = retarget_wildcard_paths_to_item(child_paths.predicate_paths);
189
22
        if (!status.ok()) {
190
0
            return ResultError(std::move(status));
191
0
        }
192
22
        return child_paths;
193
22
    }
194
195
    // Struct selectors already use child field names. Select the paths for one child without
196
    // rewriting them, so that the child can validate and strip its own name.
197
    static Result<ChildAccessPaths> select_struct_paths_for_child(
198
            const TColumnAccessPaths& all_paths, const TColumnAccessPaths& predicate_paths,
199
81
            const std::string& child_name, bool include_all_paths) {
200
81
        ChildAccessPaths child_paths;
201
81
        auto select_matching_paths = [&](const TColumnAccessPaths& source_paths,
202
148
                                         TColumnAccessPaths& child_paths) -> Status {
203
148
            for (const auto& path : source_paths) {
204
109
                const bool matches_child = DORIS_TRY(selected_payload_head_matches(
205
109
                        path, child_name, PathHeadMatchMode::CASE_INSENSITIVE));
206
109
                if (matches_child) {
207
57
                    child_paths.emplace_back(path);
208
57
                }
209
109
            }
210
148
            return Status::OK();
211
148
        };
212
213
81
        if (include_all_paths) {
214
67
            auto status = select_matching_paths(all_paths, child_paths.all_paths);
215
67
            if (!status.ok()) {
216
0
                return ResultError(std::move(status));
217
0
            }
218
67
        }
219
81
        auto status = select_matching_paths(predicate_paths, child_paths.predicate_paths);
220
81
        if (!status.ok()) {
221
0
            return ResultError(std::move(status));
222
0
        }
223
81
        return child_paths;
224
81
    }
225
226
private:
227
    enum class PathHeadMatchMode { EXACT, CASE_INSENSITIVE };
228
    enum class MapSelector { UNRECOGNIZED, WILDCARD, KEYS, VALUES };
229
230
    // TColumnAccessPath may carry both payload fields after compatibility forwarding. Selected
231
    // means data_access_path for DATA and meta_access_path for META. Visit only that payload and
232
    // leave the other payload untouched.
233
    template <typename AccessPath, typename Visitor>
234
217
    static Status visit_selected_payload(AccessPath& access_path, Visitor&& visitor) {
235
217
        switch (access_path.type) {
236
176
        case TAccessPathType::DATA:
237
176
            if (!access_path.__isset.data_access_path) {
238
0
                return Status::InternalError(
239
0
                        "Invalid DATA access path: data_access_path payload is not set");
240
0
            }
241
176
            return std::forward<Visitor>(visitor)(access_path.data_access_path);
242
41
        case TAccessPathType::META:
243
41
            if (!access_path.__isset.meta_access_path) {
244
0
                return Status::InternalError(
245
0
                        "Invalid META access path: meta_access_path payload is not set");
246
0
            }
247
41
            return std::forward<Visitor>(visitor)(access_path.meta_access_path);
248
0
        default:
249
0
            return Status::InternalError("Invalid access path type: {}",
250
0
                                         static_cast<int>(access_path.type));
251
217
        }
252
217
    }
column_reader.cpp:_ZN5doris10segment_v212_GLOBAL__N_126DescendantAccessPathRouter22visit_selected_payloadIKNS_17TColumnAccessPathEZNS2_21classify_map_selectorERS5_EUlRKT_E_EENS_6StatusERS7_OT0_
Line
Count
Source
234
40
    static Status visit_selected_payload(AccessPath& access_path, Visitor&& visitor) {
235
40
        switch (access_path.type) {
236
29
        case TAccessPathType::DATA:
237
29
            if (!access_path.__isset.data_access_path) {
238
0
                return Status::InternalError(
239
0
                        "Invalid DATA access path: data_access_path payload is not set");
240
0
            }
241
29
            return std::forward<Visitor>(visitor)(access_path.data_access_path);
242
11
        case TAccessPathType::META:
243
11
            if (!access_path.__isset.meta_access_path) {
244
0
                return Status::InternalError(
245
0
                        "Invalid META access path: meta_access_path payload is not set");
246
0
            }
247
11
            return std::forward<Visitor>(visitor)(access_path.meta_access_path);
248
0
        default:
249
0
            return Status::InternalError("Invalid access path type: {}",
250
0
                                         static_cast<int>(access_path.type));
251
40
        }
252
40
    }
column_reader.cpp:_ZN5doris10segment_v212_GLOBAL__N_126DescendantAccessPathRouter22visit_selected_payloadINS_17TColumnAccessPathEZNS2_29replace_selected_payload_headERS4_RKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEUlRT_E_EENS_6StatusESF_OT0_
Line
Count
Source
234
38
    static Status visit_selected_payload(AccessPath& access_path, Visitor&& visitor) {
235
38
        switch (access_path.type) {
236
25
        case TAccessPathType::DATA:
237
25
            if (!access_path.__isset.data_access_path) {
238
0
                return Status::InternalError(
239
0
                        "Invalid DATA access path: data_access_path payload is not set");
240
0
            }
241
25
            return std::forward<Visitor>(visitor)(access_path.data_access_path);
242
13
        case TAccessPathType::META:
243
13
            if (!access_path.__isset.meta_access_path) {
244
0
                return Status::InternalError(
245
0
                        "Invalid META access path: meta_access_path payload is not set");
246
0
            }
247
13
            return std::forward<Visitor>(visitor)(access_path.meta_access_path);
248
0
        default:
249
0
            return Status::InternalError("Invalid access path type: {}",
250
0
                                         static_cast<int>(access_path.type));
251
38
        }
252
38
    }
column_reader.cpp:_ZN5doris10segment_v212_GLOBAL__N_126DescendantAccessPathRouter22visit_selected_payloadIKNS_17TColumnAccessPathEZNS2_29selected_payload_head_matchesERS5_RKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEENS2_17PathHeadMatchModeEEUlRKT_E_EENS_6StatusERSG_OT0_
Line
Count
Source
234
139
    static Status visit_selected_payload(AccessPath& access_path, Visitor&& visitor) {
235
139
        switch (access_path.type) {
236
122
        case TAccessPathType::DATA:
237
122
            if (!access_path.__isset.data_access_path) {
238
0
                return Status::InternalError(
239
0
                        "Invalid DATA access path: data_access_path payload is not set");
240
0
            }
241
122
            return std::forward<Visitor>(visitor)(access_path.data_access_path);
242
17
        case TAccessPathType::META:
243
17
            if (!access_path.__isset.meta_access_path) {
244
0
                return Status::InternalError(
245
0
                        "Invalid META access path: meta_access_path payload is not set");
246
0
            }
247
17
            return std::forward<Visitor>(visitor)(access_path.meta_access_path);
248
0
        default:
249
0
            return Status::InternalError("Invalid access path type: {}",
250
0
                                         static_cast<int>(access_path.type));
251
139
        }
252
139
    }
253
254
    static Result<bool> selected_payload_head_matches(const TColumnAccessPath& access_path,
255
                                                      const std::string& expected_head,
256
139
                                                      PathHeadMatchMode match_mode) {
257
139
        bool matches = false;
258
139
        auto status = visit_selected_payload(access_path, [&](const auto& payload) {
259
139
            DORIS_CHECK(!payload.path.empty());
260
139
            matches = match_mode == PathHeadMatchMode::EXACT
261
139
                              ? payload.path.front() == expected_head
262
139
                              : StringCaseEqual()(payload.path.front(), expected_head);
263
139
            return Status::OK();
264
139
        });
column_reader.cpp:_ZZN5doris10segment_v212_GLOBAL__N_126DescendantAccessPathRouter29selected_payload_head_matchesERKNS_17TColumnAccessPathERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEENS2_17PathHeadMatchModeEENKUlRKT_E_clINS_15TDataAccessPathEEEDaSH_
Line
Count
Source
258
122
        auto status = visit_selected_payload(access_path, [&](const auto& payload) {
259
122
            DORIS_CHECK(!payload.path.empty());
260
122
            matches = match_mode == PathHeadMatchMode::EXACT
261
122
                              ? payload.path.front() == expected_head
262
122
                              : StringCaseEqual()(payload.path.front(), expected_head);
263
122
            return Status::OK();
264
122
        });
column_reader.cpp:_ZZN5doris10segment_v212_GLOBAL__N_126DescendantAccessPathRouter29selected_payload_head_matchesERKNS_17TColumnAccessPathERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEENS2_17PathHeadMatchModeEENKUlRKT_E_clINS_15TMetaAccessPathEEEDaSH_
Line
Count
Source
258
17
        auto status = visit_selected_payload(access_path, [&](const auto& payload) {
259
17
            DORIS_CHECK(!payload.path.empty());
260
17
            matches = match_mode == PathHeadMatchMode::EXACT
261
17
                              ? payload.path.front() == expected_head
262
17
                              : StringCaseEqual()(payload.path.front(), expected_head);
263
17
            return Status::OK();
264
17
        });
265
139
        if (!status.ok()) {
266
0
            return ResultError(std::move(status));
267
0
        }
268
139
        return matches;
269
139
    }
270
271
    static Status replace_selected_payload_head(TColumnAccessPath& access_path,
272
38
                                                const std::string& child_name) {
273
38
        return visit_selected_payload(access_path, [&](auto& payload) {
274
38
            DORIS_CHECK(!payload.path.empty());
275
38
            payload.path.front() = child_name;
276
38
            return Status::OK();
277
38
        });
column_reader.cpp:_ZZN5doris10segment_v212_GLOBAL__N_126DescendantAccessPathRouter29replace_selected_payload_headERNS_17TColumnAccessPathERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEENKUlRT_E_clINS_15TDataAccessPathEEEDaSE_
Line
Count
Source
273
25
        return visit_selected_payload(access_path, [&](auto& payload) {
274
25
            DORIS_CHECK(!payload.path.empty());
275
25
            payload.path.front() = child_name;
276
25
            return Status::OK();
277
25
        });
column_reader.cpp:_ZZN5doris10segment_v212_GLOBAL__N_126DescendantAccessPathRouter29replace_selected_payload_headERNS_17TColumnAccessPathERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEENKUlRT_E_clINS_15TMetaAccessPathEEEDaSE_
Line
Count
Source
273
13
        return visit_selected_payload(access_path, [&](auto& payload) {
274
13
            DORIS_CHECK(!payload.path.empty());
275
13
            payload.path.front() = child_name;
276
13
            return Status::OK();
277
13
        });
278
38
    }
279
280
    // Map `*` applies trailing qualifiers only to values, while locating entries still requires
281
    // complete KEYS as DATA. Construct a fresh key path because the source may be META, and
282
    // preserve its version so child routing keeps the same legacy/typed encoding.
283
    static TColumnAccessPath make_full_data_path_for_map_keys(
284
9
            const TColumnAccessPath& version_source) {
285
9
        TColumnAccessPath child_path;
286
9
        child_path.__set_type(TAccessPathType::DATA);
287
9
        TDataAccessPath data_path;
288
9
        data_path.__set_path({ColumnIterator::ACCESS_MAP_KEYS});
289
9
        child_path.__set_data_access_path(data_path);
290
9
        if (version_source.__isset.version) {
291
9
            child_path.__set_version(version_source.version);
292
9
        }
293
9
        return child_path;
294
9
    }
295
296
40
    static Result<MapSelector> classify_map_selector(const TColumnAccessPath& access_path) {
297
40
        MapSelector selector = MapSelector::UNRECOGNIZED;
298
40
        auto status = visit_selected_payload(access_path, [&](const auto& payload) {
299
40
            DORIS_CHECK(!payload.path.empty());
300
40
            const auto& head = payload.path.front();
301
40
            if (head == ColumnIterator::ACCESS_ALL) {
302
9
                selector = MapSelector::WILDCARD;
303
31
            } else if (head == ColumnIterator::ACCESS_MAP_KEYS) {
304
9
                selector = MapSelector::KEYS;
305
22
            } else if (head == ColumnIterator::ACCESS_MAP_VALUES) {
306
22
                selector = MapSelector::VALUES;
307
22
            }
308
40
            return Status::OK();
309
40
        });
column_reader.cpp:_ZZN5doris10segment_v212_GLOBAL__N_126DescendantAccessPathRouter21classify_map_selectorERKNS_17TColumnAccessPathEENKUlRKT_E_clINS_15TDataAccessPathEEEDaS8_
Line
Count
Source
298
29
        auto status = visit_selected_payload(access_path, [&](const auto& payload) {
299
29
            DORIS_CHECK(!payload.path.empty());
300
29
            const auto& head = payload.path.front();
301
29
            if (head == ColumnIterator::ACCESS_ALL) {
302
2
                selector = MapSelector::WILDCARD;
303
27
            } else if (head == ColumnIterator::ACCESS_MAP_KEYS) {
304
9
                selector = MapSelector::KEYS;
305
18
            } else if (head == ColumnIterator::ACCESS_MAP_VALUES) {
306
18
                selector = MapSelector::VALUES;
307
18
            }
308
29
            return Status::OK();
309
29
        });
column_reader.cpp:_ZZN5doris10segment_v212_GLOBAL__N_126DescendantAccessPathRouter21classify_map_selectorERKNS_17TColumnAccessPathEENKUlRKT_E_clINS_15TMetaAccessPathEEEDaS8_
Line
Count
Source
298
11
        auto status = visit_selected_payload(access_path, [&](const auto& payload) {
299
11
            DORIS_CHECK(!payload.path.empty());
300
11
            const auto& head = payload.path.front();
301
11
            if (head == ColumnIterator::ACCESS_ALL) {
302
7
                selector = MapSelector::WILDCARD;
303
7
            } else if (head == ColumnIterator::ACCESS_MAP_KEYS) {
304
0
                selector = MapSelector::KEYS;
305
4
            } else if (head == ColumnIterator::ACCESS_MAP_VALUES) {
306
4
                selector = MapSelector::VALUES;
307
4
            }
308
11
            return Status::OK();
309
11
        });
310
40
        if (!status.ok()) {
311
0
            return ResultError(std::move(status));
312
0
        }
313
40
        return selector;
314
40
    }
315
316
    static Status distribute_map_paths(TColumnAccessPaths source_paths,
317
                                       TColumnAccessPaths& key_paths,
318
56
                                       TColumnAccessPaths& value_paths) {
319
56
        for (auto& path : source_paths) {
320
40
            const auto selector = DORIS_TRY(classify_map_selector(path));
321
40
            switch (selector) {
322
9
            case MapSelector::WILDCARD:
323
                // A wildcard needs complete keys for runtime lookup, while any remaining
324
                // qualifiers apply only to the value. The value keeps its type and version.
325
9
                key_paths.emplace_back(make_full_data_path_for_map_keys(path));
326
9
                RETURN_IF_ERROR(
327
9
                        replace_selected_payload_head(path, ColumnIterator::ACCESS_MAP_VALUES));
328
9
                value_paths.emplace_back(std::move(path));
329
9
                break;
330
9
            case MapSelector::KEYS:
331
9
                key_paths.emplace_back(std::move(path));
332
9
                break;
333
22
            case MapSelector::VALUES:
334
22
                value_paths.emplace_back(std::move(path));
335
22
                break;
336
0
            case MapSelector::UNRECOGNIZED:
337
0
                break;
338
40
            }
339
40
        }
340
56
        return Status::OK();
341
56
    }
342
};
343
344
} // namespace
345
346
Status ColumnReader::create_array(const ColumnReaderOptions& opts, const ColumnMetaPB& meta,
347
                                  const io::FileReaderSPtr& file_reader,
348
37
                                  std::shared_ptr<ColumnReader>* reader) {
349
37
    DCHECK(meta.children_columns_size() == 2 || meta.children_columns_size() == 3);
350
351
37
    std::shared_ptr<ColumnReader> item_reader;
352
37
    RETURN_IF_ERROR(ColumnReader::create(opts, meta.children_columns(0),
353
37
                                         meta.children_columns(0).num_rows(), file_reader,
354
37
                                         &item_reader));
355
356
37
    std::shared_ptr<ColumnReader> offset_reader;
357
37
    RETURN_IF_ERROR(ColumnReader::create(opts, meta.children_columns(1),
358
37
                                         meta.children_columns(1).num_rows(), file_reader,
359
37
                                         &offset_reader));
360
361
37
    std::shared_ptr<ColumnReader> null_reader;
362
37
    if (meta.is_nullable()) {
363
33
        RETURN_IF_ERROR(ColumnReader::create(opts, meta.children_columns(2),
364
33
                                             meta.children_columns(2).num_rows(), file_reader,
365
33
                                             &null_reader));
366
33
    }
367
368
    // The num rows of the array reader equals to the num rows of the length reader.
369
37
    uint64_t array_num_rows = meta.children_columns(1).num_rows();
370
37
    std::shared_ptr<ColumnReader> array_reader(
371
37
            new ColumnReader(opts, meta, array_num_rows, file_reader));
372
    //  array reader do not need to init
373
37
    array_reader->_sub_readers.resize(meta.children_columns_size());
374
37
    array_reader->_sub_readers[0] = std::move(item_reader);
375
37
    array_reader->_sub_readers[1] = std::move(offset_reader);
376
37
    if (meta.is_nullable()) {
377
33
        array_reader->_sub_readers[2] = std::move(null_reader);
378
33
    }
379
37
    array_reader->_meta_type = FieldType::OLAP_FIELD_TYPE_ARRAY;
380
37
    *reader = std::move(array_reader);
381
37
    return Status::OK();
382
37
}
383
384
Status ColumnReader::create_map(const ColumnReaderOptions& opts, const ColumnMetaPB& meta,
385
                                const io::FileReaderSPtr& file_reader,
386
795
                                std::shared_ptr<ColumnReader>* reader) {
387
    // map reader now has 3 sub readers for key, value, offsets(scalar), null(scala)
388
795
    DCHECK(meta.children_columns_size() == 3 || meta.children_columns_size() == 4);
389
795
    std::shared_ptr<ColumnReader> key_reader;
390
795
    RETURN_IF_ERROR(ColumnReader::create(opts, meta.children_columns(0),
391
795
                                         meta.children_columns(0).num_rows(), file_reader,
392
795
                                         &key_reader));
393
795
    std::shared_ptr<ColumnReader> val_reader;
394
795
    RETURN_IF_ERROR(ColumnReader::create(opts, meta.children_columns(1),
395
795
                                         meta.children_columns(1).num_rows(), file_reader,
396
795
                                         &val_reader));
397
795
    std::shared_ptr<ColumnReader> offset_reader;
398
795
    RETURN_IF_ERROR(ColumnReader::create(opts, meta.children_columns(2),
399
795
                                         meta.children_columns(2).num_rows(), file_reader,
400
795
                                         &offset_reader));
401
795
    std::shared_ptr<ColumnReader> null_reader;
402
795
    if (meta.is_nullable()) {
403
0
        RETURN_IF_ERROR(ColumnReader::create(opts, meta.children_columns(3),
404
0
                                             meta.children_columns(3).num_rows(), file_reader,
405
0
                                             &null_reader));
406
0
    }
407
408
    // The num rows of the map reader equals to the num rows of the length reader.
409
795
    uint64_t map_num_rows = meta.children_columns(2).num_rows();
410
795
    std::shared_ptr<ColumnReader> map_reader(
411
795
            new ColumnReader(opts, meta, map_num_rows, file_reader));
412
795
    map_reader->_sub_readers.resize(meta.children_columns_size());
413
414
795
    map_reader->_sub_readers[0] = std::move(key_reader);
415
795
    map_reader->_sub_readers[1] = std::move(val_reader);
416
795
    map_reader->_sub_readers[2] = std::move(offset_reader);
417
795
    if (meta.is_nullable()) {
418
0
        map_reader->_sub_readers[3] = std::move(null_reader);
419
0
    }
420
795
    map_reader->_meta_type = FieldType::OLAP_FIELD_TYPE_MAP;
421
795
    *reader = std::move(map_reader);
422
795
    return Status::OK();
423
795
}
424
425
Status ColumnReader::create_struct(const ColumnReaderOptions& opts, const ColumnMetaPB& meta,
426
                                   uint64_t num_rows, const io::FileReaderSPtr& file_reader,
427
0
                                   std::shared_ptr<ColumnReader>* reader) {
428
    // not support empty struct
429
0
    DCHECK(meta.children_columns_size() >= 1);
430
    // create struct column reader
431
0
    std::shared_ptr<ColumnReader> struct_reader(
432
0
            new ColumnReader(opts, meta, num_rows, file_reader));
433
0
    struct_reader->_sub_readers.reserve(meta.children_columns_size());
434
    // now we support struct column can add the children columns according to the schema-change behavior
435
0
    for (int i = 0; i < meta.children_columns_size(); i++) {
436
0
        std::shared_ptr<ColumnReader> sub_reader;
437
0
        RETURN_IF_ERROR(ColumnReader::create(opts, meta.children_columns(i),
438
0
                                             meta.children_columns(i).num_rows(), file_reader,
439
0
                                             &sub_reader));
440
0
        struct_reader->_sub_readers.push_back(std::move(sub_reader));
441
0
    }
442
0
    struct_reader->_meta_type = FieldType::OLAP_FIELD_TYPE_STRUCT;
443
0
    *reader = std::move(struct_reader);
444
0
    return Status::OK();
445
0
}
446
447
Status ColumnReader::create_agg_state(const ColumnReaderOptions& opts, const ColumnMetaPB& meta,
448
                                      uint64_t num_rows, const io::FileReaderSPtr& file_reader,
449
0
                                      std::shared_ptr<ColumnReader>* reader) {
450
0
    if (!meta.has_function_name()) { // meet old version ColumnMetaPB
451
0
        std::shared_ptr<ColumnReader> reader_local(
452
0
                new ColumnReader(opts, meta, num_rows, file_reader));
453
0
        RETURN_IF_ERROR(reader_local->init(&meta));
454
0
        *reader = std::move(reader_local);
455
0
        return Status::OK();
456
0
    }
457
458
0
    auto data_type = DataTypeFactory::instance().create_data_type(meta);
459
0
    const auto* agg_state_type = assert_cast<const DataTypeAggState*>(data_type.get());
460
0
    agg_state_type->check_function_compatibility(opts.be_exec_version);
461
0
    auto type = agg_state_type->get_serialized_type()->get_primitive_type();
462
463
0
    if (read_as_string(type)) {
464
0
        std::shared_ptr<ColumnReader> reader_local(
465
0
                new ColumnReader(opts, meta, num_rows, file_reader));
466
0
        RETURN_IF_ERROR(reader_local->init(&meta));
467
0
        *reader = std::move(reader_local);
468
0
        return Status::OK();
469
0
    } else if (type == PrimitiveType::TYPE_MAP) {
470
0
        return create_map(opts, meta, file_reader, reader);
471
0
    } else if (type == PrimitiveType::TYPE_ARRAY) {
472
0
        return create_array(opts, meta, file_reader, reader);
473
0
    } else if (type == PrimitiveType::TYPE_STRUCT) {
474
0
        return create_struct(opts, meta, num_rows, file_reader, reader);
475
0
    }
476
477
0
    return Status::InternalError("Not supported type: {}, serialized type: {}",
478
0
                                 agg_state_type->get_name(), int(type));
479
0
}
480
481
1.51k
bool ColumnReader::is_compaction_reader_type(ReaderType type) {
482
1.51k
    return type == ReaderType::READER_BASE_COMPACTION ||
483
1.51k
           type == ReaderType::READER_CUMULATIVE_COMPACTION ||
484
1.51k
           type == ReaderType::READER_COLD_DATA_COMPACTION ||
485
1.51k
           type == ReaderType::READER_SEGMENT_COMPACTION ||
486
1.51k
           type == ReaderType::READER_FULL_COMPACTION;
487
1.51k
}
488
489
Status ColumnReader::create(const ColumnReaderOptions& opts, const ColumnMetaPB& meta,
490
                            uint64_t num_rows, const io::FileReaderSPtr& file_reader,
491
11.9k
                            std::shared_ptr<ColumnReader>* reader) {
492
11.9k
    if (opts.const_value.has_value()) {
493
3
        *reader = std::make_shared<ConstantColumnReader>(*opts.const_value);
494
3
        return Status::OK();
495
3
    }
496
11.9k
    if (is_scalar_type((FieldType)meta.type())) {
497
10.3k
        std::shared_ptr<ColumnReader> reader_local(
498
10.3k
                new ColumnReader(opts, meta, num_rows, file_reader));
499
10.3k
        RETURN_IF_ERROR(reader_local->init(&meta));
500
10.3k
        *reader = std::move(reader_local);
501
10.3k
        return Status::OK();
502
10.3k
    } else {
503
1.56k
        auto type = (FieldType)meta.type();
504
1.56k
        switch (type) {
505
0
        case FieldType::OLAP_FIELD_TYPE_AGG_STATE: {
506
0
            return create_agg_state(opts, meta, num_rows, file_reader, reader);
507
0
        }
508
0
        case FieldType::OLAP_FIELD_TYPE_STRUCT: {
509
0
            return create_struct(opts, meta, num_rows, file_reader, reader);
510
0
        }
511
37
        case FieldType::OLAP_FIELD_TYPE_ARRAY: {
512
37
            return create_array(opts, meta, file_reader, reader);
513
0
        }
514
795
        case FieldType::OLAP_FIELD_TYPE_MAP: {
515
795
            return create_map(opts, meta, file_reader, reader);
516
0
        }
517
733
        case FieldType::OLAP_FIELD_TYPE_VARIANT: {
518
            // Read variant only root data using a single ColumnReader
519
733
            std::shared_ptr<ColumnReader> reader_local(
520
733
                    new ColumnReader(opts, meta, num_rows, file_reader));
521
733
            RETURN_IF_ERROR(reader_local->init(&meta));
522
733
            *reader = std::move(reader_local);
523
733
            return Status::OK();
524
733
        }
525
0
        default:
526
0
            return Status::NotSupported("unsupported type for ColumnReader: {}",
527
0
                                        std::to_string(int(type)));
528
1.56k
        }
529
1.56k
    }
530
11.9k
}
531
532
1.23k
ColumnReader::ColumnReader() = default;
533
534
ColumnReader::ColumnReader(const ColumnReaderOptions& opts, const ColumnMetaPB& meta,
535
                           uint64_t num_rows, io::FileReaderSPtr file_reader)
536
11.9k
        : _use_index_page_cache(!config::disable_storage_page_cache),
537
11.9k
          _opts(opts),
538
11.9k
          _num_rows(num_rows),
539
11.9k
          _file_reader(std::move(file_reader)),
540
11.9k
          _dict_encoding_type(UNKNOWN_DICT_ENCODING) {
541
11.9k
    _meta_length = meta.length();
542
11.9k
    _meta_type = (FieldType)meta.type();
543
11.9k
    if (_meta_type == FieldType::OLAP_FIELD_TYPE_ARRAY) {
544
37
        _meta_children_column_type = (FieldType)meta.children_columns(0).type();
545
37
    }
546
11.9k
    _data_type = DataTypeFactory::instance().create_data_type(meta);
547
11.9k
    _meta_is_nullable = meta.is_nullable();
548
11.9k
    _meta_dict_page = meta.dict_page();
549
11.9k
    _meta_compression = meta.compression();
550
11.9k
}
551
552
13.1k
ColumnReader::~ColumnReader() = default;
553
554
11.3k
int64_t ColumnReader::get_metadata_size() const {
555
11.3k
    return sizeof(ColumnReader) + (_segment_zone_map ? _segment_zone_map->ByteSizeLong() : 0);
556
11.3k
}
557
558
#ifdef BE_TEST
559
/// This function is only used in UT to verify the correctness of data read from zone map
560
/// See UT case 'SegCompactionMoWTest.SegCompactionInterleaveWithBig_ooooOOoOooooooooO'
561
/// be/test/olap/segcompaction_mow_test.cpp
562
20.4k
void ColumnReader::check_data_by_zone_map_for_test(const MutableColumnPtr& dst) const {
563
20.4k
    if (!_segment_zone_map) {
564
2.10k
        return;
565
2.10k
    }
566
567
18.3k
    const auto rows = dst->size();
568
18.3k
    if (rows == 0) {
569
0
        return;
570
0
    }
571
572
18.3k
    FieldType type = _type;
573
574
18.3k
    if (type != FieldType::OLAP_FIELD_TYPE_INT) {
575
2.92k
        return;
576
2.92k
    }
577
578
15.4k
    auto* non_nullable_column =
579
15.4k
            is_column_nullable(*dst)
580
15.4k
                    ? assert_cast<ColumnNullable*>(dst.get())->get_nested_column_ptr().get()
581
15.4k
                    : dst.get();
582
583
    /// Only verify when the destination column carries Field-accessible TYPE_INT data.
584
15.4k
    if (check_and_get_column<ColumnVector<TYPE_INT>>(non_nullable_column) == nullptr) {
585
0
        return;
586
0
    }
587
588
15.4k
    ZoneMap zone_map;
589
15.4k
    THROW_IF_ERROR(ZoneMap::from_proto(*_segment_zone_map, _data_type, zone_map));
590
591
15.4k
    if (zone_map.has_null) {
592
10
        return;
593
10
    }
594
595
22.1M
    for (size_t i = 0; i != rows; ++i) {
596
22.1M
        Field field;
597
22.1M
        dst->get(i, field);
598
22.1M
        DCHECK(!field.is_null());
599
22.1M
        const auto v = field.get<TYPE_INT>();
600
22.1M
        DCHECK_GE(v, zone_map.min_value.get<TYPE_INT>());
601
22.1M
        DCHECK_LE(v, zone_map.max_value.get<TYPE_INT>());
602
22.1M
    }
603
15.4k
}
604
#endif
605
606
11.1k
Status ColumnReader::init(const ColumnMetaPB* meta) {
607
11.1k
    _type = (FieldType)meta->type();
608
609
11.1k
    if (meta->has_be_exec_version()) {
610
7.97k
        _be_exec_version = meta->be_exec_version();
611
7.97k
    }
612
613
11.1k
    if (_type == FieldType::OLAP_FIELD_TYPE_NONE || _type == FieldType::OLAP_FIELD_TYPE_UNKNOWN) {
614
0
        return Status::NotSupported("unsupported typeinfo, type={}", meta->type());
615
0
    }
616
11.1k
    RETURN_IF_ERROR(EncodingInfo::get(_type, meta->encoding(), &_encoding_info));
617
618
29.1k
    for (int i = 0; i < meta->indexes_size(); i++) {
619
18.0k
        const auto& index_meta = meta->indexes(i);
620
18.0k
        switch (index_meta.type()) {
621
0
        case BITMAP_INDEX:
622
0
            break;
623
10.5k
        case ORDINAL_INDEX:
624
10.5k
            _ordinal_index.reset(
625
10.5k
                    new OrdinalIndexReader(_file_reader, _num_rows, index_meta.ordinal_index()));
626
10.5k
            break;
627
7.55k
        case ZONE_MAP_INDEX:
628
7.55k
            _segment_zone_map =
629
7.55k
                    std::make_unique<ZoneMapPB>(index_meta.zone_map_index().segment_zone_map());
630
7.55k
            _zone_map_index.reset(new ZoneMapIndexReader(
631
7.55k
                    _file_reader, index_meta.zone_map_index().page_zone_maps()));
632
7.55k
            break;
633
7
        case BLOOM_FILTER_INDEX:
634
7
            _bloom_filter_index.reset(
635
7
                    new BloomFilterIndexReader(_file_reader, index_meta.bloom_filter_index()));
636
7
            break;
637
0
        case NESTED_OFFSETS_INDEX:
638
0
            break;
639
0
        default:
640
0
            return Status::Corruption("Bad file {}: invalid column index type {}",
641
0
                                      _file_reader->path().native(), index_meta.type());
642
18.0k
        }
643
18.0k
    }
644
11.1k
    update_metadata_size();
645
646
    // ArrayColumnWriter writes a single empty array and flushes. In this scenario,
647
    // the item writer doesn't write any data and the corresponding ordinal index is empty.
648
11.1k
    if (_ordinal_index == nullptr && !is_empty()) {
649
0
        return Status::Corruption("Bad file {}: missing ordinal index for column {}",
650
0
                                  _file_reader->path().native(), meta->column_id());
651
0
    }
652
653
11.1k
    return Status::OK();
654
11.1k
}
655
656
Status ColumnReader::new_index_iterator(const std::shared_ptr<IndexFileReader>& index_file_reader,
657
                                        const TabletIndex* index_meta, const std::string& rowset_id,
658
                                        uint32_t segment_id, size_t rows_of_segment,
659
2.67k
                                        std::unique_ptr<IndexIterator>* iterator) {
660
2.67k
    RETURN_IF_ERROR(
661
2.67k
            _load_index(index_file_reader, index_meta, rowset_id, segment_id, rows_of_segment));
662
2.67k
    {
663
2.67k
        std::shared_lock<std::shared_mutex> rlock(_load_index_lock);
664
2.67k
        auto iter = _index_readers.find(index_meta->index_id());
665
2.67k
        if (iter != _index_readers.end()) {
666
2.67k
            if (iter->second != nullptr) {
667
2.67k
                RETURN_IF_ERROR(iter->second->new_iterator(iterator));
668
2.67k
            }
669
2.67k
        }
670
2.67k
    }
671
2.67k
    return Status::OK();
672
2.67k
}
673
674
Status ColumnReader::read_page(const ColumnIteratorOptions& iter_opts, const PagePointer& pp,
675
                               PageHandle* handle, Slice* page_body, PageFooterPB* footer,
676
13.8k
                               BlockCompressionCodec* codec) const {
677
13.8k
    SCOPED_CONCURRENCY_COUNT(ConcurrencyStatsManager::instance().column_reader_read_page);
678
13.8k
    iter_opts.sanity_check();
679
13.8k
    PageReadOptions opts(iter_opts.io_ctx);
680
13.8k
    opts.verify_checksum = _opts.verify_checksum;
681
13.8k
    opts.use_page_cache = iter_opts.use_page_cache;
682
13.8k
    opts.kept_in_memory = _opts.kept_in_memory;
683
13.8k
    opts.type = iter_opts.type;
684
13.8k
    opts.file_reader = iter_opts.file_reader;
685
13.8k
    opts.page_pointer = pp;
686
13.8k
    opts.codec = codec;
687
13.8k
    opts.stats = iter_opts.stats;
688
13.8k
    opts.encoding_info = _encoding_info;
689
690
13.8k
    return PageIO::read_and_decompress_page(opts, handle, page_body, footer);
691
13.8k
}
692
693
Status ColumnReader::get_row_ranges_by_zone_map(
694
        const AndBlockColumnPredicate* col_predicates,
695
        const std::vector<std::shared_ptr<const ColumnPredicate>>* delete_predicates,
696
64
        RowRanges* row_ranges, const ColumnIteratorOptions& iter_opts) {
697
64
    std::vector<uint32_t> page_indexes;
698
64
    RETURN_IF_ERROR(
699
64
            _get_filtered_pages(col_predicates, delete_predicates, &page_indexes, iter_opts));
700
64
    RETURN_IF_ERROR(_calculate_row_ranges(page_indexes, row_ranges, iter_opts));
701
64
    return Status::OK();
702
64
}
703
704
0
Status ColumnReader::next_batch_of_zone_map(size_t* n, MutableColumnPtr& dst) const {
705
0
    if (_segment_zone_map == nullptr) {
706
0
        return Status::InternalError("segment zonemap not exist");
707
0
    }
708
    // TODO: this work to get min/max value seems should only do once
709
0
    ZoneMap zone_map;
710
0
    RETURN_IF_ERROR(ZoneMap::from_proto(*_segment_zone_map, _data_type, zone_map));
711
712
0
    dst->reserve(*n);
713
0
    if (!zone_map.has_not_null) {
714
0
        assert_cast<ColumnNullable&>(*dst).insert_many_defaults(*n);
715
0
        return Status::OK();
716
0
    }
717
0
    dst->insert(zone_map.max_value);
718
0
    for (int i = 1; i < *n; ++i) {
719
0
        dst->insert(zone_map.min_value);
720
0
    }
721
0
    return Status::OK();
722
0
}
723
724
Status ColumnReader::match_condition(const AndBlockColumnPredicate* col_predicates,
725
71
                                     bool* matched) const {
726
71
    *matched = true;
727
71
    if (_zone_map_index == nullptr) {
728
0
        return Status::OK();
729
0
    }
730
71
    ZoneMap zone_map;
731
71
    RETURN_IF_ERROR(ZoneMap::from_proto(*_segment_zone_map, _data_type, zone_map));
732
733
71
    *matched = _zone_map_match_condition(zone_map, col_predicates);
734
71
    return Status::OK();
735
71
}
736
737
Status ConstantColumnReader::match_condition(const AndBlockColumnPredicate* col_predicates,
738
3
                                             bool* matched) const {
739
3
    ZoneMap zone_map;
740
3
    zone_map.min_value = _value;
741
3
    zone_map.max_value = _value;
742
3
    zone_map.has_not_null = !_value.is_null();
743
    // evaluate_and returns false iff no value in [min, max] (i.e. the real constant) can satisfy
744
    // the predicates; predicates that don't support zonemap conservatively return true.
745
3
    *matched = col_predicates->evaluate_and(zone_map);
746
3
    return Status::OK();
747
3
}
748
749
Status ColumnReader::prune_predicates_by_zone_map(
750
        std::vector<std::shared_ptr<ColumnPredicate>>& predicates, const int column_id,
751
64
        bool* pruned) const {
752
64
    *pruned = false;
753
64
    if (_zone_map_index == nullptr) {
754
44
        return Status::OK();
755
44
    }
756
757
20
    ZoneMap zone_map;
758
20
    RETURN_IF_ERROR(ZoneMap::from_proto(*_segment_zone_map, _data_type, zone_map));
759
20
    if (zone_map.pass_all) {
760
0
        return Status::OK();
761
0
    }
762
763
40
    for (auto it = predicates.begin(); it != predicates.end();) {
764
20
        auto predicate = *it;
765
20
        if (predicate->column_id() == column_id && predicate->is_always_true(zone_map)) {
766
0
            *pruned = true;
767
0
            it = predicates.erase(it);
768
20
        } else {
769
20
            ++it;
770
20
        }
771
20
    }
772
20
    return Status::OK();
773
20
}
774
775
bool ColumnReader::_zone_map_match_condition(const ZoneMap& zone_map,
776
78
                                             const AndBlockColumnPredicate* col_predicates) const {
777
78
    if (zone_map.pass_all) {
778
0
        return true;
779
0
    }
780
781
78
    return col_predicates->evaluate_and(zone_map);
782
78
}
783
784
Status ColumnReader::_get_filtered_pages(
785
        const AndBlockColumnPredicate* col_predicates,
786
        const std::vector<std::shared_ptr<const ColumnPredicate>>* delete_predicates,
787
64
        std::vector<uint32_t>* page_indexes, const ColumnIteratorOptions& iter_opts) {
788
64
    RETURN_IF_ERROR(_load_zone_map_index(_use_index_page_cache, _opts.kept_in_memory, iter_opts));
789
790
64
    const std::vector<ZoneMapPB>& zone_maps = _zone_map_index->page_zone_maps();
791
64
    size_t page_size = _zone_map_index->num_pages();
792
128
    for (size_t i = 0; i < page_size; ++i) {
793
64
        if (zone_maps[i].pass_all()) {
794
57
            page_indexes->push_back(cast_set<uint32_t>(i));
795
57
        } else {
796
7
            segment_v2::ZoneMap zone_map;
797
7
            RETURN_IF_ERROR(ZoneMap::from_proto(zone_maps[i], _data_type, zone_map));
798
7
            if (_zone_map_match_condition(zone_map, col_predicates)) {
799
7
                bool should_read = true;
800
7
                if (delete_predicates != nullptr) {
801
0
                    for (auto del_pred : *delete_predicates) {
802
                        // TODO: Both `min_value` and `max_value` should be 0 or neither should be 0.
803
                        //  So nullable only need to judge once.
804
0
                        if (del_pred->evaluate_del(zone_map)) {
805
0
                            should_read = false;
806
0
                            break;
807
0
                        }
808
0
                    }
809
0
                }
810
7
                if (should_read) {
811
7
                    page_indexes->push_back(cast_set<uint32_t>(i));
812
7
                }
813
7
            }
814
7
        }
815
64
    }
816
64
    VLOG(1) << "total-pages: " << page_size << " not-filtered-pages: " << page_indexes->size()
817
64
            << " filtered-percent:"
818
64
            << 1.0 - (static_cast<double>(page_indexes->size()) /
819
64
                      (static_cast<double>(page_size) * 1.0));
820
64
    return Status::OK();
821
64
}
822
823
Status ColumnReader::_calculate_row_ranges(const std::vector<uint32_t>& page_indexes,
824
                                           RowRanges* row_ranges,
825
64
                                           const ColumnIteratorOptions& iter_opts) {
826
64
    row_ranges->clear();
827
64
    RETURN_IF_ERROR(_load_ordinal_index(_use_index_page_cache, _opts.kept_in_memory, iter_opts));
828
64
    for (auto i : page_indexes) {
829
64
        ordinal_t page_first_id = _ordinal_index->get_first_ordinal(i);
830
64
        ordinal_t page_last_id = _ordinal_index->get_last_ordinal(i);
831
64
        RowRanges page_row_ranges(RowRanges::create_single(page_first_id, page_last_id + 1));
832
64
        RowRanges::ranges_union(*row_ranges, page_row_ranges, row_ranges);
833
64
    }
834
64
    return Status::OK();
835
64
}
836
837
Status ColumnReader::get_row_ranges_by_bloom_filter(const AndBlockColumnPredicate* col_predicates,
838
                                                    RowRanges* row_ranges,
839
3
                                                    const ColumnIteratorOptions& iter_opts) {
840
3
    RETURN_IF_ERROR(_load_ordinal_index(_use_index_page_cache, _opts.kept_in_memory, iter_opts));
841
3
    RETURN_IF_ERROR(
842
3
            _load_bloom_filter_index(_use_index_page_cache, _opts.kept_in_memory, iter_opts));
843
3
    RowRanges bf_row_ranges;
844
3
    std::unique_ptr<BloomFilterIndexIterator> bf_iter;
845
3
    RETURN_IF_ERROR(
846
3
            _bloom_filter_index->new_iterator(&bf_iter, iter_opts.stats, &iter_opts.io_ctx));
847
3
    size_t range_size = row_ranges->range_size();
848
    // get covered page ids
849
3
    std::set<uint32_t> page_ids;
850
6
    for (int i = 0; i < range_size; ++i) {
851
3
        int64_t from = row_ranges->get_range_from(i);
852
3
        int64_t idx = from;
853
3
        int64_t to = row_ranges->get_range_to(i);
854
3
        auto iter = _ordinal_index->seek_at_or_before(from);
855
6
        while (idx < to && iter.valid()) {
856
3
            page_ids.insert(iter.page_index());
857
3
            idx = iter.last_ordinal() + 1;
858
3
            iter.next();
859
3
        }
860
3
    }
861
3
    for (auto& pid : page_ids) {
862
3
        std::unique_ptr<BloomFilter> bf;
863
3
        RETURN_IF_ERROR(bf_iter->read_bloom_filter(pid, &bf));
864
3
        if (col_predicates->evaluate_and(bf.get())) {
865
0
            bf_row_ranges.add(RowRange(_ordinal_index->get_first_ordinal(pid),
866
0
                                       _ordinal_index->get_last_ordinal(pid) + 1));
867
0
        }
868
3
    }
869
3
    RowRanges::ranges_intersection(*row_ranges, bf_row_ranges, row_ranges);
870
3
    return Status::OK();
871
3
}
872
873
Status ColumnReader::_load_ordinal_index(bool use_page_cache, bool kept_in_memory,
874
9.11k
                                         const ColumnIteratorOptions& iter_opts) {
875
9.11k
    if (!_ordinal_index) {
876
0
        return Status::InternalError("ordinal_index not inited");
877
0
    }
878
9.11k
    return _ordinal_index->load(use_page_cache, kept_in_memory, iter_opts.stats, &iter_opts.io_ctx);
879
9.11k
}
880
881
Status ColumnReader::_load_zone_map_index(bool use_page_cache, bool kept_in_memory,
882
65
                                          const ColumnIteratorOptions& iter_opts) {
883
65
    if (_zone_map_index != nullptr) {
884
65
        return _zone_map_index->load(use_page_cache, kept_in_memory, iter_opts.stats,
885
65
                                     &iter_opts.io_ctx);
886
65
    }
887
0
    return Status::OK();
888
65
}
889
890
8
Status ColumnReader::get_segment_zone_map(segment_v2::ZoneMap* zone_map) const {
891
8
    DORIS_CHECK(zone_map != nullptr);
892
8
    DORIS_CHECK(_segment_zone_map != nullptr);
893
8
    return ZoneMap::from_proto(*_segment_zone_map, _data_type, *zone_map);
894
8
}
895
896
1
Status ConstantColumnReader::get_segment_zone_map(segment_v2::ZoneMap* zone_map) const {
897
1
    zone_map->min_value = _value;
898
1
    zone_map->max_value = _value;
899
1
    zone_map->has_not_null = !_value.is_null();
900
1
    return Status::OK();
901
1
}
902
903
Status ColumnReader::get_page_zone_maps(const ColumnIteratorOptions& iter_opts,
904
1
                                        const std::vector<ZoneMapPB>** zone_maps) {
905
1
    DORIS_CHECK(zone_maps != nullptr);
906
1
    if (_zone_map_index == nullptr) {
907
0
        *zone_maps = nullptr;
908
0
        return Status::OK();
909
0
    }
910
1
    RETURN_IF_ERROR(_load_zone_map_index(_use_index_page_cache, _opts.kept_in_memory, iter_opts));
911
1
    *zone_maps = &_zone_map_index->page_zone_maps();
912
1
    return Status::OK();
913
1
}
914
915
Status ColumnReader::get_row_range_for_page(uint32_t page_index,
916
                                            const ColumnIteratorOptions& iter_opts,
917
8
                                            RowRange* row_range) {
918
8
    DORIS_CHECK(row_range != nullptr);
919
8
    RETURN_IF_ERROR(_load_ordinal_index(_use_index_page_cache, _opts.kept_in_memory, iter_opts));
920
8
    DORIS_CHECK(page_index < _ordinal_index->num_data_pages());
921
8
    *row_range = RowRange(_ordinal_index->get_first_ordinal(page_index),
922
8
                          _ordinal_index->get_last_ordinal(page_index) + 1);
923
8
    return Status::OK();
924
8
}
925
926
Status ColumnReader::_load_index(const std::shared_ptr<IndexFileReader>& index_file_reader,
927
                                 const TabletIndex* index_meta, const std::string& rowset_id,
928
2.67k
                                 uint32_t segment_id, size_t rows_of_segment) {
929
2.67k
    std::unique_lock<std::shared_mutex> wlock(_load_index_lock);
930
931
2.67k
    if (index_meta == nullptr) {
932
0
        return Status::Error<ErrorCode::INVERTED_INDEX_CLUCENE_ERROR>(
933
0
                "Failed to load inverted index: index metadata is null");
934
0
    }
935
936
2.67k
    auto it = _index_readers.find(index_meta->index_id());
937
2.67k
    if (it != _index_readers.end()) {
938
0
        return Status::OK();
939
0
    }
940
941
2.67k
    bool should_analyzer =
942
2.67k
            inverted_index::InvertedIndexAnalyzer::should_analyzer(index_meta->properties());
943
944
2.67k
    FieldType type;
945
2.67k
    if (_meta_type == FieldType::OLAP_FIELD_TYPE_ARRAY) {
946
22
        type = _meta_children_column_type;
947
2.65k
    } else {
948
2.65k
        type = _type;
949
2.65k
    }
950
951
2.67k
    if (index_meta->index_type() == IndexType::ANN) {
952
1
        _index_readers[index_meta->index_id()] = std::make_shared<AnnIndexReader>(
953
1
                index_meta, index_file_reader, rowset_id, segment_id, rows_of_segment);
954
1
        return Status::OK();
955
1
    }
956
957
2.67k
    IndexReaderPtr index_reader;
958
959
2.67k
    if (is_string_type(type)) {
960
2.24k
        if (should_analyzer) {
961
1.69k
            try {
962
1.69k
                index_reader = FullTextIndexReader::create_shared(index_meta, index_file_reader);
963
1.69k
            } catch (const CLuceneError& e) {
964
0
                return Status::Error<ErrorCode::INVERTED_INDEX_CLUCENE_ERROR>(
965
0
                        "create FullTextIndexReader error: {}", e.what());
966
0
            }
967
1.69k
        } else {
968
545
            try {
969
545
                index_reader =
970
545
                        StringTypeInvertedIndexReader::create_shared(index_meta, index_file_reader);
971
545
            } catch (const CLuceneError& e) {
972
0
                return Status::Error<ErrorCode::INVERTED_INDEX_CLUCENE_ERROR>(
973
0
                        "create StringTypeInvertedIndexReader error: {}", e.what());
974
0
            }
975
545
        }
976
2.24k
    } else if (field_is_numeric_type(type)) {
977
433
        try {
978
433
            index_reader = BkdIndexReader::create_shared(index_meta, index_file_reader);
979
433
        } catch (const CLuceneError& e) {
980
0
            return Status::Error<ErrorCode::INVERTED_INDEX_CLUCENE_ERROR>(
981
0
                    "create BkdIndexReader error: {}", e.what());
982
0
        }
983
433
    } else {
984
0
        return Status::Error<ErrorCode::INVERTED_INDEX_NOT_SUPPORTED>(
985
0
                "Field type {} is not supported for inverted index", type);
986
0
    }
987
2.67k
    _index_readers[index_meta->index_id()] = index_reader;
988
2.67k
    return Status::OK();
989
2.67k
}
990
991
48
bool ColumnReader::has_bloom_filter_index(bool ngram) const {
992
48
    if (_bloom_filter_index == nullptr) return false;
993
994
3
    if (ngram) {
995
0
        return _bloom_filter_index->algorithm() == BloomFilterAlgorithmPB::NGRAM_BLOOM_FILTER;
996
3
    } else {
997
3
        return _bloom_filter_index->algorithm() != BloomFilterAlgorithmPB::NGRAM_BLOOM_FILTER;
998
3
    }
999
3
}
1000
1001
Status ColumnReader::_load_bloom_filter_index(bool use_page_cache, bool kept_in_memory,
1002
3
                                              const ColumnIteratorOptions& iter_opts) {
1003
3
    if (_bloom_filter_index != nullptr) {
1004
3
        return _bloom_filter_index->load(use_page_cache, kept_in_memory, iter_opts.stats,
1005
3
                                         &iter_opts.io_ctx);
1006
3
    }
1007
0
    return Status::OK();
1008
3
}
1009
1010
Status ColumnReader::seek_at_or_before(ordinal_t ordinal, OrdinalPageIndexIterator* iter,
1011
9.03k
                                       const ColumnIteratorOptions& iter_opts) {
1012
9.03k
    RETURN_IF_ERROR(_load_ordinal_index(_use_index_page_cache, _opts.kept_in_memory, iter_opts));
1013
9.03k
    *iter = _ordinal_index->seek_at_or_before(ordinal);
1014
9.03k
    if (!iter->valid()) {
1015
0
        return Status::NotFound("Failed to seek to ordinal {}, ", ordinal);
1016
0
    }
1017
9.03k
    return Status::OK();
1018
9.03k
}
1019
1020
Status ColumnReader::get_ordinal_index_reader(OrdinalIndexReader*& reader,
1021
0
                                              OlapReaderStatistics* index_load_stats) {
1022
0
    CHECK(_ordinal_index) << fmt::format("ordinal index is null for column reader of type {}",
1023
0
                                         std::to_string(int(_meta_type)));
1024
0
    RETURN_IF_ERROR(
1025
0
            _ordinal_index->load(_use_index_page_cache, _opts.kept_in_memory, index_load_stats));
1026
0
    reader = _ordinal_index.get();
1027
0
    return Status::OK();
1028
0
}
1029
1030
1.73k
Status ColumnReader::new_iterator(ColumnIteratorUPtr* iterator, const TabletColumn* tablet_column) {
1031
1.73k
    return new_iterator(iterator, tablet_column, nullptr);
1032
1.73k
}
1033
1034
Status ColumnReader::new_iterator(ColumnIteratorUPtr* iterator, const TabletColumn* tablet_column,
1035
8.87k
                                  const StorageReadOptions* opt) {
1036
8.87k
    if (is_empty()) {
1037
154
        *iterator = std::make_unique<EmptyFileColumnIterator>();
1038
154
        return Status::OK();
1039
154
    }
1040
8.71k
    if (is_scalar_type(_meta_type)) {
1041
8.40k
        if (is_string_type(_meta_type)) {
1042
2.90k
            *iterator = std::make_unique<StringFileColumnIterator>(shared_from_this());
1043
5.50k
        } else {
1044
5.50k
            *iterator = std::make_unique<FileColumnIterator>(shared_from_this());
1045
5.50k
        }
1046
8.40k
        (*iterator)->set_column_name(tablet_column ? tablet_column->name() : "");
1047
8.40k
        return Status::OK();
1048
8.40k
    } else {
1049
312
        auto type = _meta_type;
1050
312
        switch (type) {
1051
0
        case FieldType::OLAP_FIELD_TYPE_AGG_STATE: {
1052
0
            return new_agg_state_iterator(iterator);
1053
0
        }
1054
0
        case FieldType::OLAP_FIELD_TYPE_STRUCT: {
1055
0
            return new_struct_iterator(iterator, tablet_column);
1056
0
        }
1057
30
        case FieldType::OLAP_FIELD_TYPE_ARRAY: {
1058
30
            return new_array_iterator(iterator, tablet_column);
1059
0
        }
1060
282
        case FieldType::OLAP_FIELD_TYPE_MAP: {
1061
282
            return new_map_iterator(iterator, tablet_column);
1062
0
        }
1063
0
        default:
1064
0
            return Status::NotSupported("unsupported type to create iterator: {}",
1065
0
                                        std::to_string(int(type)));
1066
312
        }
1067
312
    }
1068
8.71k
}
1069
1070
0
Status ColumnReader::new_agg_state_iterator(ColumnIteratorUPtr* iterator) {
1071
0
    *iterator = std::make_unique<FileColumnIterator>(shared_from_this());
1072
0
    return Status::OK();
1073
0
}
1074
1075
Status ColumnReader::new_array_iterator(ColumnIteratorUPtr* iterator,
1076
30
                                        const TabletColumn* tablet_column) {
1077
30
    ColumnIteratorUPtr item_iterator;
1078
30
    RETURN_IF_ERROR(_sub_readers[0]->new_iterator(
1079
30
            &item_iterator, tablet_column && tablet_column->get_subtype_count() > 0
1080
30
                                    ? &tablet_column->get_sub_column(0)
1081
30
                                    : nullptr));
1082
1083
30
    item_iterator->set_column_name(tablet_column ? tablet_column->get_sub_column(0).name() : "");
1084
1085
30
    ColumnIteratorUPtr offset_iterator;
1086
30
    RETURN_IF_ERROR(_sub_readers[1]->new_iterator(&offset_iterator, nullptr));
1087
30
    auto* file_iter = static_cast<FileColumnIterator*>(offset_iterator.release());
1088
30
    OffsetFileColumnIteratorUPtr ofcIter = std::make_unique<OffsetFileColumnIterator>(
1089
30
            std::unique_ptr<FileColumnIterator>(file_iter));
1090
1091
30
    ColumnIteratorUPtr null_iterator;
1092
30
    if (is_nullable()) {
1093
28
        RETURN_IF_ERROR(_sub_readers[2]->new_iterator(&null_iterator, nullptr));
1094
28
    }
1095
30
    *iterator = std::make_unique<ArrayFileColumnIterator>(shared_from_this(), std::move(ofcIter),
1096
30
                                                          std::move(item_iterator),
1097
30
                                                          std::move(null_iterator));
1098
30
    return Status::OK();
1099
30
}
1100
1101
Status ColumnReader::new_map_iterator(ColumnIteratorUPtr* iterator,
1102
282
                                      const TabletColumn* tablet_column) {
1103
282
    ColumnIteratorUPtr key_iterator;
1104
282
    RETURN_IF_ERROR(_sub_readers[0]->new_iterator(
1105
282
            &key_iterator, tablet_column && tablet_column->get_subtype_count() > 1
1106
282
                                   ? &tablet_column->get_sub_column(0)
1107
282
                                   : nullptr));
1108
282
    ColumnIteratorUPtr val_iterator;
1109
282
    RETURN_IF_ERROR(_sub_readers[1]->new_iterator(
1110
282
            &val_iterator, tablet_column && tablet_column->get_subtype_count() > 1
1111
282
                                   ? &tablet_column->get_sub_column(1)
1112
282
                                   : nullptr));
1113
282
    ColumnIteratorUPtr offsets_iterator;
1114
282
    RETURN_IF_ERROR(_sub_readers[2]->new_iterator(&offsets_iterator, nullptr));
1115
282
    auto* file_iter = static_cast<FileColumnIterator*>(offsets_iterator.release());
1116
282
    OffsetFileColumnIteratorUPtr ofcIter = std::make_unique<OffsetFileColumnIterator>(
1117
282
            std::unique_ptr<FileColumnIterator>(file_iter));
1118
1119
282
    ColumnIteratorUPtr null_iterator;
1120
282
    if (is_nullable()) {
1121
0
        RETURN_IF_ERROR(_sub_readers[3]->new_iterator(&null_iterator, nullptr));
1122
0
    }
1123
282
    *iterator = std::make_unique<MapFileColumnIterator>(
1124
282
            shared_from_this(), std::move(null_iterator), std::move(ofcIter),
1125
282
            std::move(key_iterator), std::move(val_iterator));
1126
282
    return Status::OK();
1127
282
}
1128
1129
Status ColumnReader::new_struct_iterator(ColumnIteratorUPtr* iterator,
1130
0
                                         const TabletColumn* tablet_column) {
1131
0
    std::vector<ColumnIteratorUPtr> sub_column_iterators;
1132
0
    size_t child_size = is_nullable() ? _sub_readers.size() - 1 : _sub_readers.size();
1133
0
    size_t tablet_column_size = tablet_column ? tablet_column->get_sub_columns().size() : 0;
1134
0
    sub_column_iterators.reserve(child_size);
1135
1136
0
    for (uint64_t i = 0; i < child_size; i++) {
1137
0
        ColumnIteratorUPtr sub_column_iterator;
1138
0
        RETURN_IF_ERROR(_sub_readers[i]->new_iterator(
1139
0
                &sub_column_iterator, tablet_column ? &tablet_column->get_sub_column(i) : nullptr));
1140
0
        sub_column_iterator->set_column_name(tablet_column ? tablet_column->get_sub_column(i).name()
1141
0
                                                           : "");
1142
0
        sub_column_iterators.emplace_back(std::move(sub_column_iterator));
1143
0
    }
1144
1145
    // create default_iterator for schema-change behavior which increase column
1146
0
    for (size_t i = child_size; i < tablet_column_size; i++) {
1147
0
        TabletColumn column = tablet_column->get_sub_column(i);
1148
0
        ColumnIteratorUPtr it;
1149
0
        RETURN_IF_ERROR(Segment::new_default_iterator(column, &it));
1150
0
        it->set_column_name(column.name());
1151
0
        sub_column_iterators.emplace_back(std::move(it));
1152
0
    }
1153
1154
0
    ColumnIteratorUPtr null_iterator;
1155
0
    if (is_nullable()) {
1156
0
        RETURN_IF_ERROR(_sub_readers[child_size]->new_iterator(&null_iterator, nullptr));
1157
0
    }
1158
0
    *iterator = std::make_unique<StructFileColumnIterator>(
1159
0
            shared_from_this(), std::move(null_iterator), std::move(sub_column_iterators));
1160
0
    return Status::OK();
1161
0
}
1162
1163
10
void ColumnIterator::_convert_to_place_holder_column(MutableColumnPtr& dst, size_t count) {
1164
10
    if (_read_phase == ReadPhase::LAZY) {
1165
3
        return;
1166
7
    } else if (_read_requirement == ReadRequirement::LAZY_OUTPUT &&
1167
7
               _read_phase == ReadPhase::PREDICATE) {
1168
        // This branch is for non-predicate columns that still have to appear in the
1169
        // predicate-phase block so row filtering can keep all block columns aligned.
1170
        // Columns already marked PREDICATE are read normally, and SKIP/NORMAL
1171
        // columns do not participate in lazy materialization.
1172
6
        _has_place_holder_column = true;
1173
6
    }
1174
1175
7
    dst->insert_many_defaults(count);
1176
7
}
1177
1178
24.3k
void ColumnIterator::_recovery_from_place_holder_column(MutableColumnPtr& dst) {
1179
24.3k
    if (_read_phase == ReadPhase::LAZY && _has_place_holder_column) {
1180
6
        dst->clear();
1181
6
        _has_place_holder_column = false;
1182
6
    }
1183
24.3k
}
1184
1185
Result<ColumnIterator::AccessPathSplit> ColumnIterator::_split_access_paths(
1186
334
        TColumnAccessPaths access_paths) const {
1187
334
    AccessPathSplit split;
1188
334
    for (auto& path : access_paths) {
1189
276
        const bool uses_legacy_encoding = uses_legacy_access_path_encoding(path);
1190
276
        if (!uses_legacy_encoding &&
1191
276
            path.version != g_Descriptors_constants.TCOLUMN_ACCESS_PATH_VERSION_TYPED) {
1192
1
            return ResultError(
1193
1
                    Status::InternalError("Unsupported access path version: {}", path.version));
1194
1
        }
1195
1196
275
        std::vector<std::string>* components = nullptr;
1197
275
        if (uses_legacy_encoding) {
1198
30
            if (path.type != TAccessPathType::DATA) {
1199
3
                return ResultError(Status::InternalError("Invalid legacy access path type: {}",
1200
3
                                                         static_cast<int>(path.type)));
1201
3
            }
1202
27
            if (!path.__isset.data_access_path) {
1203
1
                return ResultError(Status::InternalError(
1204
1
                        "Invalid legacy access path: data_access_path payload is not set"));
1205
1
            }
1206
26
            components = &path.data_access_path.path;
1207
245
        } else {
1208
245
            switch (path.type) {
1209
170
            case TAccessPathType::DATA:
1210
170
                if (!path.__isset.data_access_path) {
1211
1
                    return ResultError(Status::InternalError(
1212
1
                            "Invalid DATA access path: data_access_path payload is not set"));
1213
1
                }
1214
169
                components = &path.data_access_path.path;
1215
169
                break;
1216
74
            case TAccessPathType::META:
1217
74
                if (!path.__isset.meta_access_path) {
1218
1
                    return ResultError(Status::InternalError(
1219
1
                            "Invalid META access path: meta_access_path payload is not set"));
1220
1
                }
1221
73
                components = &path.meta_access_path.path;
1222
73
                break;
1223
1
            default:
1224
1
                return ResultError(Status::InternalError("Invalid access path type: {}",
1225
1
                                                         static_cast<int>(path.type)));
1226
245
            }
1227
245
        }
1228
1229
268
        if (components->empty()) {
1230
1
            return ResultError(Status::InternalError(
1231
1
                    "Invalid access path for column '{}': path is empty", _column_name));
1232
1
        }
1233
1234
267
        if (!StringCaseEqual()((*components)[0], _column_name)) {
1235
4
            return ResultError(Status::InternalError(
1236
4
                    R"(Invalid access path for column: expected name "{}", got "{}")", _column_name,
1237
4
                    (*components)[0]));
1238
4
        }
1239
1240
263
        components->erase(components->begin());
1241
263
        if (components->empty()) {
1242
61
            split.reads_current_data = true;
1243
61
            continue;
1244
61
        }
1245
1246
202
        const bool is_current_level_meta =
1247
202
                components->size() == 1 && is_meta_access_path_component((*components)[0]) &&
1248
202
                (path.type == TAccessPathType::META || uses_legacy_encoding);
1249
202
        if (is_current_level_meta) {
1250
64
            if (StringCaseEqual()((*components)[0], ACCESS_OFFSET)) {
1251
16
                split.current_meta_mode = MetaReadMode::OFFSET_ONLY;
1252
48
            } else if (split.current_meta_mode == MetaReadMode::DEFAULT) {
1253
45
                split.current_meta_mode = MetaReadMode::NULL_MAP_ONLY;
1254
45
            }
1255
64
            continue;
1256
64
        }
1257
1258
138
        split.descendant_paths.emplace_back(std::move(path));
1259
138
    }
1260
321
    return split;
1261
334
}
1262
1263
Result<ColumnIterator::NestedAccessPathPlan> ColumnIterator::_prepare_nested_access_paths(
1264
        const TColumnAccessPaths& all_access_paths,
1265
        const TColumnAccessPaths& predicate_access_paths, NestedMetaSupport meta_support,
1266
109
        const std::function<void(ReadRequirement)>& set_all_data_descendants_read_requirement) {
1267
109
    const auto requirement_before = _read_requirement;
1268
109
    if (!predicate_access_paths.empty()) {
1269
73
        set_read_requirement_self(ReadRequirement::PREDICATE);
1270
73
    }
1271
1272
109
    NestedAccessPathPlan plan;
1273
109
    auto all_split = _split_access_paths(all_access_paths);
1274
109
    if (!all_split.has_value()) {
1275
5
        return ResultError(std::move(all_split).error());
1276
5
    }
1277
104
    plan.all = std::move(all_split).value();
1278
1279
104
    auto predicate_split = _split_access_paths(predicate_access_paths);
1280
104
    if (!predicate_split.has_value()) {
1281
0
        return ResultError(std::move(predicate_split).error());
1282
0
    }
1283
104
    plan.predicate = std::move(predicate_split).value();
1284
104
    if (plan.all.reads_current_data) {
1285
16
        set_lazy_output_requirement();
1286
16
    }
1287
104
    if (plan.predicate.reads_current_data) {
1288
6
        set_read_requirement(ReadRequirement::PREDICATE);
1289
6
    }
1290
1291
104
    if (!plan.predicate.has_descendant_paths()) {
1292
48
        RETURN_IF_ERROR_RESULT(_check_and_set_meta_read_mode(requirement_before, plan.all));
1293
48
        plan.skip_data_descendants =
1294
48
                read_null_map_only() ||
1295
48
                (meta_support == NestedMetaSupport::NULL_MAP_AND_OFFSET && read_offset_only());
1296
48
        if (plan.skip_data_descendants) {
1297
10
            set_all_data_descendants_read_requirement(ReadRequirement::SKIP);
1298
10
        }
1299
48
    }
1300
104
    return plan;
1301
104
}
1302
1303
///====================== MapFileColumnIterator ============================////
1304
MapFileColumnIterator::MapFileColumnIterator(std::shared_ptr<ColumnReader> reader,
1305
                                             ColumnIteratorUPtr null_iterator,
1306
                                             OffsetFileColumnIteratorUPtr offsets_iterator,
1307
                                             ColumnIteratorUPtr key_iterator,
1308
                                             ColumnIteratorUPtr val_iterator)
1309
325
        : _map_reader(reader),
1310
325
          _offsets_iterator(std::move(offsets_iterator)),
1311
325
          _key_iterator(std::move(key_iterator)),
1312
325
          _val_iterator(std::move(val_iterator)) {
1313
325
    if (_map_reader->is_nullable()) {
1314
2
        _null_iterator = std::move(null_iterator);
1315
2
    }
1316
    // Access paths identify map children by logical selectors rather than storage/schema child
1317
    // names. These names are consumed by each child's _split_access_paths().
1318
325
    _key_iterator->set_column_name(ACCESS_MAP_KEYS);
1319
325
    _val_iterator->set_column_name(ACCESS_MAP_VALUES);
1320
325
}
1321
1322
278
Status MapFileColumnIterator::init(const ColumnIteratorOptions& opts) {
1323
278
    if (_read_requirement == ReadRequirement::SKIP) {
1324
0
        DLOG(INFO) << "Map column iterator column " << _column_name << " skip reading.";
1325
0
        return Status::OK();
1326
0
    }
1327
278
    RETURN_IF_ERROR(_key_iterator->init(opts));
1328
278
    RETURN_IF_ERROR(_val_iterator->init(opts));
1329
278
    RETURN_IF_ERROR(_offsets_iterator->init(opts));
1330
278
    if (_map_reader->is_nullable()) {
1331
0
        RETURN_IF_ERROR(_null_iterator->init(opts));
1332
0
    }
1333
278
    return Status::OK();
1334
278
}
1335
1336
266
Status MapFileColumnIterator::seek_to_ordinal(ordinal_t ord) {
1337
266
    if (!need_to_read()) {
1338
0
        DLOG(INFO) << "Map column iterator column " << _column_name << " skip reading.";
1339
0
        return Status::OK();
1340
0
    }
1341
1342
266
    if (read_null_map_only()) {
1343
        // In NULL_MAP_ONLY mode, only seek the null iterator; skip offset/key/val iterators
1344
0
        if (_map_reader->is_nullable() && _null_iterator) {
1345
0
            RETURN_IF_ERROR(_null_iterator->seek_to_ordinal(ord));
1346
0
        }
1347
0
        return Status::OK();
1348
0
    }
1349
1350
266
    if (_map_reader->is_nullable()) {
1351
0
        RETURN_IF_ERROR(_null_iterator->seek_to_ordinal(ord));
1352
0
    }
1353
266
    RETURN_IF_ERROR(_offsets_iterator->seek_to_ordinal(ord));
1354
266
    if (read_offset_only()) {
1355
        // In OFFSET_ONLY mode, key/value iterators are SKIP, no need to seek them
1356
0
        return Status::OK();
1357
0
    }
1358
    // here to use offset info
1359
266
    ordinal_t offset = 0;
1360
266
    RETURN_IF_ERROR(_offsets_iterator->_peek_one_offset(&offset));
1361
266
    RETURN_IF_ERROR(_key_iterator->seek_to_ordinal(offset));
1362
266
    RETURN_IF_ERROR(_val_iterator->seek_to_ordinal(offset));
1363
266
    return Status::OK();
1364
266
}
1365
1366
0
Status MapFileColumnIterator::init_prefetcher(const SegmentPrefetchParams& params) {
1367
0
    RETURN_IF_ERROR(_offsets_iterator->init_prefetcher(params));
1368
0
    if (_map_reader->is_nullable()) {
1369
0
        RETURN_IF_ERROR(_null_iterator->init_prefetcher(params));
1370
0
    }
1371
0
    RETURN_IF_ERROR(_key_iterator->init_prefetcher(params));
1372
0
    RETURN_IF_ERROR(_val_iterator->init_prefetcher(params));
1373
0
    return Status::OK();
1374
0
}
1375
1376
void MapFileColumnIterator::collect_prefetchers(
1377
        std::map<PrefetcherInitMethod, std::vector<SegmentPrefetcher*>>& prefetchers,
1378
1
        PrefetcherInitMethod init_method) {
1379
1
    if (!need_to_read()) {
1380
0
        return;
1381
0
    }
1382
1
    if (!read_null_map_only()) {
1383
1
        _offsets_iterator->collect_prefetchers(prefetchers, init_method);
1384
1
    }
1385
1
    if (_map_reader->is_nullable()) {
1386
1
        _null_iterator->collect_prefetchers(prefetchers, init_method);
1387
1
    }
1388
1
    if (read_offset_only() || read_null_map_only()) {
1389
0
        return;
1390
0
    }
1391
    // the actual data pages to read of key/value column depends on the read result of offset column,
1392
    // so we can't init prefetch blocks according to rowids, just prefetch all data blocks here.
1393
1
    if (_key_iterator->need_to_read()) {
1394
1
        _key_iterator->collect_prefetchers(prefetchers, PrefetcherInitMethod::ALL_DATA_BLOCKS);
1395
1
    }
1396
1
    if (_val_iterator->need_to_read()) {
1397
1
        _val_iterator->collect_prefetchers(prefetchers, PrefetcherInitMethod::ALL_DATA_BLOCKS);
1398
1
    }
1399
1
}
1400
1401
266
Status MapFileColumnIterator::next_batch(size_t* n, MutableColumnPtr& dst, bool* has_null) {
1402
266
    if (!need_to_read()) {
1403
0
        DLOG(INFO) << "Map column iterator column " << _column_name << " skip reading.";
1404
0
        _convert_to_place_holder_column(dst, *n);
1405
0
        return Status::OK();
1406
0
    }
1407
1408
266
    _recovery_from_place_holder_column(dst);
1409
1410
266
    if (read_null_map_only()) {
1411
        // NULL_MAP_ONLY mode: read null map, fill nested ColumnMap with empty defaults
1412
1
        DORIS_CHECK(is_column_nullable(*dst));
1413
1
        auto& nullable_col = assert_cast<ColumnNullable&>(*dst);
1414
1
        auto null_map_ptr = nullable_col.get_null_map_column_ptr();
1415
1
        size_t num_read = *n;
1416
1
        if (_null_iterator) {
1417
1
            bool null_signs_has_null = false;
1418
1
            MutableColumnPtr null_map_column = std::move(null_map_ptr);
1419
1
            RETURN_IF_ERROR(
1420
1
                    _null_iterator->next_batch(&num_read, null_map_column, &null_signs_has_null));
1421
1
        } else {
1422
            // schema-change: column became nullable but old segment has no null data
1423
0
            null_map_ptr->insert_many_vals(0, num_read);
1424
0
        }
1425
1
        DCHECK(num_read == *n);
1426
        // fill nested ColumnMap with empty (zero-element) maps
1427
1
        auto& column_map = assert_cast<ColumnMap&, TypeCheckOnRelease::DISABLE>(
1428
1
                nullable_col.get_nested_column());
1429
1
        column_map.insert_many_defaults(num_read);
1430
1
        *has_null = true;
1431
1
        return Status::OK();
1432
1
    }
1433
1434
265
    auto& column_map = assert_cast<ColumnMap&, TypeCheckOnRelease::DISABLE>(
1435
265
            is_column_nullable(*dst) ? static_cast<ColumnNullable&>(*dst).get_nested_column()
1436
265
                                     : *dst);
1437
265
    const bool read_meta_columns = need_to_read_meta_columns();
1438
265
    MutableColumnPtr column_offsets_ptr;
1439
265
    if (read_meta_columns) {
1440
265
        column_offsets_ptr = IColumn::mutate(std::move(column_map.get_offsets_ptr()));
1441
265
    } else {
1442
        // The parent offsets were already materialized in the predicate phase, so
1443
        // they must not be appended to dst again. We still read offsets into a
1444
        // temporary column here: this sequential path may be serving a nested
1445
        // lazy read after seek_to_ordinal(), and the storage offsets are needed to
1446
        // compute how many key/value elements to read from the current source
1447
        // ordinal. The existing dst offsets only describe the filtered output
1448
        // shape and do not track the current source ordinal consumed by this
1449
        // iterator call.
1450
0
        const auto base_offset =
1451
0
                column_map.get_offsets().empty() ? 0 : column_map.get_offsets().back();
1452
0
        column_offsets_ptr = ColumnMap::COffsets::create();
1453
0
        assert_cast<ColumnOffset64&, TypeCheckOnRelease::DISABLE>(*column_offsets_ptr)
1454
0
                .insert_value(base_offset);
1455
0
    }
1456
265
    Defer defer_offsets {[&] {
1457
265
        if (read_meta_columns) {
1458
265
            auto typed_column_offsets_ptr = ColumnMap::COffsets::cast_to_column_mutptr(
1459
265
                    assert_cast<ColumnMap::COffsets*, TypeCheckOnRelease::DISABLE>(
1460
265
                            column_offsets_ptr.get()));
1461
265
            column_offsets_ptr = nullptr;
1462
265
            column_map.get_offsets_ptr() = std::move(typed_column_offsets_ptr);
1463
265
        }
1464
265
    }};
1465
265
    bool offsets_has_null = false;
1466
265
    ssize_t start = column_offsets_ptr->size();
1467
265
    RETURN_IF_ERROR(_offsets_iterator->next_batch(n, column_offsets_ptr, &offsets_has_null));
1468
265
    if (*n == 0) {
1469
0
        return Status::OK();
1470
0
    }
1471
265
    auto& column_offsets = static_cast<ColumnArray::ColumnOffsets&>(*column_offsets_ptr);
1472
265
    RETURN_IF_ERROR(_offsets_iterator->_calculate_offsets(start, column_offsets));
1473
265
    DCHECK(column_offsets.get_data().back() >= column_offsets.get_data()[start - 1]);
1474
265
    size_t num_items =
1475
265
            column_offsets.get_data().back() - column_offsets.get_data()[start - 1]; // -1 is valid
1476
1477
265
    if (num_items > 0) {
1478
204
        auto key_ptr = IColumn::mutate(std::move(column_map.get_keys_ptr()));
1479
204
        auto val_ptr = IColumn::mutate(std::move(column_map.get_values_ptr()));
1480
204
        Defer defer_keys {[&] { column_map.get_keys_ptr() = std::move(key_ptr); }};
1481
204
        Defer defer_values {[&] { column_map.get_values_ptr() = std::move(val_ptr); }};
1482
204
        if (read_offset_only()) {
1483
            // OFFSET_ONLY mode: skip reading actual key/value data, fill with defaults
1484
0
            key_ptr->insert_many_defaults(num_items);
1485
0
            val_ptr->insert_many_defaults(num_items);
1486
204
        } else {
1487
204
            auto read_or_fill_child = [&](ColumnIterator* iterator,
1488
408
                                          MutableColumnPtr& column) -> Status {
1489
408
                if (_read_phase == ReadPhase::LAZY && read_meta_columns &&
1490
408
                    iterator->read_requirement() == ReadRequirement::SKIP) {
1491
0
                    column->insert_many_defaults(num_items);
1492
0
                    return Status::OK();
1493
0
                }
1494
1495
408
                bool dummy_has_null = false;
1496
408
                size_t num_read = num_items;
1497
408
                RETURN_IF_ERROR(iterator->next_batch(&num_read, column, &dummy_has_null));
1498
408
                DCHECK(num_read == num_items);
1499
408
                return Status::OK();
1500
408
            };
1501
204
            RETURN_IF_ERROR(read_or_fill_child(_key_iterator.get(), key_ptr));
1502
204
            RETURN_IF_ERROR(read_or_fill_child(_val_iterator.get(), val_ptr));
1503
204
        }
1504
204
    }
1505
1506
265
    if (is_column_nullable(*dst) && read_meta_columns) {
1507
0
        size_t num_read = *n;
1508
0
        auto null_map_ptr = static_cast<ColumnNullable&>(*dst).get_null_map_column_ptr();
1509
        // in not-null to null linked-schemachange mode,
1510
        // actually we do not change dat data include meta in footer,
1511
        // so may dst from changed meta which is nullable but old data is not nullable,
1512
        // if so, we should set null_map to all null by default
1513
0
        if (_null_iterator) {
1514
0
            bool null_signs_has_null = false;
1515
0
            MutableColumnPtr null_map_column = std::move(null_map_ptr);
1516
0
            RETURN_IF_ERROR(
1517
0
                    _null_iterator->next_batch(&num_read, null_map_column, &null_signs_has_null));
1518
0
        } else {
1519
0
            null_map_ptr->insert_many_vals(0, num_read);
1520
0
        }
1521
0
        DCHECK(num_read == *n);
1522
0
    }
1523
265
    return Status::OK();
1524
265
}
1525
1526
Status MapFileColumnIterator::read_by_rowids(const rowid_t* rowids, const size_t count,
1527
25
                                             MutableColumnPtr& dst) {
1528
25
    if (!need_to_read()) {
1529
1
        DLOG(INFO) << "Map column iterator column " << _column_name << " skip reading.";
1530
1
        _convert_to_place_holder_column(dst, count);
1531
1
        return Status::OK();
1532
1
    }
1533
1534
24
    _recovery_from_place_holder_column(dst);
1535
1536
24
    if (read_null_map_only()) {
1537
        // NULL_MAP_ONLY mode: read null map by rowids, fill nested ColumnMap with empty defaults
1538
1
        DORIS_CHECK(is_column_nullable(*dst));
1539
1
        auto& nullable_col = assert_cast<ColumnNullable&>(*dst);
1540
1
        if (_null_iterator) {
1541
1
            auto null_map_ptr = nullable_col.get_null_map_column_ptr();
1542
1
            MutableColumnPtr null_map_column = std::move(null_map_ptr);
1543
1
            RETURN_IF_ERROR(_null_iterator->read_by_rowids(rowids, count, null_map_column));
1544
1
        } else {
1545
            // schema-change: column became nullable but old segment has no null data
1546
0
            auto null_map_ptr = nullable_col.get_null_map_column_ptr();
1547
0
            null_map_ptr->insert_many_vals(0, count);
1548
0
        }
1549
        // fill nested ColumnMap with empty (zero-element) maps
1550
1
        auto& column_map = assert_cast<ColumnMap&, TypeCheckOnRelease::DISABLE>(
1551
1
                nullable_col.get_nested_column());
1552
1
        column_map.insert_many_defaults(count);
1553
1
        return Status::OK();
1554
1
    }
1555
1556
23
    if (count == 0) {
1557
0
        return Status::OK();
1558
0
    }
1559
1560
    // resolve ColumnMap and nullable wrapper
1561
23
    auto& column_map = assert_cast<ColumnMap&, TypeCheckOnRelease::DISABLE>(
1562
23
            is_column_nullable(*dst) ? static_cast<ColumnNullable&>(*dst).get_nested_column()
1563
23
                                     : *dst);
1564
23
    const bool read_meta_columns = need_to_read_meta_columns();
1565
23
    MutableColumnPtr offsets_ptr;
1566
23
    if (read_meta_columns) {
1567
22
        offsets_ptr = IColumn::mutate(std::move(column_map.get_offsets_ptr()));
1568
22
    } else {
1569
1
        const auto base_offset =
1570
1
                column_map.get_offsets().empty() ? 0 : column_map.get_offsets().back();
1571
1
        offsets_ptr = ColumnMap::COffsets::create();
1572
1
        assert_cast<ColumnOffset64&, TypeCheckOnRelease::DISABLE>(*offsets_ptr)
1573
1
                .insert_value(base_offset);
1574
1
    }
1575
23
    Defer defer_offsets {[&] {
1576
23
        if (read_meta_columns) {
1577
22
            auto typed_offsets_ptr = ColumnMap::COffsets::cast_to_column_mutptr(
1578
22
                    assert_cast<ColumnMap::COffsets*, TypeCheckOnRelease::DISABLE>(
1579
22
                            offsets_ptr.get()));
1580
22
            offsets_ptr = nullptr;
1581
22
            column_map.get_offsets_ptr() = std::move(typed_offsets_ptr);
1582
22
        }
1583
23
    }};
1584
23
    auto& offsets = static_cast<ColumnArray::ColumnOffsets&>(*offsets_ptr);
1585
23
    size_t base = offsets.get_data().empty() ? 0 : offsets.get_data().back();
1586
1587
    // 1. bulk read null-map if nullable
1588
23
    std::vector<uint8_t> null_mask; // 0: not null, 1: null
1589
23
    if (read_meta_columns) {
1590
22
        if (_map_reader->is_nullable()) {
1591
            // For nullable map columns, the destination column must also be nullable.
1592
0
            if (UNLIKELY(!is_column_nullable(*dst))) {
1593
0
                return Status::InternalError(
1594
0
                        "unexpected non-nullable destination column for nullable map reader");
1595
0
            }
1596
0
            MutableColumnPtr null_map_ptr =
1597
0
                    static_cast<ColumnNullable&>(*dst).get_null_map_column_ptr();
1598
0
            size_t null_before = null_map_ptr->size();
1599
0
            RETURN_IF_ERROR(_null_iterator->read_by_rowids(rowids, count, null_map_ptr));
1600
            // extract a light-weight view to decide element reads
1601
0
            auto& null_map_col = assert_cast<ColumnUInt8&>(*null_map_ptr);
1602
0
            const auto* src = null_map_col.get_data().data() + null_before;
1603
0
            null_mask.assign(src, src + count);
1604
22
        } else if (is_column_nullable(*dst)) {
1605
            // in not-null to null linked-schemachange mode,
1606
            // actually we do not change dat data include meta in footer,
1607
            // so may dst from changed meta which is nullable but old data is not nullable,
1608
            // if so, we should set null_map to all null by default
1609
0
            MutableColumnPtr null_map_ptr =
1610
0
                    static_cast<ColumnNullable&>(*dst).get_null_map_column_ptr();
1611
0
            auto& null_map = assert_cast<ColumnUInt8&>(*null_map_ptr);
1612
0
            null_map.insert_many_vals(0, count);
1613
0
        }
1614
22
    } else if (_map_reader->is_nullable()) {
1615
        // In lazy mode the parent null map has already been materialized during
1616
        // predicate read and filtered together with the block. Reuse that dst
1617
        // null map to avoid re-reading the same meta column from storage.
1618
0
        if (UNLIKELY(!is_column_nullable(*dst))) {
1619
0
            return Status::InternalError(
1620
0
                    "unexpected non-nullable destination column for nullable map reader");
1621
0
        }
1622
0
        const auto& null_map_col = static_cast<const ColumnNullable&>(*dst).get_null_map_column();
1623
0
        DORIS_CHECK(null_map_col.size() == count);
1624
0
        const auto* src = null_map_col.get_data().data();
1625
0
        null_mask.assign(src, src + count);
1626
0
    }
1627
1628
    // 2. Bulk read source start ordinals for requested rows. The offsets stored
1629
    // in dst already describe the filtered output shape when read_meta_columns is
1630
    // false, but they do not contain the source key/value ordinal for each
1631
    // selected rowid. We still need the storage offsets here to seek child
1632
    // iterators to the correct source element ranges.
1633
23
    MutableColumnPtr starts_col = ColumnOffset64::create();
1634
23
    starts_col->reserve(count);
1635
23
    RETURN_IF_ERROR(_offsets_iterator->read_by_rowids(rowids, count, starts_col));
1636
1637
    // 3. bulk read next-start ordinals for rowid+1 (within bounds)
1638
23
    std::vector<rowid_t> next_rowids(count);
1639
3.04k
    for (size_t i = 0; i < count; ++i) {
1640
3.01k
        uint64_t nr = rowids[i] + 1;
1641
3.01k
        next_rowids[i] = nr < _map_reader->num_rows() ? static_cast<rowid_t>(nr)
1642
3.01k
                                                      : static_cast<rowid_t>(0); // placeholder
1643
3.01k
    }
1644
23
    MutableColumnPtr next_starts_col = ColumnOffset64::create();
1645
23
    next_starts_col->reserve(count);
1646
    // read for all; we'll fix out-of-bound cases below
1647
23
    RETURN_IF_ERROR(_offsets_iterator->read_by_rowids(next_rowids.data(), count, next_starts_col));
1648
1649
    // 4. fix next_start for rows whose next_rowid is out-of-bound (rowid == num_rows-1)
1650
3.04k
    for (size_t i = 0; i < count; ++i) {
1651
3.01k
        if (rowids[i] + 1 >= _map_reader->num_rows()) {
1652
            // seek to the last row and consume one to move decoder to end-of-page,
1653
            // then peek page-tail sentinel next_array_item_ordinal as next_start
1654
13
            RETURN_IF_ERROR(_offsets_iterator->seek_to_ordinal(rowids[i]));
1655
13
            size_t one = 1;
1656
13
            bool has_null_unused = false;
1657
13
            MutableColumnPtr tmp = ColumnOffset64::create();
1658
13
            RETURN_IF_ERROR(_offsets_iterator->next_batch(&one, tmp, &has_null_unused));
1659
13
            ordinal_t ns = 0;
1660
13
            RETURN_IF_ERROR(_offsets_iterator->_peek_one_offset(&ns));
1661
            // overwrite with sentinel
1662
13
            assert_cast<ColumnOffset64&, TypeCheckOnRelease::DISABLE>(*next_starts_col)
1663
13
                    .get_data()[i] = ns;
1664
13
        }
1665
3.01k
    }
1666
1667
    // 5. compute sizes and append offsets prefix-sum
1668
23
    auto& starts_data = assert_cast<ColumnOffset64&>(*starts_col).get_data();
1669
23
    auto& next_starts_data = assert_cast<ColumnOffset64&>(*next_starts_col).get_data();
1670
23
    std::vector<size_t> sizes(count, 0);
1671
23
    size_t acc = base;
1672
23
    if (read_meta_columns) {
1673
22
        offsets.get_data().reserve(offsets.get_data().size() + count);
1674
22
    }
1675
3.04k
    for (size_t i = 0; i < count; ++i) {
1676
3.01k
        auto sz = static_cast<size_t>(next_starts_data[i] - starts_data[i]);
1677
3.01k
        if (_map_reader->is_nullable() && !null_mask.empty() && null_mask[i]) {
1678
0
            sz = 0; // null rows do not consume elements
1679
0
        }
1680
3.01k
        sizes[i] = sz;
1681
3.01k
        acc += sz;
1682
3.01k
        if (read_meta_columns) {
1683
3.01k
            offsets.get_data().push_back(acc);
1684
3.01k
        }
1685
3.01k
    }
1686
1687
    // 6. read key/value elements for non-empty sizes
1688
23
    auto keys_ptr = IColumn::mutate(std::move(column_map.get_keys_ptr()));
1689
23
    auto vals_ptr = IColumn::mutate(std::move(column_map.get_values_ptr()));
1690
23
    Defer defer_keys {[&] { column_map.get_keys_ptr() = std::move(keys_ptr); }};
1691
23
    Defer defer_values {[&] { column_map.get_values_ptr() = std::move(vals_ptr); }};
1692
    // In lazy phase with read_meta_columns=true, this map was only a placeholder during
1693
    // predicate evaluation and is cleared before the lazy read. If only KEYS or VALUES is
1694
    // requested, fill the skipped counterpart with defaults to keep ColumnMap's
1695
    // key/value/offset sizes consistent without reading unnecessary data pages.
1696
23
    const bool fill_lazy_skipped_keys = _read_phase == ReadPhase::LAZY && read_meta_columns &&
1697
23
                                        _key_iterator->read_requirement() == ReadRequirement::SKIP;
1698
23
    const bool fill_lazy_skipped_values =
1699
23
            _read_phase == ReadPhase::LAZY && read_meta_columns &&
1700
23
            _val_iterator->read_requirement() == ReadRequirement::SKIP;
1701
23
    auto read_or_fill_range = [&](ColumnIterator* iterator, MutableColumnPtr& column,
1702
23
                                  ordinal_t start_idx, size_t num_items,
1703
592
                                  bool fill_lazy_skipped_child) -> Status {
1704
592
        if (num_items == 0) {
1705
36
            return Status::OK();
1706
36
        }
1707
556
        if (fill_lazy_skipped_child) {
1708
1
            column->insert_many_defaults(num_items);
1709
1
            return Status::OK();
1710
1
        }
1711
555
        if (_read_phase == ReadPhase::LAZY && !iterator->need_to_read()) {
1712
1
            return Status::OK();
1713
1
        }
1714
554
        size_t n = num_items;
1715
554
        bool dummy_has_null = false;
1716
554
        RETURN_IF_ERROR(iterator->seek_to_ordinal(start_idx));
1717
554
        RETURN_IF_ERROR(iterator->next_batch(&n, column, &dummy_has_null));
1718
554
        DCHECK(n == num_items);
1719
554
        return Status::OK();
1720
554
    };
1721
1722
23
    size_t this_run = sizes[0];
1723
23
    auto start_idx = starts_data[0];
1724
23
    auto last_idx = starts_data[0] + this_run;
1725
3.01k
    for (size_t i = 1; i < count; ++i) {
1726
2.99k
        size_t sz = sizes[i];
1727
2.99k
        if (sz == 0) {
1728
2.71k
            continue;
1729
2.71k
        }
1730
278
        auto start = static_cast<ordinal_t>(starts_data[i]);
1731
278
        if (start != last_idx) {
1732
273
            RETURN_IF_ERROR(read_or_fill_range(_key_iterator.get(), keys_ptr, start_idx, this_run,
1733
273
                                               fill_lazy_skipped_keys));
1734
273
            RETURN_IF_ERROR(read_or_fill_range(_val_iterator.get(), vals_ptr, start_idx, this_run,
1735
273
                                               fill_lazy_skipped_values));
1736
273
            start_idx = start;
1737
273
            this_run = sz;
1738
273
            last_idx = start + sz;
1739
273
            continue;
1740
273
        }
1741
1742
5
        this_run += sz;
1743
5
        last_idx += sz;
1744
5
    }
1745
1746
23
    RETURN_IF_ERROR(read_or_fill_range(_key_iterator.get(), keys_ptr, start_idx, this_run,
1747
23
                                       fill_lazy_skipped_keys));
1748
23
    RETURN_IF_ERROR(read_or_fill_range(_val_iterator.get(), vals_ptr, start_idx, this_run,
1749
23
                                       fill_lazy_skipped_values));
1750
23
    return Status::OK();
1751
23
}
1752
1753
7
void MapFileColumnIterator::set_lazy_output_requirement() {
1754
7
    set_read_requirement_self(ReadRequirement::LAZY_OUTPUT);
1755
7
    _key_iterator->set_lazy_output_requirement();
1756
7
    _val_iterator->set_lazy_output_requirement();
1757
7
}
1758
1759
0
void MapFileColumnIterator::remove_pruned_sub_iterators() {
1760
0
    _key_iterator->remove_pruned_sub_iterators();
1761
0
    _val_iterator->remove_pruned_sub_iterators();
1762
0
}
1763
1764
Status MapFileColumnIterator::set_access_paths(const TColumnAccessPaths& all_access_paths,
1765
34
                                               const TColumnAccessPaths& predicate_access_paths) {
1766
34
    if (all_access_paths.empty() && predicate_access_paths.empty()) {
1767
0
        return Status::OK();
1768
0
    }
1769
1770
34
    auto plan = DORIS_TRY(_prepare_nested_access_paths(
1771
33
            all_access_paths, predicate_access_paths, NestedMetaSupport::NULL_MAP_AND_OFFSET,
1772
33
            [this](ReadRequirement requirement) {
1773
33
                _key_iterator->set_read_requirement(requirement);
1774
33
                _val_iterator->set_read_requirement(requirement);
1775
33
            }));
1776
33
    if (plan.skip_data_descendants) {
1777
3
        return Status::OK();
1778
3
    }
1779
1780
30
    if (!plan.all.has_descendant_paths() && !plan.predicate.has_descendant_paths()) {
1781
2
        return Status::OK();
1782
2
    }
1783
1784
28
    auto child_paths = DORIS_TRY(DescendantAccessPathRouter::route_map_paths_to_children(
1785
28
            std::move(plan.all.descendant_paths), std::move(plan.predicate.descendant_paths)));
1786
1787
28
    if (!child_paths.key.empty()) {
1788
16
        RETURN_IF_ERROR(_key_iterator->set_access_paths(child_paths.key.all_paths,
1789
16
                                                        child_paths.key.predicate_paths));
1790
        // Apply LAZY_OUTPUT after child predicate paths have been handled. Read requirements are
1791
        // monotonic, so a predicate-only child already promoted to PREDICATE will not
1792
        // be downgraded, while a non-predicate child becomes a lazy materialization target.
1793
16
        _key_iterator->set_read_requirement_self(ReadRequirement::LAZY_OUTPUT);
1794
16
    } else {
1795
12
        _key_iterator->set_read_requirement(ReadRequirement::SKIP);
1796
12
        DLOG(INFO) << "Map column iterator set key column to SKIP";
1797
12
    }
1798
1799
28
    if (!child_paths.value.empty()) {
1800
23
        RETURN_IF_ERROR(_val_iterator->set_access_paths(child_paths.value.all_paths,
1801
23
                                                        child_paths.value.predicate_paths));
1802
        // Same as keys: predicate-only value paths stay PREDICATE because this
1803
        // post-processing update cannot lower a stronger child requirement.
1804
23
        _val_iterator->set_read_requirement_self(ReadRequirement::LAZY_OUTPUT);
1805
23
    } else {
1806
5
        _val_iterator->set_read_requirement(ReadRequirement::SKIP);
1807
5
        DLOG(INFO) << "Map column iterator set value column to SKIP";
1808
5
    }
1809
28
    return Status::OK();
1810
28
}
1811
1812
14
void MapFileColumnIterator::set_read_phase(ReadPhase mode) {
1813
14
    ColumnIterator::set_read_phase(mode);
1814
14
    _key_iterator->set_read_phase(mode);
1815
14
    _val_iterator->set_read_phase(mode);
1816
14
}
1817
1818
0
void MapFileColumnIterator::finalize_lazy_phase(MutableColumnPtr& dst) {
1819
0
    _recovery_from_place_holder_column(dst);
1820
0
    auto& map_column = assert_cast<ColumnMap&, TypeCheckOnRelease::DISABLE>(
1821
0
            dst->is_nullable() ? static_cast<ColumnNullable&>(*dst).get_nested_column() : *dst);
1822
0
    auto keys_ptr = IColumn::mutate(std::move(map_column.get_keys_ptr()));
1823
0
    auto vals_ptr = IColumn::mutate(std::move(map_column.get_values_ptr()));
1824
0
    _key_iterator->finalize_lazy_phase(keys_ptr);
1825
0
    _val_iterator->finalize_lazy_phase(vals_ptr);
1826
0
    map_column.get_keys_ptr() = std::move(keys_ptr);
1827
0
    map_column.get_values_ptr() = std::move(vals_ptr);
1828
0
}
1829
1830
6
void MapFileColumnIterator::set_read_requirement(ReadRequirement requirement) {
1831
6
    set_read_requirement_self(requirement);
1832
6
    _key_iterator->set_read_requirement(requirement);
1833
6
    _val_iterator->set_read_requirement(requirement);
1834
6
}
1835
1836
21
bool MapFileColumnIterator::has_lazy_read_target() const {
1837
21
    return _read_requirement == ReadRequirement::LAZY_OUTPUT ||
1838
21
           _key_iterator->has_lazy_read_target() || _val_iterator->has_lazy_read_target();
1839
21
}
1840
1841
////////////////////////////////////////////////////////////////////////////////
1842
1843
StructFileColumnIterator::StructFileColumnIterator(
1844
        std::shared_ptr<ColumnReader> reader, ColumnIteratorUPtr null_iterator,
1845
        std::vector<ColumnIteratorUPtr>&& sub_column_iterators)
1846
66
        : _struct_reader(reader), _sub_column_iterators(std::move(sub_column_iterators)) {
1847
66
    if (_struct_reader->is_nullable()) {
1848
2
        _null_iterator = std::move(null_iterator);
1849
2
    }
1850
66
}
1851
1852
0
Status StructFileColumnIterator::init(const ColumnIteratorOptions& opts) {
1853
0
    if (_read_requirement == ReadRequirement::SKIP) {
1854
0
        DLOG(INFO) << "Struct column iterator column " << _column_name << " skip reading.";
1855
0
        return Status::OK();
1856
0
    }
1857
1858
0
    for (auto& column_iterator : _sub_column_iterators) {
1859
0
        RETURN_IF_ERROR(column_iterator->init(opts));
1860
0
    }
1861
0
    if (_struct_reader->is_nullable()) {
1862
0
        RETURN_IF_ERROR(_null_iterator->init(opts));
1863
0
    }
1864
0
    return Status::OK();
1865
0
}
1866
1867
4
Status StructFileColumnIterator::next_batch(size_t* n, MutableColumnPtr& dst, bool* has_null) {
1868
4
    if (!need_to_read()) {
1869
0
        DLOG(INFO) << "Struct column iterator column " << _column_name << " skip reading.";
1870
0
        _convert_to_place_holder_column(dst, *n);
1871
0
        return Status::OK();
1872
0
    }
1873
1874
4
    _recovery_from_place_holder_column(dst);
1875
1876
4
    if (read_null_map_only()) {
1877
        // NULL_MAP_ONLY mode: read null map, fill nested ColumnStruct with empty defaults
1878
1
        DORIS_CHECK(is_column_nullable(*dst));
1879
1
        auto& nullable_col = assert_cast<ColumnNullable&>(*dst);
1880
1
        auto null_map_ptr = nullable_col.get_null_map_column_ptr();
1881
1
        size_t num_read = *n;
1882
1
        if (_null_iterator) {
1883
1
            bool null_signs_has_null = false;
1884
1
            MutableColumnPtr null_map_column = std::move(null_map_ptr);
1885
1
            RETURN_IF_ERROR(
1886
1
                    _null_iterator->next_batch(&num_read, null_map_column, &null_signs_has_null));
1887
1
        } else {
1888
            // schema-change: column became nullable but old segment has no null data
1889
0
            null_map_ptr->insert_many_vals(0, num_read);
1890
0
        }
1891
1
        DCHECK(num_read == *n);
1892
        // fill nested ColumnStruct with defaults to maintain consistent column sizes
1893
1
        auto& column_struct = assert_cast<ColumnStruct&, TypeCheckOnRelease::DISABLE>(
1894
1
                nullable_col.get_nested_column());
1895
1
        column_struct.insert_many_defaults(num_read);
1896
1
        *has_null = true;
1897
1
        return Status::OK();
1898
1
    }
1899
1900
3
    auto& column_struct = assert_cast<ColumnStruct&, TypeCheckOnRelease::DISABLE>(
1901
3
            is_column_nullable(*dst) ? static_cast<ColumnNullable&>(*dst).get_nested_column()
1902
3
                                     : *dst);
1903
9
    for (size_t i = 0; i < column_struct.tuple_size(); i++) {
1904
6
        size_t num_read = *n;
1905
6
        auto sub_column_ptr = IColumn::mutate(std::move(column_struct.get_column_ptr(i)));
1906
6
        Defer defer_sub_column {
1907
6
                [&] { column_struct.get_column_ptr(i) = std::move(sub_column_ptr); }};
1908
6
        bool column_has_null = false;
1909
6
        RETURN_IF_ERROR(
1910
6
                _sub_column_iterators[i]->next_batch(&num_read, sub_column_ptr, &column_has_null));
1911
6
        DCHECK(num_read == *n);
1912
6
    }
1913
1914
3
    if (is_column_nullable(*dst) && need_to_read_meta_columns()) {
1915
0
        size_t num_read = *n;
1916
0
        auto null_map_ptr = static_cast<ColumnNullable&>(*dst).get_null_map_column_ptr();
1917
        // in not-null to null linked-schemachange mode,
1918
        // actually we do not change dat data include meta in footer,
1919
        // so may dst from changed meta which is nullable but old data is not nullable,
1920
        // if so, we should set null_map to all null by default
1921
0
        if (_null_iterator) {
1922
0
            bool null_signs_has_null = false;
1923
0
            MutableColumnPtr null_map_column = std::move(null_map_ptr);
1924
0
            RETURN_IF_ERROR(
1925
0
                    _null_iterator->next_batch(&num_read, null_map_column, &null_signs_has_null));
1926
0
        } else {
1927
0
            null_map_ptr->insert_many_vals(0, num_read);
1928
0
        }
1929
0
        DCHECK(num_read == *n);
1930
0
    }
1931
1932
3
    return Status::OK();
1933
3
}
1934
1935
2
Status StructFileColumnIterator::seek_to_ordinal(ordinal_t ord) {
1936
2
    if (!need_to_read()) {
1937
0
        DLOG(INFO) << "Struct column iterator column " << _column_name << " skip reading.";
1938
0
        return Status::OK();
1939
0
    }
1940
1941
2
    if (read_null_map_only()) {
1942
        // In NULL_MAP_ONLY mode, only seek the null iterator; skip all sub-column iterators
1943
0
        if (_struct_reader->is_nullable() && _null_iterator) {
1944
0
            RETURN_IF_ERROR(_null_iterator->seek_to_ordinal(ord));
1945
0
        }
1946
0
        return Status::OK();
1947
0
    }
1948
1949
4
    for (auto& column_iterator : _sub_column_iterators) {
1950
4
        RETURN_IF_ERROR(column_iterator->seek_to_ordinal(ord));
1951
4
    }
1952
1953
2
    if (_struct_reader->is_nullable() && need_to_read_meta_columns()) {
1954
0
        RETURN_IF_ERROR(_null_iterator->seek_to_ordinal(ord));
1955
0
    }
1956
2
    return Status::OK();
1957
2
}
1958
1959
0
Status StructFileColumnIterator::init_prefetcher(const SegmentPrefetchParams& params) {
1960
0
    for (auto& column_iterator : _sub_column_iterators) {
1961
0
        RETURN_IF_ERROR(column_iterator->init_prefetcher(params));
1962
0
    }
1963
0
    if (_struct_reader->is_nullable()) {
1964
0
        RETURN_IF_ERROR(_null_iterator->init_prefetcher(params));
1965
0
    }
1966
0
    return Status::OK();
1967
0
}
1968
1969
void StructFileColumnIterator::collect_prefetchers(
1970
        std::map<PrefetcherInitMethod, std::vector<SegmentPrefetcher*>>& prefetchers,
1971
2
        PrefetcherInitMethod init_method) {
1972
2
    if (!need_to_read()) {
1973
0
        return;
1974
0
    }
1975
2
    if (_struct_reader->is_nullable()) {
1976
2
        _null_iterator->collect_prefetchers(prefetchers, init_method);
1977
2
    }
1978
2
    if (read_null_map_only()) {
1979
0
        return;
1980
0
    }
1981
4
    for (auto& column_iterator : _sub_column_iterators) {
1982
4
        if (column_iterator->need_to_read()) {
1983
2
            column_iterator->collect_prefetchers(prefetchers, init_method);
1984
2
        }
1985
4
    }
1986
2
}
1987
1988
Status StructFileColumnIterator::read_by_rowids(const rowid_t* rowids, const size_t count,
1989
1
                                                MutableColumnPtr& dst) {
1990
1
    if (!need_to_read()) {
1991
0
        DLOG(INFO) << "Struct column iterator column " << _column_name << " skip reading.";
1992
0
        _convert_to_place_holder_column(dst, count);
1993
0
        return Status::OK();
1994
0
    }
1995
1996
1
    _recovery_from_place_holder_column(dst);
1997
1998
1
    if (count == 0) {
1999
0
        return Status::OK();
2000
0
    }
2001
2002
1
    size_t this_run = 1;
2003
1
    auto start_idx = rowids[0];
2004
1
    auto last_idx = rowids[0];
2005
5
    for (size_t i = 1; i < count; ++i) {
2006
4
        if (last_idx == rowids[i] - 1) {
2007
3
            last_idx = rowids[i];
2008
3
            this_run++;
2009
3
            continue;
2010
3
        }
2011
1
        RETURN_IF_ERROR(seek_to_ordinal(start_idx));
2012
1
        size_t num_read = this_run;
2013
1
        RETURN_IF_ERROR(next_batch(&num_read, dst));
2014
1
        DCHECK_EQ(num_read, this_run);
2015
2016
1
        start_idx = rowids[i];
2017
1
        last_idx = rowids[i];
2018
1
        this_run = 1;
2019
1
    }
2020
2021
1
    RETURN_IF_ERROR(seek_to_ordinal(start_idx));
2022
1
    size_t num_read = this_run;
2023
1
    RETURN_IF_ERROR(next_batch(&num_read, dst));
2024
1
    DCHECK_EQ(num_read, this_run);
2025
1
    return Status::OK();
2026
1
}
2027
2028
12
void StructFileColumnIterator::set_lazy_output_requirement() {
2029
12
    set_read_requirement_self(ReadRequirement::LAZY_OUTPUT);
2030
24
    for (auto& sub_iterator : _sub_column_iterators) {
2031
24
        sub_iterator->set_lazy_output_requirement();
2032
24
    }
2033
12
}
2034
2035
4
void StructFileColumnIterator::remove_pruned_sub_iterators() {
2036
13
    for (auto it = _sub_column_iterators.begin(); it != _sub_column_iterators.end();) {
2037
9
        auto& sub_iterator = *it;
2038
9
        if (sub_iterator->read_requirement() == ReadRequirement::SKIP) {
2039
4
            DLOG(INFO) << "Struct column iterator remove pruned sub-column "
2040
4
                       << sub_iterator->column_name();
2041
4
            it = _sub_column_iterators.erase(it);
2042
5
        } else {
2043
5
            sub_iterator->remove_pruned_sub_iterators();
2044
5
            ++it;
2045
5
        }
2046
9
    }
2047
4
}
2048
2049
Status StructFileColumnIterator::set_access_paths(
2050
        const TColumnAccessPaths& all_access_paths,
2051
51
        const TColumnAccessPaths& predicate_access_paths) {
2052
51
    if (all_access_paths.empty() && predicate_access_paths.empty()) {
2053
1
        return Status::OK();
2054
1
    }
2055
2056
50
    auto plan = DORIS_TRY(_prepare_nested_access_paths(
2057
46
            all_access_paths, predicate_access_paths, NestedMetaSupport::NULL_MAP,
2058
46
            [this](ReadRequirement requirement) {
2059
46
                for (auto& sub_iterator : _sub_column_iterators) {
2060
46
                    sub_iterator->set_read_requirement(requirement);
2061
46
                }
2062
46
            }));
2063
2064
46
    if (plan.skip_data_descendants) {
2065
4
        DLOG(INFO) << "Struct column iterator set column " << _column_name
2066
4
                   << " to NULL_MAP_ONLY meta read mode, all sub-columns set to SKIP";
2067
4
        return Status::OK();
2068
4
    }
2069
2070
42
    const bool reads_all_sub_columns = plan.all.reads_current_data;
2071
42
    const bool include_all_paths = !reads_all_sub_columns;
2072
81
    for (auto& sub_iterator : _sub_column_iterators) {
2073
81
        const auto name = sub_iterator->column_name();
2074
81
        auto paths = DORIS_TRY(DescendantAccessPathRouter::select_struct_paths_for_child(
2075
81
                plan.all.descendant_paths, plan.predicate.descendant_paths, name,
2076
81
                include_all_paths));
2077
2078
        // Predicate paths must still reach the child even when no non-predicate path selects it.
2079
81
        const bool need_to_read = reads_all_sub_columns || !paths.empty();
2080
81
        if (!need_to_read) {
2081
32
            set_read_requirement_self(ReadRequirement::SKIP);
2082
32
            sub_iterator->set_read_requirement(ReadRequirement::SKIP);
2083
32
            DLOG(INFO) << "Struct column iterator set sub-column " << name << " to SKIP";
2084
32
            continue;
2085
32
        }
2086
2087
49
        RETURN_IF_ERROR(sub_iterator->set_access_paths(paths.all_paths, paths.predicate_paths));
2088
        // Set LAZY_OUTPUT after routing child predicate paths. If the child was needed only for
2089
        // predicate evaluation, set_access_paths() has already promoted it to
2090
        // PREDICATE and this monotonic update will not downgrade it. Otherwise, this
2091
        // marks the child as a lazy materialization target.
2092
48
        set_read_requirement_self(ReadRequirement::LAZY_OUTPUT);
2093
48
        sub_iterator->set_read_requirement_self(ReadRequirement::LAZY_OUTPUT);
2094
48
    }
2095
41
    return Status::OK();
2096
42
}
2097
2098
24
void StructFileColumnIterator::set_read_phase(ReadPhase mode) {
2099
24
    ColumnIterator::set_read_phase(mode);
2100
46
    for (auto& sub_iterator : _sub_column_iterators) {
2101
46
        sub_iterator->set_read_phase(mode);
2102
46
    }
2103
24
}
2104
2105
1
void StructFileColumnIterator::finalize_lazy_phase(MutableColumnPtr& dst) {
2106
1
    _recovery_from_place_holder_column(dst);
2107
1
    auto& column_struct = assert_cast<ColumnStruct&, TypeCheckOnRelease::DISABLE>(
2108
1
            dst->is_nullable() ? static_cast<ColumnNullable&>(*dst).get_nested_column() : *dst);
2109
2110
2
    for (size_t i = 0; i < _sub_column_iterators.size(); ++i) {
2111
1
        auto& sub_column = column_struct.get_column_ptr(i);
2112
1
        MutableColumnPtr mutable_sub_column = IColumn::mutate(std::move(sub_column));
2113
1
        _sub_column_iterators[i]->finalize_lazy_phase(mutable_sub_column);
2114
1
        sub_column = std::move(mutable_sub_column);
2115
1
    }
2116
1
}
2117
2118
9
void StructFileColumnIterator::set_read_requirement(ReadRequirement requirement) {
2119
9
    set_read_requirement_self(requirement);
2120
18
    for (const auto& sub_column_iterator : _sub_column_iterators) {
2121
18
        sub_column_iterator->set_read_requirement(requirement);
2122
18
    }
2123
9
}
2124
2125
29
bool StructFileColumnIterator::has_lazy_read_target() const {
2126
29
    if (_read_requirement == ReadRequirement::LAZY_OUTPUT) {
2127
2
        return true;
2128
2
    }
2129
27
    return std::any_of(_sub_column_iterators.begin(), _sub_column_iterators.end(),
2130
52
                       [](const auto& sub_column_iterator) {
2131
52
                           return sub_column_iterator->has_lazy_read_target();
2132
52
                       });
2133
29
}
2134
2135
////////////////////////////////////////////////////////////////////////////////
2136
308
Status OffsetFileColumnIterator::init(const ColumnIteratorOptions& opts) {
2137
308
    RETURN_IF_ERROR(_offset_iterator->init(opts));
2138
    // allocate peek tmp column once
2139
308
    _peek_tmp_col = ColumnOffset64::create();
2140
308
    return Status::OK();
2141
308
}
2142
2143
307
Status OffsetFileColumnIterator::next_batch(size_t* n, MutableColumnPtr& dst, bool* has_null) {
2144
307
    RETURN_IF_ERROR(_offset_iterator->next_batch(n, dst, has_null));
2145
307
    return Status::OK();
2146
307
}
2147
2148
606
Status OffsetFileColumnIterator::_peek_one_offset(ordinal_t* offset) {
2149
606
    if (_offset_iterator->get_current_page()->has_remaining()) {
2150
302
        PageDecoder* offset_page_decoder = _offset_iterator->get_current_page()->data_decoder.get();
2151
302
        size_t n = 1;
2152
302
        _peek_tmp_col->clear();
2153
302
        RETURN_IF_ERROR(offset_page_decoder->peek_next_batch(&n, _peek_tmp_col)); // not null
2154
302
        DCHECK(_peek_tmp_col->size() == 1);
2155
302
        *offset =
2156
302
                assert_cast<const ColumnOffset64*, TypeCheckOnRelease::DISABLE>(_peek_tmp_col.get())
2157
302
                        ->get_element(0);
2158
304
    } else {
2159
304
        *offset = _offset_iterator->get_current_page()->next_array_item_ordinal;
2160
304
    }
2161
606
    return Status::OK();
2162
606
}
2163
2164
0
Status OffsetFileColumnIterator::init_prefetcher(const SegmentPrefetchParams& params) {
2165
0
    return _offset_iterator->init_prefetcher(params);
2166
0
}
2167
2168
void OffsetFileColumnIterator::collect_prefetchers(
2169
        std::map<PrefetcherInitMethod, std::vector<SegmentPrefetcher*>>& prefetchers,
2170
2
        PrefetcherInitMethod init_method) {
2171
2
    _offset_iterator->collect_prefetchers(prefetchers, init_method);
2172
2
}
2173
2174
/**
2175
 *  first_storage_offset read from page should smaller than next_storage_offset which here call _peek_one_offset from page,
2176
    and first_column_offset is keep in memory data which is different dimension with (first_storage_offset and next_storage_offset)
2177
     eg. step1. read page: first_storage_offset = 16382
2178
         step2. read page below with _peek_one_offset(&last_offset): last_offset = 16387
2179
         step3. first_offset = 126 which is calculate in column offsets
2180
         for loop column offsets element in size
2181
            we can calculate from first_storage_offset to next_storage_offset one by one to fill with offsets_data in memory column offsets
2182
 * @param start
2183
 * @param column_offsets
2184
 * @return
2185
 */
2186
Status OffsetFileColumnIterator::_calculate_offsets(ssize_t start,
2187
295
                                                    ColumnArray::ColumnOffsets& column_offsets) {
2188
295
    ordinal_t next_storage_offset = 0;
2189
295
    RETURN_IF_ERROR(_peek_one_offset(&next_storage_offset));
2190
2191
    // calculate real offsets
2192
295
    auto& offsets_data = column_offsets.get_data();
2193
295
    ordinal_t first_column_offset = offsets_data[start - 1]; // -1 is valid
2194
295
    ordinal_t first_storage_offset = offsets_data[start];
2195
295
    DCHECK(next_storage_offset >= first_storage_offset);
2196
95.1k
    for (ssize_t i = start; i < offsets_data.size() - 1; ++i) {
2197
94.8k
        offsets_data[i] = first_column_offset + (offsets_data[i + 1] - first_storage_offset);
2198
94.8k
    }
2199
    // last offset
2200
295
    offsets_data[offsets_data.size() - 1] =
2201
295
            first_column_offset + (next_storage_offset - first_storage_offset);
2202
295
    return Status::OK();
2203
295
}
2204
2205
////////////////////////////////////////////////////////////////////////////////
2206
ArrayFileColumnIterator::ArrayFileColumnIterator(std::shared_ptr<ColumnReader> reader,
2207
                                                 OffsetFileColumnIteratorUPtr offset_reader,
2208
                                                 ColumnIteratorUPtr item_iterator,
2209
                                                 ColumnIteratorUPtr null_iterator)
2210
63
        : _array_reader(reader),
2211
63
          _offset_iterator(std::move(offset_reader)),
2212
63
          _item_iterator(std::move(item_iterator)) {
2213
63
    if (_array_reader->is_nullable()) {
2214
30
        _null_iterator = std::move(null_iterator);
2215
30
    }
2216
63
}
2217
2218
30
Status ArrayFileColumnIterator::init(const ColumnIteratorOptions& opts) {
2219
30
    if (_read_requirement == ReadRequirement::SKIP) {
2220
0
        DLOG(INFO) << "Array column iterator column " << _column_name << " skip reading.";
2221
0
        return Status::OK();
2222
0
    }
2223
2224
30
    RETURN_IF_ERROR(_offset_iterator->init(opts));
2225
30
    RETURN_IF_ERROR(_item_iterator->init(opts));
2226
30
    if (_array_reader->is_nullable()) {
2227
28
        RETURN_IF_ERROR(_null_iterator->init(opts));
2228
28
    }
2229
30
    return Status::OK();
2230
30
}
2231
2232
31
Status ArrayFileColumnIterator::_seek_by_offsets(ordinal_t ord) {
2233
31
    if (read_offset_only()) {
2234
        // In OFFSET_ONLY mode, item iterator is SKIP, no need to seek it
2235
0
        return Status::OK();
2236
0
    }
2237
    // using offsets info
2238
31
    ordinal_t offset = 0;
2239
31
    RETURN_IF_ERROR(_offset_iterator->_peek_one_offset(&offset));
2240
31
    RETURN_IF_ERROR(_item_iterator->seek_to_ordinal(offset));
2241
31
    return Status::OK();
2242
31
}
2243
2244
33
Status ArrayFileColumnIterator::seek_to_ordinal(ordinal_t ord) {
2245
33
    if (!need_to_read()) {
2246
0
        DLOG(INFO) << "Array column iterator column " << _column_name << " skip reading.";
2247
0
        return Status::OK();
2248
0
    }
2249
2250
33
    if (read_null_map_only()) {
2251
        // In NULL_MAP_ONLY mode, only seek the null iterator; skip offset and item iterators
2252
2
        if (_array_reader->is_nullable() && _null_iterator) {
2253
2
            RETURN_IF_ERROR(_null_iterator->seek_to_ordinal(ord));
2254
2
        }
2255
2
        return Status::OK();
2256
2
    }
2257
2258
31
    RETURN_IF_ERROR(_offset_iterator->seek_to_ordinal(ord));
2259
31
    if (_array_reader->is_nullable()) {
2260
29
        RETURN_IF_ERROR(_null_iterator->seek_to_ordinal(ord));
2261
29
    }
2262
31
    return _seek_by_offsets(ord);
2263
31
}
2264
2265
32
Status ArrayFileColumnIterator::next_batch(size_t* n, MutableColumnPtr& dst, bool* has_null) {
2266
32
    if (!need_to_read()) {
2267
0
        DLOG(INFO) << "Array column iterator column " << _column_name << " skip reading, read phase"
2268
0
                   << static_cast<int>(_read_phase)
2269
0
                   << ", read requirement: " << static_cast<int>(_read_requirement);
2270
0
        _convert_to_place_holder_column(dst, *n);
2271
0
        return Status::OK();
2272
0
    }
2273
2274
32
    _recovery_from_place_holder_column(dst);
2275
2276
32
    if (read_null_map_only()) {
2277
        // NULL_MAP_ONLY mode: read null map, fill nested ColumnArray with empty defaults
2278
3
        DORIS_CHECK(is_column_nullable(*dst));
2279
3
        auto& nullable_col = assert_cast<ColumnNullable&>(*dst);
2280
3
        auto null_map_ptr = nullable_col.get_null_map_column_ptr();
2281
3
        size_t num_read = *n;
2282
3
        if (_null_iterator) {
2283
3
            bool null_signs_has_null = false;
2284
3
            MutableColumnPtr null_map_column = std::move(null_map_ptr);
2285
3
            RETURN_IF_ERROR(
2286
3
                    _null_iterator->next_batch(&num_read, null_map_column, &null_signs_has_null));
2287
3
        } else {
2288
            // schema-change: column became nullable but old segment has no null data
2289
0
            null_map_ptr->insert_many_vals(0, num_read);
2290
0
        }
2291
3
        DCHECK(num_read == *n);
2292
        // fill nested ColumnArray with empty (zero-length) arrays
2293
3
        auto& column_array = assert_cast<ColumnArray&, TypeCheckOnRelease::DISABLE>(
2294
3
                nullable_col.get_nested_column());
2295
3
        column_array.insert_many_defaults(num_read);
2296
3
        *has_null = true;
2297
3
        return Status::OK();
2298
3
    }
2299
2300
29
    auto& column_array = assert_cast<ColumnArray&, TypeCheckOnRelease::DISABLE>(
2301
29
            is_column_nullable(*dst) ? static_cast<ColumnNullable&>(*dst).get_nested_column()
2302
29
                                     : *dst);
2303
2304
29
    bool offsets_has_null = false;
2305
29
    const bool read_meta_columns = need_to_read_meta_columns();
2306
29
    MutableColumnPtr column_offsets_ptr;
2307
29
    if (read_meta_columns) {
2308
29
        column_offsets_ptr = IColumn::mutate(std::move(column_array.get_offsets_ptr()));
2309
29
    } else {
2310
0
        const auto base_offset =
2311
0
                column_array.get_offsets().empty() ? 0 : column_array.get_offsets().back();
2312
0
        column_offsets_ptr = ColumnArray::ColumnOffsets::create();
2313
0
        assert_cast<ColumnOffset64&, TypeCheckOnRelease::DISABLE>(*column_offsets_ptr)
2314
0
                .insert_value(base_offset);
2315
0
    }
2316
29
    Defer defer_offsets {[&] {
2317
29
        if (read_meta_columns) {
2318
29
            auto typed_column_offsets_ptr = ColumnArray::ColumnOffsets::cast_to_column_mutptr(
2319
29
                    assert_cast<ColumnArray::ColumnOffsets*, TypeCheckOnRelease::DISABLE>(
2320
29
                            column_offsets_ptr.get()));
2321
29
            column_offsets_ptr = nullptr;
2322
29
            column_array.get_offsets_ptr() = std::move(typed_column_offsets_ptr);
2323
29
        }
2324
29
    }};
2325
29
    ssize_t start = column_offsets_ptr->size();
2326
29
    RETURN_IF_ERROR(_offset_iterator->next_batch(n, column_offsets_ptr, &offsets_has_null));
2327
29
    if (*n == 0) {
2328
0
        return Status::OK();
2329
0
    }
2330
29
    auto& column_offsets = static_cast<ColumnArray::ColumnOffsets&>(*column_offsets_ptr);
2331
29
    RETURN_IF_ERROR(_offset_iterator->_calculate_offsets(start, column_offsets));
2332
29
    size_t num_items =
2333
29
            column_offsets.get_data().back() - column_offsets.get_data()[start - 1]; // -1 is valid
2334
29
    if (num_items > 0) {
2335
27
        auto column_items_ptr = IColumn::mutate(std::move(column_array.get_data_ptr()));
2336
27
        Defer defer_items {[&] { column_array.get_data_ptr() = std::move(column_items_ptr); }};
2337
27
        if (read_offset_only()) {
2338
            // OFFSET_ONLY mode: skip reading actual item data, fill with defaults
2339
0
            column_items_ptr->insert_many_defaults(num_items);
2340
27
        } else {
2341
27
            size_t num_read = num_items;
2342
27
            bool items_has_null = false;
2343
27
            RETURN_IF_ERROR(
2344
27
                    _item_iterator->next_batch(&num_read, column_items_ptr, &items_has_null));
2345
27
            DCHECK(num_read == num_items);
2346
27
        }
2347
27
    }
2348
2349
29
    if (is_column_nullable(*dst) && read_meta_columns) {
2350
27
        auto null_map_ptr = static_cast<ColumnNullable&>(*dst).get_null_map_column_ptr();
2351
27
        size_t num_read = *n;
2352
        // in not-null to null linked-schemachange mode,
2353
        // actually we do not change dat data include meta in footer,
2354
        // so may dst from changed meta which is nullable but old data is not nullable,
2355
        // if so, we should set null_map to all null by default
2356
27
        if (_null_iterator) {
2357
27
            bool null_signs_has_null = false;
2358
27
            MutableColumnPtr null_map_column = std::move(null_map_ptr);
2359
27
            RETURN_IF_ERROR(
2360
27
                    _null_iterator->next_batch(&num_read, null_map_column, &null_signs_has_null));
2361
27
        } else {
2362
0
            null_map_ptr->insert_many_vals(0, num_read);
2363
0
        }
2364
27
        DCHECK(num_read == *n);
2365
27
    }
2366
2367
29
    return Status::OK();
2368
29
}
2369
2370
0
Status ArrayFileColumnIterator::init_prefetcher(const SegmentPrefetchParams& params) {
2371
0
    RETURN_IF_ERROR(_offset_iterator->init_prefetcher(params));
2372
0
    RETURN_IF_ERROR(_item_iterator->init_prefetcher(params));
2373
0
    if (_array_reader->is_nullable()) {
2374
0
        RETURN_IF_ERROR(_null_iterator->init_prefetcher(params));
2375
0
    }
2376
0
    return Status::OK();
2377
0
}
2378
2379
void ArrayFileColumnIterator::collect_prefetchers(
2380
        std::map<PrefetcherInitMethod, std::vector<SegmentPrefetcher*>>& prefetchers,
2381
1
        PrefetcherInitMethod init_method) {
2382
1
    if (!need_to_read()) {
2383
0
        return;
2384
0
    }
2385
1
    if (!read_null_map_only()) {
2386
1
        _offset_iterator->collect_prefetchers(prefetchers, init_method);
2387
1
    }
2388
1
    if (_array_reader->is_nullable()) {
2389
1
        _null_iterator->collect_prefetchers(prefetchers, init_method);
2390
1
    }
2391
1
    if (read_offset_only() || read_null_map_only()) {
2392
0
        return;
2393
0
    }
2394
    // the actual data pages to read of item column depends on the read result of offset column,
2395
    // so we can't init prefetch blocks according to rowids, just prefetch all data blocks here.
2396
1
    if (_item_iterator->need_to_read()) {
2397
1
        _item_iterator->collect_prefetchers(prefetchers, PrefetcherInitMethod::ALL_DATA_BLOCKS);
2398
1
    }
2399
1
}
2400
2401
Status ArrayFileColumnIterator::read_by_rowids(const rowid_t* rowids, const size_t count,
2402
12
                                               MutableColumnPtr& dst) {
2403
12
    if (!need_to_read()) {
2404
0
        DLOG(INFO) << "Array column iterator column " << _column_name << " skip reading.";
2405
0
        _convert_to_place_holder_column(dst, count);
2406
0
        return Status::OK();
2407
0
    }
2408
2409
12
    _recovery_from_place_holder_column(dst);
2410
2411
26
    for (size_t i = 0; i < count; ++i) {
2412
        // TODO(cambyszju): now read array one by one, need optimize later
2413
14
        RETURN_IF_ERROR(seek_to_ordinal(rowids[i]));
2414
14
        size_t num_read = 1;
2415
14
        RETURN_IF_ERROR(next_batch(&num_read, dst));
2416
14
    }
2417
12
    return Status::OK();
2418
12
}
2419
2420
3
void ArrayFileColumnIterator::set_lazy_output_requirement() {
2421
3
    set_read_requirement_self(ReadRequirement::LAZY_OUTPUT);
2422
3
    _item_iterator->set_lazy_output_requirement();
2423
3
}
2424
2425
1
void ArrayFileColumnIterator::remove_pruned_sub_iterators() {
2426
1
    _item_iterator->remove_pruned_sub_iterators();
2427
1
}
2428
2429
92
void ArrayFileColumnIterator::set_read_phase(ReadPhase mode) {
2430
92
    ColumnIterator::set_read_phase(mode);
2431
92
    _item_iterator->set_read_phase(mode);
2432
92
}
2433
2434
0
void ArrayFileColumnIterator::finalize_lazy_phase(MutableColumnPtr& dst) {
2435
0
    _recovery_from_place_holder_column(dst);
2436
0
    auto& column_array = assert_cast<ColumnArray&, TypeCheckOnRelease::DISABLE>(
2437
0
            dst->is_nullable() ? static_cast<ColumnNullable&>(*dst).get_nested_column() : *dst);
2438
0
    auto item_column_ptr = IColumn::mutate(std::move(column_array.get_data_ptr()));
2439
0
    _item_iterator->finalize_lazy_phase(item_column_ptr);
2440
0
    column_array.get_data_ptr() = std::move(item_column_ptr);
2441
0
}
2442
2443
5
void ArrayFileColumnIterator::set_read_requirement(ReadRequirement requirement) {
2444
5
    set_read_requirement_self(requirement);
2445
5
    _item_iterator->set_read_requirement(requirement);
2446
5
}
2447
2448
14
bool ArrayFileColumnIterator::has_lazy_read_target() const {
2449
14
    return _read_requirement == ReadRequirement::LAZY_OUTPUT ||
2450
14
           _item_iterator->has_lazy_read_target();
2451
14
}
2452
2453
Status ArrayFileColumnIterator::set_access_paths(const TColumnAccessPaths& all_access_paths,
2454
25
                                                 const TColumnAccessPaths& predicate_access_paths) {
2455
25
    if (all_access_paths.empty() && predicate_access_paths.empty()) {
2456
0
        return Status::OK();
2457
0
    }
2458
2459
25
    auto plan = DORIS_TRY(_prepare_nested_access_paths(
2460
25
            all_access_paths, predicate_access_paths, NestedMetaSupport::NULL_MAP_AND_OFFSET,
2461
25
            [this](ReadRequirement requirement) {
2462
25
                _item_iterator->set_read_requirement(requirement);
2463
25
            }));
2464
2465
25
    if (plan.skip_data_descendants) {
2466
3
        return Status::OK();
2467
3
    }
2468
2469
22
    const bool has_all_descendant = plan.all.has_descendant_paths();
2470
22
    const bool has_predicate_descendant = plan.predicate.has_descendant_paths();
2471
22
    auto child_paths = DORIS_TRY(DescendantAccessPathRouter::route_array_paths_to_item(
2472
22
            std::move(plan.all.descendant_paths), std::move(plan.predicate.descendant_paths),
2473
22
            _item_iterator->column_name()));
2474
2475
22
    if (has_all_descendant || has_predicate_descendant) {
2476
20
        RETURN_IF_ERROR(_item_iterator->set_access_paths(child_paths.all_paths,
2477
20
                                                         child_paths.predicate_paths));
2478
        // Predicate-only item paths stay PREDICATE because this update runs after
2479
        // child set_access_paths() and read requirements are monotonic. Non-predicate item paths are
2480
        // marked as lazy materialization targets.
2481
19
        _item_iterator->set_read_requirement_self(ReadRequirement::LAZY_OUTPUT);
2482
19
    }
2483
21
    return Status::OK();
2484
22
}
2485
2486
////////////////////////////////////////////////////////////////////////////////
2487
// StringFileColumnIterator implementation
2488
////////////////////////////////////////////////////////////////////////////////
2489
2490
StringFileColumnIterator::StringFileColumnIterator(std::shared_ptr<ColumnReader> reader)
2491
2.92k
        : FileColumnIterator(std::move(reader)) {}
2492
2493
2.89k
Status StringFileColumnIterator::init(const ColumnIteratorOptions& opts) {
2494
2.89k
    if (read_offset_only()) {
2495
        // Propagate only_read_offsets to the FileColumnIterator's options
2496
0
        auto modified_opts = opts;
2497
0
        modified_opts.only_read_offsets = true;
2498
0
        return FileColumnIterator::init(modified_opts);
2499
0
    }
2500
2.89k
    return FileColumnIterator::init(opts);
2501
2.89k
}
2502
2503
Status StringFileColumnIterator::set_access_paths(
2504
        const TColumnAccessPaths& all_access_paths,
2505
19
        const TColumnAccessPaths& predicate_access_paths) {
2506
19
    RETURN_IF_ERROR(FileColumnIterator::set_access_paths(all_access_paths, predicate_access_paths));
2507
    // OFFSET_ONLY mode is fundamentally incompatible with CHAR columns:
2508
    // CHAR is stored padded to its declared length (see
2509
    // OlapColumnDataConvertorChar::clone_and_padding), so the per-row length
2510
    // recorded in dict word info / page headers is always the padded length
2511
    // (e.g. 25 for CHAR(25)) — never the logical length expected by length().
2512
    // Recovering the logical length requires scanning the chars buffer with
2513
    // strnlen() (shrink_padding_chars), which OFFSET_ONLY by definition skips.
2514
    // There is no partial-benefit path: any optimization that still produces
2515
    // the correct length() result must read the chars buffer in full.
2516
    //
2517
    // FE (NestedColumnPruning) already filters CHAR slots out of the
2518
    // OFFSET-only access plan, so reaching this branch means an FE/BE
2519
    // contract violation. Fail loudly instead of silently falling back.
2520
19
    if (read_offset_only() && get_reader() != nullptr &&
2521
19
        get_reader()->get_meta_type() == FieldType::OLAP_FIELD_TYPE_CHAR) {
2522
0
        return Status::InternalError(
2523
0
                "OFFSET_ONLY access path is not supported on CHAR column '{}': CHAR is stored "
2524
0
                "padded so the per-row length information available without reading the chars "
2525
0
                "buffer is always the padded length, not the logical length. The FE planner "
2526
0
                "must not emit an OFFSET access path for CHAR columns.",
2527
0
                _column_name);
2528
0
    }
2529
19
    if (read_offset_only()) {
2530
6
        DLOG(INFO) << "String column iterator set column " << _column_name
2531
6
                   << " to OFFSET_ONLY meta read mode";
2532
13
    } else if (read_null_map_only()) {
2533
4
        DLOG(INFO) << "String column iterator set column " << _column_name
2534
4
                   << " to NULL_MAP_ONLY meta read mode";
2535
4
    }
2536
2537
19
    return Status::OK();
2538
19
}
2539
2540
////////////////////////////////////////////////////////////////////////////////
2541
2542
Status FileColumnIterator::set_access_paths(const TColumnAccessPaths& all_access_paths,
2543
68
                                            const TColumnAccessPaths& predicate_access_paths) {
2544
68
    if (all_access_paths.empty() && predicate_access_paths.empty()) {
2545
11
        return Status::OK();
2546
11
    }
2547
2548
57
    const auto requirement_before_access_path = _read_requirement;
2549
57
    if (!predicate_access_paths.empty()) {
2550
32
        set_read_requirement(ReadRequirement::PREDICATE);
2551
32
    }
2552
2553
57
    auto all_split = DORIS_TRY(_split_access_paths(all_access_paths));
2554
50
    auto predicate_split = DORIS_TRY(_split_access_paths(predicate_access_paths));
2555
49
    if (all_split.reads_current_data) {
2556
20
        set_lazy_output_requirement();
2557
20
    }
2558
49
    if (predicate_split.reads_current_data) {
2559
18
        set_read_requirement(ReadRequirement::PREDICATE);
2560
18
    }
2561
49
    return _check_and_set_meta_read_mode(requirement_before_access_path, all_split);
2562
50
}
2563
2564
8.94k
FileColumnIterator::FileColumnIterator(std::shared_ptr<ColumnReader> reader) : _reader(reader) {}
2565
2566
Status ColumnIterator::_check_and_set_meta_read_mode(ReadRequirement requirement_before_access_path,
2567
106
                                                     const AccessPathSplit& all_access_paths) {
2568
106
    _meta_read_mode = MetaReadMode::DEFAULT;
2569
106
    if (requirement_before_access_path != ReadRequirement::NORMAL &&
2570
106
        requirement_before_access_path != ReadRequirement::SKIP) {
2571
        // A stronger requirement means a parent/full-data path already required this iterator
2572
        // to materialize data. In that case a later predicate NULL/OFFSET path is only
2573
        // an additional predicate requirement and must not downgrade the read to
2574
        // meta-only.
2575
10
        return Status::OK();
2576
10
    }
2577
2578
96
    if (all_access_paths.reads_current_data || all_access_paths.has_descendant_paths()) {
2579
61
        return Status::OK();
2580
61
    }
2581
2582
35
    _meta_read_mode = all_access_paths.current_meta_mode;
2583
35
    return Status::OK();
2584
96
}
2585
2586
8.57k
Status FileColumnIterator::init(const ColumnIteratorOptions& opts) {
2587
8.57k
    if (_read_requirement == ReadRequirement::SKIP) {
2588
0
        DLOG(INFO) << "File column iterator column " << _column_name << " skip reading.";
2589
0
        return Status::OK();
2590
0
    }
2591
2592
8.57k
    _opts = opts;
2593
8.57k
    if (!_opts.use_page_cache) {
2594
8.57k
        _reader->disable_index_meta_cache();
2595
8.57k
    }
2596
8.57k
    RETURN_IF_ERROR(get_block_compression_codec(_reader->get_compression(), &_compress_codec));
2597
8.57k
    if (config::enable_low_cardinality_optimize &&
2598
8.57k
        opts.io_ctx.reader_type == ReaderType::READER_QUERY &&
2599
8.57k
        _reader->encoding_info()->encoding() == DICT_ENCODING) {
2600
139
        auto dict_encoding_type = _reader->get_dict_encoding_type();
2601
        // Only if the column is a predicate column, then we need check the all dict encoding flag
2602
        // because we could rewrite the predciate to accelarate query speed. But if it is not a
2603
        // predicate column, then it is useless. And it has a bad impact on cold read(first time read)
2604
        // because it will load the column's ordinal index and zonemap index and maybe other indices.
2605
        // it has bad impact on primary key query. For example, select * from table where pk = 1, and
2606
        // the table has 2000 columns.
2607
139
        if (dict_encoding_type == ColumnReader::UNKNOWN_DICT_ENCODING && opts.is_predicate_column) {
2608
33
            RETURN_IF_ERROR(seek_to_ordinal(_reader->num_rows() - 1));
2609
33
            _is_all_dict_encoding = _page.is_dict_encoding;
2610
33
            _reader->set_dict_encoding_type(_is_all_dict_encoding
2611
33
                                                    ? ColumnReader::ALL_DICT_ENCODING
2612
33
                                                    : ColumnReader::PARTIAL_DICT_ENCODING);
2613
106
        } else {
2614
106
            _is_all_dict_encoding = dict_encoding_type == ColumnReader::ALL_DICT_ENCODING;
2615
106
        }
2616
139
    }
2617
8.57k
    return Status::OK();
2618
8.57k
}
2619
2620
8.94k
FileColumnIterator::~FileColumnIterator() = default;
2621
2622
0
void FileColumnIterator::_trigger_prefetch_if_eligible(ordinal_t ord) {
2623
0
    std::vector<BlockRange> ranges;
2624
0
    if (_prefetcher->need_prefetch(cast_set<uint32_t>(ord), &ranges)) {
2625
0
        for (const auto& range : ranges) {
2626
0
            _cached_remote_file_reader->prefetch_range(range.offset, range.size, &_opts.io_ctx);
2627
0
        }
2628
0
    }
2629
0
}
2630
2631
24.5k
Status FileColumnIterator::seek_to_ordinal(ordinal_t ord) {
2632
24.5k
    if (!need_to_read()) {
2633
0
        DLOG(INFO) << "File column iterator column " << _column_name << " skip reading.";
2634
0
        return Status::OK();
2635
0
    }
2636
2637
24.5k
    LOG_IF(INFO, config::enable_segment_prefetch_verbose_log) << fmt::format(
2638
0
            "[verbose] FileColumnIterator::seek_to_ordinal seek to ordinal {}, enable_prefetch={}",
2639
0
            ord, _enable_prefetch);
2640
24.5k
    if (_enable_prefetch) {
2641
0
        _trigger_prefetch_if_eligible(ord);
2642
0
    }
2643
2644
    // if current page contains this row, we don't need to seek
2645
24.5k
    if (!_page || !_page.contains(ord) || !_page_iter.valid()) {
2646
9.03k
        RETURN_IF_ERROR(_reader->seek_at_or_before(ord, &_page_iter, _opts));
2647
9.03k
        RETURN_IF_ERROR(_read_data_page(_page_iter));
2648
9.03k
    }
2649
24.5k
    RETURN_IF_ERROR(_seek_to_pos_in_page(&_page, ord - _page.first_ordinal));
2650
24.5k
    _current_ordinal = ord;
2651
24.5k
    return Status::OK();
2652
24.5k
}
2653
2654
0
Status FileColumnIterator::seek_to_page_start() {
2655
0
    return seek_to_ordinal(_page.first_ordinal);
2656
0
}
2657
2658
26.2k
Status FileColumnIterator::_seek_to_pos_in_page(ParsedPage* page, ordinal_t offset_in_page) const {
2659
26.2k
    if (page->offset_in_page == offset_in_page) {
2660
        // fast path, do nothing
2661
22.9k
        return Status::OK();
2662
22.9k
    }
2663
2664
3.29k
    ordinal_t pos_in_data = offset_in_page;
2665
3.29k
    if (_page.has_null) {
2666
5
        ordinal_t offset_in_data = 0;
2667
5
        ordinal_t skips = offset_in_page;
2668
2669
5
        if (offset_in_page > page->offset_in_page) {
2670
            // forward, reuse null bitmap
2671
3
            skips = offset_in_page - page->offset_in_page;
2672
3
            offset_in_data = page->data_decoder->current_index();
2673
3
        } else {
2674
            // rewind null bitmap, and
2675
2
            page->null_decoder = RleDecoder<bool>((const uint8_t*)page->null_bitmap.data,
2676
2
                                                  cast_set<int>(page->null_bitmap.size), 1);
2677
2
        }
2678
2679
5
        auto skip_nulls = page->null_decoder.Skip(skips);
2680
5
        pos_in_data = offset_in_data + skips - skip_nulls;
2681
5
    }
2682
2683
3.29k
    RETURN_IF_ERROR(page->data_decoder->seek_to_position_in_page(pos_in_data));
2684
3.29k
    page->offset_in_page = offset_in_page;
2685
3.29k
    return Status::OK();
2686
3.29k
}
2687
2688
0
Status FileColumnIterator::next_batch_of_zone_map(size_t* n, MutableColumnPtr& dst) {
2689
0
    return _reader->next_batch_of_zone_map(n, dst);
2690
0
}
2691
2692
20.4k
Status FileColumnIterator::next_batch(size_t* n, MutableColumnPtr& dst, bool* has_null) {
2693
20.4k
    if (!need_to_read()) {
2694
0
        DLOG(INFO) << "File column iterator column " << _column_name << " skip reading.";
2695
0
        _convert_to_place_holder_column(dst, *n);
2696
0
        return Status::OK();
2697
0
    }
2698
2699
20.4k
    _recovery_from_place_holder_column(dst);
2700
2701
20.4k
    if (read_null_map_only()) {
2702
0
        DLOG(INFO) << "File column iterator column " << _column_name
2703
0
                   << " in NULL_MAP_ONLY mode, reading only null map.";
2704
0
        DORIS_CHECK(is_column_nullable(*dst));
2705
0
        auto& nullable_col = assert_cast<ColumnNullable&>(*dst);
2706
0
        auto& null_map_data = nullable_col.get_null_map_data();
2707
2708
0
        size_t remaining = *n;
2709
0
        *has_null = false;
2710
0
        while (remaining > 0) {
2711
0
            if (!_page.has_remaining()) {
2712
0
                bool eos = false;
2713
0
                RETURN_IF_ERROR(_load_next_page(&eos));
2714
0
                if (eos) {
2715
0
                    break;
2716
0
                }
2717
0
            }
2718
2719
0
            size_t nrows_in_page = std::min(remaining, _page.remaining());
2720
0
            size_t nrows_to_read = nrows_in_page;
2721
0
            if (_page.has_null) {
2722
0
                while (nrows_to_read > 0) {
2723
0
                    bool is_null = false;
2724
0
                    size_t this_run = _page.null_decoder.GetNextRun(&is_null, nrows_to_read);
2725
0
                    const size_t cur_size = null_map_data.size();
2726
0
                    null_map_data.resize(cur_size + this_run);
2727
0
                    memset(null_map_data.data() + cur_size, is_null ? 1 : 0, this_run);
2728
0
                    if (is_null) {
2729
0
                        *has_null = true;
2730
0
                    }
2731
0
                    nrows_to_read -= this_run;
2732
0
                    _page.offset_in_page += this_run;
2733
0
                    _current_ordinal += this_run;
2734
0
                }
2735
0
            } else {
2736
0
                const size_t cur_size = null_map_data.size();
2737
0
                null_map_data.resize(cur_size + nrows_to_read);
2738
0
                memset(null_map_data.data() + cur_size, 0, nrows_to_read);
2739
0
                _page.offset_in_page += nrows_to_read;
2740
0
                _current_ordinal += nrows_to_read;
2741
0
            }
2742
0
            remaining -= nrows_in_page;
2743
0
        }
2744
0
        *n -= remaining;
2745
0
        nullable_col.get_nested_column().insert_many_defaults(*n);
2746
0
        return Status::OK();
2747
0
    }
2748
2749
20.4k
    size_t curr_size = dst->byte_size();
2750
20.4k
    dst->reserve(*n);
2751
20.4k
    size_t remaining = *n;
2752
20.4k
    *has_null = false;
2753
42.6k
    while (remaining > 0) {
2754
22.2k
        if (!_page.has_remaining()) {
2755
1.76k
            bool eos = false;
2756
1.76k
            RETURN_IF_ERROR(_load_next_page(&eos));
2757
1.76k
            if (eos) {
2758
0
                break;
2759
0
            }
2760
1.76k
        }
2761
2762
        // number of rows to be read from this page
2763
22.2k
        size_t nrows_in_page = std::min(remaining, _page.remaining());
2764
22.2k
        size_t nrows_to_read = nrows_in_page;
2765
22.2k
        if (_page.has_null) {
2766
20.5k
            while (nrows_to_read > 0) {
2767
20.3k
                bool is_null = false;
2768
20.3k
                size_t this_run = _page.null_decoder.GetNextRun(&is_null, nrows_to_read);
2769
                // we use num_rows only for CHECK
2770
20.3k
                size_t num_rows = this_run;
2771
20.3k
                if (!is_null) {
2772
10.2k
                    RETURN_IF_ERROR(_page.data_decoder->next_batch(&num_rows, dst));
2773
10.2k
                    DCHECK_EQ(this_run, num_rows);
2774
10.2k
                } else {
2775
10.0k
                    *has_null = true;
2776
10.0k
                    auto* null_col = check_and_get_column<ColumnNullable>(dst.get());
2777
10.0k
                    if (null_col != nullptr) {
2778
10.0k
                        null_col->insert_many_defaults(this_run);
2779
10.0k
                    } else {
2780
0
                        return Status::InternalError("unexpected column type in column reader");
2781
0
                    }
2782
10.0k
                }
2783
2784
20.3k
                nrows_to_read -= this_run;
2785
20.3k
                _page.offset_in_page += this_run;
2786
20.3k
                _current_ordinal += this_run;
2787
20.3k
            }
2788
21.9k
        } else {
2789
21.9k
            RETURN_IF_ERROR(_page.data_decoder->next_batch(&nrows_to_read, dst));
2790
21.9k
            DCHECK_EQ(nrows_to_read, nrows_in_page);
2791
2792
21.9k
            _page.offset_in_page += nrows_to_read;
2793
21.9k
            _current_ordinal += nrows_to_read;
2794
21.9k
        }
2795
22.2k
        remaining -= nrows_in_page;
2796
22.2k
    }
2797
20.4k
    *n -= remaining;
2798
20.4k
    _opts.stats->bytes_read += (dst->byte_size() - curr_size) + BitmapSize(*n);
2799
2800
20.4k
#ifdef BE_TEST
2801
20.4k
    _reader->check_data_by_zone_map_for_test(dst);
2802
20.4k
#endif
2803
20.4k
    return Status::OK();
2804
20.4k
}
2805
2806
Status FileColumnIterator::read_by_rowids(const rowid_t* rowids, const size_t count,
2807
3.47k
                                          MutableColumnPtr& dst) {
2808
3.47k
    if (!need_to_read()) {
2809
0
        DLOG(INFO) << "File column iterator column " << _column_name << " skip reading.";
2810
0
        _convert_to_place_holder_column(dst, count);
2811
0
        return Status::OK();
2812
0
    }
2813
2814
3.47k
    _recovery_from_place_holder_column(dst);
2815
2816
3.47k
    if (read_null_map_only()) {
2817
1
        DLOG(INFO) << "File column iterator column " << _column_name
2818
1
                   << " in NULL_MAP_ONLY mode, reading only null map by rowids.";
2819
2820
1
        DORIS_CHECK(is_column_nullable(*dst));
2821
1
        auto& nullable_col = assert_cast<ColumnNullable&>(*dst);
2822
1
        auto& null_map_data = nullable_col.get_null_map_data();
2823
1
        const size_t base_size = null_map_data.size();
2824
1
        null_map_data.resize(base_size + count);
2825
2826
1
        size_t remaining = count;
2827
1
        size_t total_read_count = 0;
2828
1
        size_t nrows_to_read = 0;
2829
3
        while (remaining > 0) {
2830
2
            RETURN_IF_ERROR(seek_to_ordinal(rowids[total_read_count]));
2831
2832
2
            nrows_to_read = std::min(remaining, _page.remaining());
2833
2834
2
            if (_page.has_null) {
2835
1
                size_t already_read = 0;
2836
2
                while ((nrows_to_read - already_read) > 0) {
2837
1
                    bool is_null = false;
2838
1
                    size_t this_run = std::min(nrows_to_read - already_read, _page.remaining());
2839
1
                    if (UNLIKELY(this_run == 0)) {
2840
0
                        break;
2841
0
                    }
2842
1
                    this_run = _page.null_decoder.GetNextRun(&is_null, this_run);
2843
2844
1
                    size_t offset = total_read_count + already_read;
2845
1
                    size_t this_read_count = 0;
2846
1
                    rowid_t current_ordinal_in_page =
2847
1
                            cast_set<uint32_t>(_page.offset_in_page + _page.first_ordinal);
2848
2
                    for (size_t i = 0; i < this_run; ++i) {
2849
1
                        if (rowids[offset + i] - current_ordinal_in_page >= this_run) {
2850
0
                            break;
2851
0
                        }
2852
1
                        this_read_count++;
2853
1
                    }
2854
2855
1
                    if (this_read_count > 0) {
2856
1
                        memset(null_map_data.data() + base_size + offset, is_null ? 1 : 0,
2857
1
                               this_read_count);
2858
1
                    }
2859
2860
1
                    already_read += this_read_count;
2861
1
                    _page.offset_in_page += this_run;
2862
1
                }
2863
2864
1
                nrows_to_read = already_read;
2865
1
                total_read_count += nrows_to_read;
2866
1
                remaining -= nrows_to_read;
2867
1
            } else {
2868
1
                rowid_t current_ordinal_in_page =
2869
1
                        cast_set<uint32_t>(_page.offset_in_page + _page.first_ordinal);
2870
1
                size_t rows_in_current_page = 0;
2871
2
                for (size_t i = 0; i < nrows_to_read; ++i) {
2872
2
                    if (rowids[total_read_count + i] - current_ordinal_in_page >= nrows_to_read) {
2873
1
                        break;
2874
1
                    }
2875
1
                    ++rows_in_current_page;
2876
1
                }
2877
1
                DCHECK_GT(rows_in_current_page, 0);
2878
1
                memset(null_map_data.data() + base_size + total_read_count, 0,
2879
1
                       rows_in_current_page);
2880
1
                _page.offset_in_page += rows_in_current_page;
2881
1
                total_read_count += rows_in_current_page;
2882
1
                remaining -= rows_in_current_page;
2883
1
            }
2884
2
        }
2885
2886
1
        null_map_data.resize(base_size + total_read_count);
2887
1
        nullable_col.get_nested_column().insert_many_defaults(total_read_count);
2888
1
        return Status::OK();
2889
1
    }
2890
2891
3.47k
    size_t remaining = count;
2892
3.47k
    size_t total_read_count = 0;
2893
3.47k
    size_t nrows_to_read = 0;
2894
7.47k
    while (remaining > 0) {
2895
3.99k
        RETURN_IF_ERROR(seek_to_ordinal(rowids[total_read_count]));
2896
2897
        // number of rows to be read from this page
2898
3.99k
        nrows_to_read = std::min(remaining, _page.remaining());
2899
2900
3.99k
        if (_page.has_null) {
2901
11
            size_t already_read = 0;
2902
574
            while ((nrows_to_read - already_read) > 0) {
2903
563
                bool is_null = false;
2904
563
                size_t this_run = std::min(nrows_to_read - already_read, _page.remaining());
2905
563
                if (UNLIKELY(this_run == 0)) {
2906
0
                    break;
2907
0
                }
2908
563
                this_run = _page.null_decoder.GetNextRun(&is_null, this_run);
2909
563
                size_t offset = total_read_count + already_read;
2910
563
                size_t this_read_count = 0;
2911
563
                rowid_t current_ordinal_in_page =
2912
563
                        cast_set<uint32_t>(_page.offset_in_page + _page.first_ordinal);
2913
859
                for (size_t i = 0; i < this_run; ++i) {
2914
806
                    if (rowids[offset + i] - current_ordinal_in_page >= this_run) {
2915
510
                        break;
2916
510
                    }
2917
296
                    this_read_count++;
2918
296
                }
2919
2920
563
                auto origin_index = _page.data_decoder->current_index();
2921
563
                if (this_read_count > 0) {
2922
224
                    if (is_null) {
2923
52
                        auto* null_col = check_and_get_column<ColumnNullable>(dst.get());
2924
52
                        if (UNLIKELY(null_col == nullptr)) {
2925
0
                            return Status::InternalError("unexpected column type in column reader");
2926
0
                        }
2927
2928
52
                        null_col->insert_many_defaults(this_read_count);
2929
172
                    } else {
2930
172
                        size_t read_count = this_read_count;
2931
2932
                        // ordinal in nullable columns' data buffer maybe be not continuously(the data doesn't contain null value),
2933
                        // so we need use `page_start_off_in_decoder` to calculate the actual offset in `data_decoder`
2934
172
                        size_t page_start_off_in_decoder =
2935
172
                                _page.first_ordinal + _page.offset_in_page - origin_index;
2936
172
                        RETURN_IF_ERROR(_page.data_decoder->read_by_rowids(
2937
172
                                &rowids[offset], page_start_off_in_decoder, &read_count, dst));
2938
172
                        DCHECK_EQ(read_count, this_read_count);
2939
172
                    }
2940
224
                }
2941
2942
563
                if (!is_null) {
2943
291
                    RETURN_IF_ERROR(
2944
291
                            _page.data_decoder->seek_to_position_in_page(origin_index + this_run));
2945
291
                }
2946
2947
563
                already_read += this_read_count;
2948
563
                _page.offset_in_page += this_run;
2949
563
                DCHECK(_page.offset_in_page <= _page.num_rows);
2950
563
            }
2951
2952
11
            nrows_to_read = already_read;
2953
11
            total_read_count += nrows_to_read;
2954
11
            remaining -= nrows_to_read;
2955
3.98k
        } else {
2956
3.98k
            RETURN_IF_ERROR(_page.data_decoder->read_by_rowids(
2957
3.98k
                    &rowids[total_read_count], _page.first_ordinal, &nrows_to_read, dst));
2958
3.98k
            total_read_count += nrows_to_read;
2959
3.98k
            remaining -= nrows_to_read;
2960
3.98k
        }
2961
3.99k
    }
2962
3.47k
    return Status::OK();
2963
3.47k
}
2964
2965
1.76k
Status FileColumnIterator::_load_next_page(bool* eos) {
2966
1.76k
    _page_iter.next();
2967
1.76k
    if (!_page_iter.valid()) {
2968
0
        *eos = true;
2969
0
        return Status::OK();
2970
0
    }
2971
2972
1.76k
    RETURN_IF_ERROR(_read_data_page(_page_iter));
2973
1.76k
    RETURN_IF_ERROR(_seek_to_pos_in_page(&_page, 0));
2974
1.76k
    *eos = false;
2975
1.76k
    return Status::OK();
2976
1.76k
}
2977
2978
10.8k
Status FileColumnIterator::_read_data_page(const OrdinalPageIndexIterator& iter) {
2979
10.8k
    PageHandle handle;
2980
10.8k
    Slice page_body;
2981
10.8k
    PageFooterPB footer;
2982
10.8k
    _opts.type = DATA_PAGE;
2983
10.8k
    PageDecoderOptions decoder_opts;
2984
10.8k
    decoder_opts.only_read_offsets = _opts.only_read_offsets;
2985
10.8k
    RETURN_IF_ERROR(
2986
10.8k
            _reader->read_page(_opts, iter.page(), &handle, &page_body, &footer, _compress_codec));
2987
    // parse data page
2988
10.8k
    auto st = ParsedPage::create(std::move(handle), page_body, footer.data_page_footer(),
2989
10.8k
                                 _reader->encoding_info(), iter.page(), iter.page_index(), &_page,
2990
10.8k
                                 decoder_opts);
2991
10.8k
    if (!st.ok()) {
2992
0
        LOG(WARNING) << "failed to create ParsedPage, file=" << _opts.file_reader->path().native()
2993
0
                     << ", page_offset=" << iter.page().offset << ", page_size=" << iter.page().size
2994
0
                     << ", page_index=" << iter.page_index() << ", error=" << st;
2995
0
        return st;
2996
0
    }
2997
2998
    // dictionary page is read when the first data page that uses it is read,
2999
    // this is to optimize the memory usage: when there is no query on one column, we could
3000
    // release the memory of dictionary page.
3001
    // note that concurrent iterators for the same column won't repeatedly read dictionary page
3002
    // because of page cache.
3003
10.8k
    if (_reader->encoding_info()->encoding() == DICT_ENCODING) {
3004
4.70k
        auto dict_page_decoder = reinterpret_cast<BinaryDictPageDecoder*>(_page.data_decoder.get());
3005
4.70k
        if (dict_page_decoder->is_dict_encoding()) {
3006
3.07k
            if (_dict_decoder == nullptr) {
3007
3.07k
                RETURN_IF_ERROR(_read_dict_data());
3008
3.07k
                CHECK_NOTNULL(_dict_decoder);
3009
3.07k
            }
3010
3011
3.07k
            dict_page_decoder->set_dict_decoder(cast_set<uint32_t>(_dict_decoder->count()),
3012
3.07k
                                                _dict_word_info.get());
3013
3.07k
        }
3014
4.70k
    }
3015
10.8k
    return Status::OK();
3016
10.8k
}
3017
3018
3.07k
Status FileColumnIterator::_read_dict_data() {
3019
3.07k
    CHECK_EQ(_reader->encoding_info()->encoding(), DICT_ENCODING);
3020
    // read dictionary page
3021
3.07k
    Slice dict_data;
3022
3.07k
    PageFooterPB dict_footer;
3023
3.07k
    _opts.type = INDEX_PAGE;
3024
3025
3.07k
    RETURN_IF_ERROR(_reader->read_page(_opts, _reader->get_dict_page_pointer(), &_dict_page_handle,
3026
3.07k
                                       &dict_data, &dict_footer, _compress_codec));
3027
3.07k
    const EncodingInfo* encoding_info;
3028
    // The dict pool stores strings of the outer column's type. Using the
3029
    // outer type (CHAR vs VARCHAR/STRING) lets the EncodingInfo pick a
3030
    // CHAR-strip pre-decoder so the cached dict page is already unpadded.
3031
3.07k
    RETURN_IF_ERROR(EncodingInfo::get(_reader->get_meta_type(),
3032
3.07k
                                      dict_footer.dict_page_footer().encoding(), &encoding_info));
3033
3.07k
    RETURN_IF_ERROR(encoding_info->create_page_decoder(dict_data, {}, _dict_decoder));
3034
3.07k
    RETURN_IF_ERROR(_dict_decoder->init());
3035
3036
3.07k
    _dict_word_info.reset(new StringRef[_dict_decoder->count()]);
3037
3.07k
    RETURN_IF_ERROR(_dict_decoder->get_dict_word_info(_dict_word_info.get()));
3038
3.07k
    return Status::OK();
3039
3.07k
}
3040
3041
Status FileColumnIterator::get_row_ranges_by_zone_map(
3042
        const AndBlockColumnPredicate* col_predicates,
3043
        const std::vector<std::shared_ptr<const ColumnPredicate>>* delete_predicates,
3044
64
        RowRanges* row_ranges) {
3045
64
    if (_reader->has_zone_map()) {
3046
64
        RETURN_IF_ERROR(_reader->get_row_ranges_by_zone_map(col_predicates, delete_predicates,
3047
64
                                                            row_ranges, _opts));
3048
64
    }
3049
64
    return Status::OK();
3050
64
}
3051
3052
Status FileColumnIterator::get_row_ranges_by_bloom_filter(
3053
64
        const AndBlockColumnPredicate* col_predicates, RowRanges* row_ranges) {
3054
64
    if ((col_predicates->can_do_bloom_filter(false) && _reader->has_bloom_filter_index(false)) ||
3055
64
        (col_predicates->can_do_bloom_filter(true) && _reader->has_bloom_filter_index(true))) {
3056
3
        RETURN_IF_ERROR(_reader->get_row_ranges_by_bloom_filter(col_predicates, row_ranges, _opts));
3057
3
    }
3058
64
    return Status::OK();
3059
64
}
3060
3061
Status FileColumnIterator::get_row_ranges_by_dict(const AndBlockColumnPredicate* col_predicates,
3062
64
                                                  RowRanges* row_ranges) {
3063
64
    if (!_is_all_dict_encoding) {
3064
31
        return Status::OK();
3065
31
    }
3066
3067
33
    if (!_dict_decoder) {
3068
0
        RETURN_IF_ERROR(_read_dict_data());
3069
0
        CHECK_NOTNULL(_dict_decoder);
3070
0
    }
3071
3072
33
    if (!col_predicates->evaluate_and(_dict_word_info.get(), _dict_decoder->count())) {
3073
0
        row_ranges->clear();
3074
0
    }
3075
33
    return Status::OK();
3076
33
}
3077
3078
0
Status FileColumnIterator::init_prefetcher(const SegmentPrefetchParams& params) {
3079
0
    if (_cached_remote_file_reader =
3080
0
                std::dynamic_pointer_cast<io::CachedRemoteFileReader>(_reader->_file_reader);
3081
0
        !_cached_remote_file_reader) {
3082
0
        return Status::OK();
3083
0
    }
3084
0
    _enable_prefetch = true;
3085
0
    _prefetcher = std::make_unique<SegmentPrefetcher>(params.config);
3086
0
    RETURN_IF_ERROR(_prefetcher->init(_reader, params.read_options));
3087
0
    return Status::OK();
3088
0
}
3089
3090
void FileColumnIterator::collect_prefetchers(
3091
        std::map<PrefetcherInitMethod, std::vector<SegmentPrefetcher*>>& prefetchers,
3092
0
        PrefetcherInitMethod init_method) {
3093
0
    if (_prefetcher) {
3094
0
        prefetchers[init_method].emplace_back(_prefetcher.get());
3095
0
    }
3096
0
}
3097
3098
30
Status DefaultValueColumnIterator::init(const ColumnIteratorOptions& opts) {
3099
30
    _opts = opts;
3100
    // be consistent with segment v1
3101
    // if _has_default_value, we should create default column iterator for this column, and
3102
    // "NULL" is a special default value which means the default value is null.
3103
30
    if (_has_default_value) {
3104
6
        if (_default_value == "NULL") {
3105
1
            _default_value_field = Field::create_field<TYPE_NULL>(Null {});
3106
5
        } else {
3107
5
            if (_type == FieldType::OLAP_FIELD_TYPE_ARRAY) {
3108
0
                if (_default_value != "[]") {
3109
0
                    return Status::NotSupported("Array default {} is unsupported", _default_value);
3110
0
                } else {
3111
0
                    _default_value_field = Field::create_field<TYPE_ARRAY>(Array {});
3112
0
                    return Status::OK();
3113
0
                }
3114
5
            } else if (_type == FieldType::OLAP_FIELD_TYPE_STRUCT) {
3115
0
                return Status::NotSupported("STRUCT default type is unsupported");
3116
5
            } else if (_type == FieldType::OLAP_FIELD_TYPE_MAP) {
3117
0
                return Status::NotSupported("MAP default type is unsupported");
3118
0
            }
3119
5
            const auto t = _type;
3120
5
            const auto serde = DataTypeFactory::instance()
3121
5
                                       .create_data_type(t, _precision, _scale, _len)
3122
5
                                       ->get_serde();
3123
5
            RETURN_IF_ERROR(serde->from_fe_string(_default_value, _default_value_field));
3124
5
        }
3125
24
    } else if (_is_nullable) {
3126
24
        _default_value_field = Field::create_field<TYPE_NULL>(Null {});
3127
24
    } else {
3128
0
        return Status::InternalError(
3129
0
                "invalid default value column for no default value and not nullable");
3130
0
    }
3131
30
    return Status::OK();
3132
30
}
3133
3134
15
Status DefaultValueColumnIterator::next_batch(size_t* n, MutableColumnPtr& dst, bool* has_null) {
3135
15
    if (!need_to_read()) {
3136
4
        _convert_to_place_holder_column(dst, *n);
3137
4
        return Status::OK();
3138
4
    }
3139
3140
11
    _recovery_from_place_holder_column(dst);
3141
11
    *has_null = _default_value_field.is_null();
3142
11
    _insert_many_default(dst, *n);
3143
11
    return Status::OK();
3144
15
}
3145
3146
Status DefaultValueColumnIterator::read_by_rowids(const rowid_t* rowids, const size_t count,
3147
5
                                                  MutableColumnPtr& dst) {
3148
5
    if (!need_to_read()) {
3149
1
        _convert_to_place_holder_column(dst, count);
3150
1
        return Status::OK();
3151
1
    }
3152
3153
4
    _recovery_from_place_holder_column(dst);
3154
4
    _insert_many_default(dst, count);
3155
4
    return Status::OK();
3156
5
}
3157
3158
15
void DefaultValueColumnIterator::_insert_many_default(MutableColumnPtr& dst, size_t n) {
3159
15
    if (_default_value_field.is_null()) {
3160
11
        dst->insert_many_defaults(n);
3161
11
    } else {
3162
4
        dst = dst->convert_to_predicate_column_if_dictionary();
3163
4
        dst->insert_duplicate_fields(_default_value_field, n);
3164
4
    }
3165
15
}
3166
3167
0
Status RowIdColumnIteratorV2::next_batch(size_t* n, MutableColumnPtr& dst, bool* has_null) {
3168
0
    auto* string_column = assert_cast<ColumnString*, TypeCheckOnRelease::DISABLE>(dst.get());
3169
3170
0
    for (uint32_t i = 0; i < *n; ++i) {
3171
0
        uint32_t row_id = _current_rowid + i;
3172
0
        GlobalRowLoacationV2 location(_version, _backend_id, _file_id, row_id);
3173
0
        string_column->insert_data(reinterpret_cast<const char*>(&location),
3174
0
                                   sizeof(GlobalRowLoacationV2));
3175
0
    }
3176
0
    _current_rowid += *n;
3177
0
    return Status::OK();
3178
0
}
3179
3180
Status RowIdColumnIteratorV2::read_by_rowids(const rowid_t* rowids, const size_t count,
3181
16
                                             MutableColumnPtr& dst) {
3182
16
    auto* string_column = assert_cast<ColumnString*>(dst.get());
3183
3184
41
    for (size_t i = 0; i < count; ++i) {
3185
25
        uint32_t row_id = rowids[i];
3186
25
        GlobalRowLoacationV2 location(_version, _backend_id, _file_id, row_id);
3187
25
        string_column->insert_data(reinterpret_cast<const char*>(&location),
3188
25
                                   sizeof(GlobalRowLoacationV2));
3189
25
    }
3190
16
    return Status::OK();
3191
16
}
3192
3193
} // namespace doris::segment_v2