Coverage Report

Created: 2026-08-10 06:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/exec/scan/file_scanner.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 "exec/scan/file_scanner.h"
19
20
#include <fmt/format.h>
21
#include <gen_cpp/Exprs_types.h>
22
#include <gen_cpp/Metrics_types.h>
23
#include <gen_cpp/Opcodes_types.h>
24
#include <gen_cpp/PaloInternalService_types.h>
25
#include <gen_cpp/PlanNodes_types.h>
26
#include <glog/logging.h>
27
28
#include <algorithm>
29
#include <boost/iterator/iterator_facade.hpp>
30
#include <map>
31
#include <ranges>
32
#include <tuple>
33
#include <unordered_map>
34
#include <utility>
35
36
#include "common/cast_set.h"
37
#include "common/compiler_util.h" // IWYU pragma: keep
38
#include "common/config.h"
39
#include "common/consts.h"
40
#include "common/logging.h"
41
#include "common/status.h"
42
#include "core/block/column_with_type_and_name.h"
43
#include "core/block/columns_with_type_and_name.h"
44
#include "core/column/column.h"
45
#include "core/column/column_nullable.h"
46
#include "core/column/column_vector.h"
47
#include "core/data_type/data_type.h"
48
#include "core/data_type/data_type_nullable.h"
49
#include "core/data_type/data_type_string.h"
50
#include "core/string_ref.h"
51
#include "exec/common/stringop_substring.h"
52
#include "exec/rowid_fetcher.h"
53
#include "exec/scan/scan_node.h"
54
#include "exprs/aggregate/aggregate_function.h"
55
#include "exprs/function/function.h"
56
#include "exprs/function/simple_function_factory.h"
57
#include "exprs/vexpr.h"
58
#include "exprs/vexpr_context.h"
59
#include "exprs/vexpr_fwd.h"
60
#include "exprs/vslot_ref.h"
61
#include "format/arrow/arrow_stream_reader.h"
62
#include "format/count_reader.h"
63
#include "format/csv/csv_reader.h"
64
#include "format/json/new_json_reader.h"
65
#include "format/native/native_reader.h"
66
#include "format/orc/vorc_reader.h"
67
#include "format/parquet/vparquet_reader.h"
68
#include "format/table/es/es_http_reader.h"
69
#include "format/table/hive_reader.h"
70
#include "format/table/hudi_jni_reader.h"
71
#include "format/table/hudi_reader.h"
72
#include "format/table/iceberg_position_delete_sys_table_reader.h"
73
#include "format/table/iceberg_reader.h"
74
#include "format/table/iceberg_sys_table_jni_reader.h"
75
#include "format/table/jdbc_jni_reader.h"
76
#include "format/table/max_compute_jni_reader.h"
77
#include "format/table/paimon_cpp_reader.h"
78
#include "format/table/paimon_jni_reader.h"
79
#include "format/table/paimon_predicate_converter.h"
80
#include "format/table/paimon_reader.h"
81
#include "format/table/partition_column_filler.h"
82
#include "format/table/remote_doris_reader.h"
83
#include "format/table/transactional_hive_reader.h"
84
#include "format/table/trino_connector_jni_reader.h"
85
#include "format/text/text_reader.h"
86
#include "io/cache/block_file_cache_profile.h"
87
#include "load/group_commit/wal/wal_reader.h"
88
#include "runtime/descriptors.h"
89
#include "runtime/runtime_profile.h"
90
#include "runtime/runtime_state.h"
91
#include "service/backend_options.h"
92
93
namespace cctz {
94
class time_zone;
95
} // namespace cctz
96
namespace doris {
97
class ShardedKVCache;
98
} // namespace doris
99
100
namespace doris {
101
using namespace ErrorCode;
102
103
namespace {
104
constexpr int kIcebergPositionDeleteContent = 1;
105
constexpr int kIcebergDeletionVectorContent = 3;
106
107
6.92k
bool is_iceberg_position_deletes_sys_table(const TFileRangeDesc& range) {
108
6.92k
    return range.__isset.table_format_params &&
109
6.92k
           range.table_format_params.table_format_type == "iceberg" &&
110
6.92k
           range.table_format_params.__isset.iceberg_params &&
111
6.92k
           range.table_format_params.iceberg_params.__isset.content &&
112
6.92k
           (range.table_format_params.iceberg_params.content == kIcebergPositionDeleteContent ||
113
16
            range.table_format_params.iceberg_params.content == kIcebergDeletionVectorContent);
114
6.92k
}
115
} // namespace
116
117
const std::string FileScanner::FileReadBytesProfile = "FileReadBytes";
118
const std::string FileScanner::FileReadTimeProfile = "FileReadTime";
119
120
FileScanner::FileScanner(RuntimeState* state, FileScanLocalState* local_state, int64_t limit,
121
                         std::shared_ptr<SplitSourceConnector> split_source,
122
                         RuntimeProfile* profile, ShardedKVCache* kv_cache,
123
                         const std::unordered_map<std::string, int>* colname_to_slot_id)
124
6.89k
        : Scanner(state, local_state, limit, profile),
125
6.89k
          _split_source(split_source),
126
6.89k
          _cur_reader(nullptr),
127
6.89k
          _cur_reader_eof(false),
128
6.89k
          _kv_cache(kv_cache),
129
6.89k
          _strict_mode(false),
130
6.89k
          _col_name_to_slot_id(colname_to_slot_id) {
131
6.89k
    if (state->get_query_ctx() != nullptr &&
132
6.89k
        state->get_query_ctx()->file_scan_range_params_map.count(local_state->parent_id()) > 0) {
133
1.38k
        _params = &(state->get_query_ctx()->file_scan_range_params_map[local_state->parent_id()]);
134
5.51k
    } else {
135
        // old fe thrift protocol
136
5.51k
        _params = _split_source->get_params();
137
5.51k
    }
138
6.89k
    if (_params->__isset.strict_mode) {
139
5.51k
        _strict_mode = _params->strict_mode;
140
5.51k
    }
141
142
    // For load scanner, there are input and output tuple.
143
    // For query scanner, there is only output tuple
144
6.89k
    _input_tuple_desc = state->desc_tbl().get_tuple_descriptor(_params->src_tuple_id);
145
6.89k
    _real_tuple_desc = _input_tuple_desc == nullptr ? _output_tuple_desc : _input_tuple_desc;
146
6.89k
    _is_load = (_input_tuple_desc != nullptr);
147
6.89k
    _configure_file_scan_handlers();
148
6.89k
}
149
150
10.1k
void FileScanner::_configure_file_scan_handlers() {
151
10.1k
    if (_is_load) {
152
5.52k
        _init_src_block_handler = &FileScanner::_init_src_block_for_load;
153
5.52k
        _process_src_block_after_read_handler =
154
5.52k
                &FileScanner::_process_src_block_after_read_for_load;
155
5.52k
        _should_push_down_predicates_handler = &FileScanner::_should_push_down_predicates_for_load;
156
5.52k
        _should_enable_condition_cache_handler =
157
5.52k
                &FileScanner::_should_enable_condition_cache_for_load;
158
5.52k
    } else {
159
4.58k
        _init_src_block_handler = &FileScanner::_init_src_block_for_query;
160
4.58k
        _process_src_block_after_read_handler =
161
4.58k
                &FileScanner::_process_src_block_after_read_for_query;
162
4.58k
        _should_push_down_predicates_handler = &FileScanner::_should_push_down_predicates_for_query;
163
4.58k
        _should_enable_condition_cache_handler =
164
4.58k
                &FileScanner::_should_enable_condition_cache_for_query;
165
4.58k
    }
166
10.1k
}
167
168
6.90k
Status FileScanner::init(RuntimeState* state, const VExprContextSPtrs& conjuncts) {
169
6.90k
    RETURN_IF_ERROR(Scanner::init(state, conjuncts));
170
6.90k
    _get_block_timer =
171
6.90k
            ADD_TIMER_WITH_LEVEL(_local_state->scanner_profile(), "FileScannerGetBlockTime", 1);
172
6.90k
    _cast_to_input_block_timer = ADD_TIMER_WITH_LEVEL(_local_state->scanner_profile(),
173
6.90k
                                                      "FileScannerCastInputBlockTime", 1);
174
6.90k
    _fill_missing_columns_timer = ADD_TIMER_WITH_LEVEL(_local_state->scanner_profile(),
175
6.90k
                                                       "FileScannerFillMissingColumnTime", 1);
176
6.90k
    _pre_filter_timer =
177
6.90k
            ADD_TIMER_WITH_LEVEL(_local_state->scanner_profile(), "FileScannerPreFilterTimer", 1);
178
6.90k
    _convert_to_output_block_timer = ADD_TIMER_WITH_LEVEL(_local_state->scanner_profile(),
179
6.90k
                                                          "FileScannerConvertOuputBlockTime", 1);
180
6.90k
    _runtime_filter_partition_prune_timer = ADD_TIMER_WITH_LEVEL(
181
6.90k
            _local_state->scanner_profile(), "FileScannerRuntimeFilterPartitionPruningTime", 1);
182
6.90k
    _empty_file_counter =
183
6.90k
            ADD_COUNTER_WITH_LEVEL(_local_state->scanner_profile(), "EmptyFileNum", TUnit::UNIT, 1);
184
6.90k
    _not_found_file_counter = ADD_COUNTER_WITH_LEVEL(_local_state->scanner_profile(),
185
6.90k
                                                     "NotFoundFileNum", TUnit::UNIT, 1);
186
6.90k
    _fully_skipped_file_counter = ADD_COUNTER_WITH_LEVEL(_local_state->scanner_profile(),
187
6.90k
                                                         "FullySkippedFileNum", TUnit::UNIT, 1);
188
6.90k
    _file_counter =
189
6.90k
            ADD_COUNTER_WITH_LEVEL(_local_state->scanner_profile(), "FileNumber", TUnit::UNIT, 1);
190
191
6.90k
    _file_read_bytes_counter = ADD_COUNTER_WITH_LEVEL(_local_state->scanner_profile(),
192
6.90k
                                                      FileReadBytesProfile, TUnit::BYTES, 1);
193
6.90k
    _file_read_calls_counter = ADD_COUNTER_WITH_LEVEL(_local_state->scanner_profile(),
194
6.90k
                                                      "FileReadCalls", TUnit::UNIT, 1);
195
6.90k
    _file_read_time_counter =
196
6.90k
            ADD_TIMER_WITH_LEVEL(_local_state->scanner_profile(), FileReadTimeProfile, 1);
197
198
6.90k
    _runtime_filter_partition_pruned_range_counter =
199
6.90k
            ADD_COUNTER_WITH_LEVEL(_local_state->scanner_profile(),
200
6.90k
                                   "RuntimeFilterPartitionPrunedRangeNum", TUnit::UNIT, 1);
201
    // Keep the current file's adaptive state while also preserving the peak value across all
202
    // files handled by this scanner instance.
203
6.90k
    _adaptive_batch_predicted_rows_counter =
204
6.90k
            _local_state->scanner_profile()->AddHighWaterMarkCounter(
205
6.90k
                    "AdaptiveBatchPredictedRows", TUnit::UNIT, RuntimeProfile::ROOT_COUNTER, 1);
206
6.90k
    _adaptive_batch_actual_bytes_before_truncate_counter =
207
6.90k
            _local_state->scanner_profile()->AddHighWaterMarkCounter(
208
6.90k
                    "AdaptiveBatchActualBytesBeforeTruncate", TUnit::BYTES,
209
6.90k
                    RuntimeProfile::ROOT_COUNTER, 1);
210
6.90k
    _adaptive_batch_actual_bytes_after_truncate_counter =
211
6.90k
            _local_state->scanner_profile()->AddHighWaterMarkCounter(
212
6.90k
                    "AdaptiveBatchActualBytesAfterTruncate", TUnit::BYTES,
213
6.90k
                    RuntimeProfile::ROOT_COUNTER, 1);
214
6.90k
    _adaptive_batch_probe_count_counter = ADD_COUNTER_WITH_LEVEL(
215
6.90k
            _local_state->scanner_profile(), "AdaptiveBatchProbeCount", TUnit::UNIT, 1);
216
217
6.90k
    _file_cache_statistics.reset(new io::FileCacheStatistics());
218
6.90k
    _file_reader_stats.reset(new io::FileReaderStats());
219
220
6.90k
    RETURN_IF_ERROR(_init_io_ctx());
221
6.90k
    _io_ctx->file_cache_stats = _file_cache_statistics.get();
222
6.90k
    _io_ctx->file_reader_stats = _file_reader_stats.get();
223
6.90k
    _io_ctx->is_disposable = _state->query_options().disable_file_cache;
224
225
6.90k
    if (_is_load) {
226
5.51k
        _src_row_desc.reset(new RowDescriptor(_state->desc_tbl(),
227
5.51k
                                              std::vector<TupleId>({_input_tuple_desc->id()})));
228
        // prepare pre filters
229
5.51k
        if (_params->__isset.pre_filter_exprs_list) {
230
6
            RETURN_IF_ERROR(doris::VExpr::create_expr_trees(_params->pre_filter_exprs_list,
231
6
                                                            _pre_conjunct_ctxs));
232
5.51k
        } else if (_params->__isset.pre_filter_exprs) {
233
0
            VExprContextSPtr context;
234
0
            RETURN_IF_ERROR(doris::VExpr::create_expr_tree(_params->pre_filter_exprs, context));
235
0
            _pre_conjunct_ctxs.emplace_back(context);
236
0
        }
237
238
5.51k
        for (auto& conjunct : _pre_conjunct_ctxs) {
239
8
            RETURN_IF_ERROR(conjunct->prepare(_state, *_src_row_desc));
240
8
            RETURN_IF_ERROR(conjunct->open(_state));
241
8
        }
242
243
5.51k
        _dest_row_desc.reset(new RowDescriptor(_state->desc_tbl(),
244
5.51k
                                               std::vector<TupleId>({_output_tuple_desc->id()})));
245
5.51k
    }
246
247
6.90k
    _default_val_row_desc.reset(
248
6.90k
            new RowDescriptor(_state->desc_tbl(), std::vector<TupleId>({_real_tuple_desc->id()})));
249
250
6.90k
    return Status::OK();
251
6.90k
}
252
253
6.91k
bool FileScanner::_should_enable_adaptive_batch_size(TFileFormatType::type format_type) const {
254
    // Only enable for readers that support set_batch_size().
255
    // Table-format wrappers are covered because they delegate to native readers.
256
6.91k
    if (!config::enable_adaptive_batch_size) {
257
1
        return false;
258
1
    }
259
6.91k
    switch (format_type) {
260
825
    case TFileFormatType::FORMAT_PARQUET:
261
1.19k
    case TFileFormatType::FORMAT_ORC:
262
3.61k
    case TFileFormatType::FORMAT_CSV_PLAIN:
263
3.61k
    case TFileFormatType::FORMAT_CSV_GZ:
264
3.61k
    case TFileFormatType::FORMAT_CSV_BZ2:
265
3.61k
    case TFileFormatType::FORMAT_CSV_LZ4FRAME:
266
3.61k
    case TFileFormatType::FORMAT_CSV_LZ4BLOCK:
267
3.61k
    case TFileFormatType::FORMAT_CSV_LZOP:
268
3.61k
    case TFileFormatType::FORMAT_CSV_DEFLATE:
269
3.61k
    case TFileFormatType::FORMAT_CSV_SNAPPYBLOCK:
270
3.69k
    case TFileFormatType::FORMAT_PROTO:
271
3.69k
    case TFileFormatType::FORMAT_TEXT:
272
6.87k
    case TFileFormatType::FORMAT_JSON:
273
6.89k
    case TFileFormatType::FORMAT_JNI:
274
6.89k
        return true;
275
14
    default:
276
14
        return false;
277
6.91k
    }
278
6.91k
}
279
280
69.6k
bool FileScanner::_should_run_adaptive_batch_size() const {
281
    // Skip adaptive batch sizing for pushed-down COUNT(*): the reader is wrapped by CountReader
282
    // and only emits a single aggregated row count instead of materializing real columns, so
283
    // there is no per-row byte cost to learn from and no benefit in tuning the batch size.
284
69.6k
    return _block_size_predictor != nullptr && _get_push_down_agg_type() != TPushAggOp::type::COUNT;
285
69.6k
}
286
287
20.6k
void FileScanner::_reset_adaptive_batch_size_state() {
288
20.6k
    _block_size_predictor.reset();
289
20.6k
    COUNTER_SET(_adaptive_batch_predicted_rows_counter, int64_t(0));
290
20.6k
    COUNTER_SET(_adaptive_batch_actual_bytes_before_truncate_counter, int64_t(0));
291
20.6k
    COUNTER_SET(_adaptive_batch_actual_bytes_after_truncate_counter, int64_t(0));
292
20.6k
}
293
294
6.90k
void FileScanner::_init_adaptive_batch_size_state(TFileFormatType::type format_type) {
295
6.90k
    _reset_adaptive_batch_size_state();
296
6.90k
    if (!_should_enable_adaptive_batch_size(format_type)) {
297
15
        return;
298
15
    }
299
300
    // External file readers do not provide reliable memory-size metadata hints. Use a small probe
301
    // batch so the predictor can learn from real FileScanner output quickly.
302
6.89k
    _block_size_predictor = std::make_unique<AdaptiveBlockSizePredictor>(
303
6.89k
            _state->preferred_block_size_bytes(), 0.0, ADAPTIVE_BATCH_INITIAL_PROBE_ROWS,
304
6.89k
            _state->batch_size());
305
6.89k
}
306
307
18.0k
size_t FileScanner::_predict_reader_batch_rows() {
308
18.0k
    DCHECK(_block_size_predictor != nullptr);
309
18.0k
    size_t predicted_rows = _block_size_predictor->predict_next_rows();
310
18.0k
    COUNTER_SET(_adaptive_batch_predicted_rows_counter, static_cast<int64_t>(predicted_rows));
311
18.0k
    return predicted_rows;
312
18.0k
}
313
314
20.1k
void FileScanner::_update_adaptive_batch_size_before_truncate(const Block& block) {
315
20.1k
    if (!_should_run_adaptive_batch_size()) {
316
9.18k
        return;
317
9.18k
    }
318
319
    // Learn from the logical bytes before CHAR/VARCHAR truncation. The truncated block can be
320
    // much smaller than the data the reader and FileScanner have already materialized.
321
10.9k
    COUNTER_SET(_adaptive_batch_actual_bytes_before_truncate_counter,
322
10.9k
                static_cast<int64_t>(block.bytes()));
323
10.9k
    if (block.rows() == 0) {
324
1.02k
        return;
325
1.02k
    }
326
327
    // Count a probe only when we actually obtain the first non-empty sample that seeds history.
328
9.90k
    if (!_block_size_predictor->has_history()) {
329
6.11k
        COUNTER_UPDATE(_adaptive_batch_probe_count_counter, 1);
330
6.11k
    }
331
9.90k
    _block_size_predictor->update(block);
332
9.90k
}
333
334
20.1k
void FileScanner::_update_adaptive_batch_size_after_truncate(const Block& block) {
335
20.1k
    if (!_should_run_adaptive_batch_size()) {
336
9.19k
        return;
337
9.19k
    }
338
339
    // Keep the post-truncate size only for observability. It should not affect the next batch
340
    // because truncation happens after the upstream memory cost has already been paid.
341
10.9k
    COUNTER_SET(_adaptive_batch_actual_bytes_after_truncate_counter,
342
10.9k
                static_cast<int64_t>(block.bytes()));
343
10.9k
}
344
345
// check if the expr is a partition pruning expr
346
397
bool FileScanner::_check_partition_prune_expr(const VExprSPtr& expr) {
347
397
    if (expr->is_slot_ref()) {
348
133
        auto* slot_ref = static_cast<VSlotRef*>(expr.get());
349
133
        return _partition_slot_index_map.find(slot_ref->slot_id()) !=
350
133
               _partition_slot_index_map.end();
351
133
    }
352
264
    if (expr->is_literal()) {
353
84
        return true;
354
84
    }
355
312
    return std::ranges::all_of(expr->children(), [this](const auto& child) {
356
312
        return _check_partition_prune_expr(child);
357
312
    });
358
264
}
359
360
0
bool FileScanner::_contains_runtime_filter(const VExprContextSPtrs& conjuncts) const {
361
0
    return std::ranges::any_of(
362
0
            conjuncts, [](const auto& conjunct) { return conjunct->root()->is_rf_wrapper(); });
363
0
}
364
365
99
void FileScanner::_init_runtime_filter_partition_prune_ctxs() {
366
99
    _runtime_filter_partition_prune_ctxs.clear();
367
99
    for (auto& conjunct : _conjuncts) {
368
86
        auto impl = conjunct->root()->get_impl();
369
        // If impl is not null, which means this a conjuncts from runtime filter.
370
86
        auto expr = impl ? impl : conjunct->root();
371
        // Preserve a safe prefix of the row-level conjunct order. Considering later predicates
372
        // after an unsafe one could prune the split before the unsafe predicate is evaluated.
373
86
        if (!expr->is_safe_to_execute_on_selected_rows()) {
374
1
            break;
375
1
        }
376
85
        if (_check_partition_prune_expr(expr)) {
377
37
            _runtime_filter_partition_prune_ctxs.emplace_back(conjunct);
378
37
        }
379
85
    }
380
99
}
381
382
56
void FileScanner::_init_runtime_filter_partition_prune_block() {
383
    // init block with empty column
384
190
    for (auto const* slot_desc : _real_tuple_desc->slots()) {
385
190
        _runtime_filter_partition_prune_block.insert(
386
190
                ColumnWithTypeAndName(slot_desc->get_empty_mutable_column(),
387
190
                                      slot_desc->get_data_type_ptr(), slot_desc->col_name()));
388
190
    }
389
56
}
390
391
74
Status FileScanner::_process_runtime_filters_partition_prune(bool& can_filter_all) {
392
74
    SCOPED_TIMER(_runtime_filter_partition_prune_timer);
393
74
    if (_runtime_filter_partition_prune_ctxs.empty() || _partition_col_descs.empty()) {
394
44
        return Status::OK();
395
44
    }
396
30
    size_t partition_value_column_size = 1;
397
398
    // 1. Get partition key values to string columns.
399
30
    std::unordered_map<SlotId, MutableColumnPtr> partition_slot_id_to_column;
400
30
    for (auto const& partition_col_desc : _partition_col_descs) {
401
30
        const auto& [partition_value, partition_slot_desc] = partition_col_desc.second;
402
30
        auto data_type = partition_slot_desc->get_data_type_ptr();
403
30
        auto partition_value_column = data_type->create_column();
404
30
        auto null_it = _partition_value_is_null.find(partition_slot_desc->col_name());
405
30
        DORIS_CHECK(null_it != _partition_value_is_null.end());
406
30
        RETURN_IF_ERROR(fill_partition_column_from_path_value(
407
30
                *partition_value_column, *partition_slot_desc, partition_value,
408
30
                partition_value_column_size, null_it->second));
409
410
30
        partition_slot_id_to_column[partition_slot_desc->id()] = std::move(partition_value_column);
411
30
    }
412
413
    // 2. Fill _runtime_filter_partition_prune_block from the partition column, then execute conjuncts and filter block.
414
    // 2.1 Fill _runtime_filter_partition_prune_block from the partition column to match the conjuncts executing.
415
30
    size_t index = 0;
416
30
    bool first_column_filled = false;
417
72
    for (auto const* slot_desc : _real_tuple_desc->slots()) {
418
72
        if (partition_slot_id_to_column.find(slot_desc->id()) !=
419
72
            partition_slot_id_to_column.end()) {
420
30
            auto partition_value_column = std::move(partition_slot_id_to_column[slot_desc->id()]);
421
30
            _runtime_filter_partition_prune_block.replace_by_position(
422
30
                    index, std::move(partition_value_column));
423
30
            if (index == 0) {
424
0
                first_column_filled = true;
425
0
            }
426
30
        }
427
72
        index++;
428
72
    }
429
430
    // 2.2 Execute conjuncts.
431
30
    if (!first_column_filled) {
432
        // VExprContext.execute has an optimization, the filtering is executed when block->rows() > 0
433
        // The following process may be tricky and time-consuming, but we have no other way.
434
30
        auto column = IColumn::mutate(
435
30
                std::move(_runtime_filter_partition_prune_block.get_by_position(0).column));
436
30
        column->resize(partition_value_column_size);
437
30
        _runtime_filter_partition_prune_block.replace_by_position(0, std::move(column));
438
30
    }
439
30
    IColumn::Filter result_filter(_runtime_filter_partition_prune_block.rows(), 1);
440
30
    RETURN_IF_ERROR(VExprContext::execute_conjuncts(_runtime_filter_partition_prune_ctxs, nullptr,
441
30
                                                    &_runtime_filter_partition_prune_block,
442
30
                                                    &result_filter, &can_filter_all));
443
30
    return Status::OK();
444
30
}
445
446
343
Status FileScanner::_process_conjuncts() {
447
343
    _slot_id_to_filter_conjuncts.clear();
448
343
    _not_single_slot_filter_conjuncts.clear();
449
373
    for (auto& conjunct : _push_down_conjuncts) {
450
373
        auto impl = conjunct->root()->get_impl();
451
        // If impl is not null, which means this a conjuncts from runtime filter.
452
373
        auto cur_expr = impl ? impl : conjunct->root();
453
454
373
        std::vector<int> slot_ids;
455
373
        _get_slot_ids(cur_expr.get(), &slot_ids);
456
373
        if (slot_ids.empty()) {
457
1
            _not_single_slot_filter_conjuncts.emplace_back(conjunct);
458
1
            continue;
459
1
        }
460
372
        bool single_slot = true;
461
372
        for (int i = 1; i < slot_ids.size(); i++) {
462
32
            if (slot_ids[i] != slot_ids[0]) {
463
32
                single_slot = false;
464
32
                break;
465
32
            }
466
32
        }
467
372
        if (single_slot) {
468
340
            SlotId slot_id = slot_ids[0];
469
340
            _slot_id_to_filter_conjuncts[slot_id].emplace_back(conjunct);
470
340
        } else {
471
32
            _not_single_slot_filter_conjuncts.emplace_back(conjunct);
472
32
        }
473
372
    }
474
343
    return Status::OK();
475
343
}
476
477
1.14k
Status FileScanner::_process_late_arrival_conjuncts() {
478
1.14k
    if (_push_down_conjuncts.size() < _conjuncts.size()) {
479
341
        _push_down_conjuncts = _conjuncts;
480
        // Do not clear _conjuncts here!
481
        // We must keep it for fallback filtering, especially when mixing
482
        // Native readers (which use _push_down_conjuncts) and JNI readers (which rely on _conjuncts).
483
        // _conjuncts.clear();
484
341
        RETURN_IF_ERROR(_process_conjuncts());
485
341
    }
486
1.15k
    if (_applied_rf_num == _total_rf_num) {
487
1.15k
        _local_state->scanner_profile()->add_info_string("ApplyAllRuntimeFilters", "True");
488
1.15k
    }
489
1.14k
    return Status::OK();
490
1.14k
}
491
492
967
void FileScanner::_get_slot_ids(VExpr* expr, std::vector<int>* slot_ids) {
493
996
    for (auto& child_expr : expr->children()) {
494
996
        if (child_expr->is_slot_ref()) {
495
404
            VSlotRef* slot_ref = reinterpret_cast<VSlotRef*>(child_expr.get());
496
404
            SlotDescriptor* slot_desc = _state->desc_tbl().get_slot_descriptor(slot_ref->slot_id());
497
404
            slot_desc->set_is_predicate(true);
498
404
            slot_ids->emplace_back(slot_ref->slot_id());
499
592
        } else {
500
592
            _get_slot_ids(child_expr.get(), slot_ids);
501
592
        }
502
996
    }
503
967
}
504
505
6.91k
Status FileScanner::_open_impl(RuntimeState* state) {
506
6.91k
    RETURN_IF_CANCELLED(state);
507
6.91k
    RETURN_IF_ERROR(Scanner::_open_impl(state));
508
6.91k
    if (_local_state) {
509
6.91k
        _condition_cache_digest = _local_state->get_condition_cache_digest();
510
6.91k
    }
511
6.91k
    RETURN_IF_ERROR(_split_source->get_next(&_first_scan_range, &_current_range));
512
6.91k
    if (_first_scan_range) {
513
6.47k
        RETURN_IF_ERROR(_init_expr_ctxes());
514
6.47k
        if (_state->query_options().enable_runtime_filter_partition_prune &&
515
6.47k
            !_partition_slot_index_map.empty()) {
516
56
            _init_runtime_filter_partition_prune_ctxs();
517
56
            _init_runtime_filter_partition_prune_block();
518
56
        }
519
6.47k
    } else {
520
        // there's no scan range in split source. stop scanner directly.
521
444
        _scanner_eof = true;
522
444
    }
523
524
6.91k
    return Status::OK();
525
6.91k
}
526
527
36.3k
Status FileScanner::_get_block_impl(RuntimeState* state, Block* block, bool* eof) {
528
36.3k
    Status st = _get_block_wrapped(state, block, eof);
529
530
36.3k
    if (!st.ok()) {
531
        // add cur path in error msg for easy debugging
532
20
        return std::move(st.append(". cur path: " + get_current_scan_range_name()));
533
20
    }
534
36.3k
    return st;
535
36.3k
}
536
537
// For query:
538
//                              [exist cols]  [non-exist cols]  [col from path]  input  output
539
//                              A     B    C  D                 E
540
// _init_src_block              x     x    x  x                 x                -      x
541
// get_next_block               x     x    x  -                 -                -      x
542
// _cast_to_input_block         -     -    -  -                 -                -      -
543
// _fill_columns_from_path      -     -    -  -                 x                -      x
544
// _fill_missing_columns        -     -    -  x                 -                -      x
545
// _convert_to_output_block     -     -    -  -                 -                -      -
546
//
547
// For load:
548
//                              [exist cols]  [non-exist cols]  [col from path]  input  output
549
//                              A     B    C  D                 E
550
// _init_src_block              x     x    x  x                 x                x      -
551
// get_next_block               x     x    x  -                 -                x      -
552
// _cast_to_input_block         x     x    x  -                 -                x      -
553
// _fill_columns_from_path      -     -    -  -                 x                x      -
554
// _fill_missing_columns        -     -    -  x                 -                x      -
555
// _convert_to_output_block     -     -    -  -                 -                -      x
556
36.3k
Status FileScanner::_get_block_wrapped(RuntimeState* state, Block* block, bool* eof) {
557
36.3k
    do {
558
36.3k
        RETURN_IF_CANCELLED(state);
559
36.3k
        if (_cur_reader == nullptr || _cur_reader_eof) {
560
13.7k
            _finalize_reader_condition_cache();
561
            // The file may not exist because the file list is got from meta cache,
562
            // And the file may already be removed from storage.
563
            // Just ignore not found files.
564
13.7k
            Status st = _get_next_reader();
565
13.7k
            if (st.is<ErrorCode::NOT_FOUND>() && config::ignore_not_found_file_in_external_table) {
566
0
                _cur_reader_eof = true;
567
0
                COUNTER_UPDATE(_not_found_file_counter, 1);
568
0
                continue;
569
13.7k
            } else if (st.is<ErrorCode::END_OF_FILE>()) {
570
0
                _cur_reader_eof = true;
571
0
                COUNTER_UPDATE(_fully_skipped_file_counter, 1);
572
0
                continue;
573
13.7k
            } else if (!st) {
574
8
                return st;
575
8
            }
576
13.7k
            _init_reader_condition_cache();
577
13.7k
        }
578
579
36.3k
        if (_scanner_eof) {
580
6.87k
            *eof = true;
581
6.87k
            return Status::OK();
582
6.87k
        }
583
584
        // Init src block for load job based on the data file schema (e.g. parquet)
585
        // For query job, simply set _src_block_ptr to block.
586
29.4k
        size_t read_rows = 0;
587
29.4k
        RETURN_IF_ERROR(_init_src_block(block));
588
589
29.4k
        if (_should_run_adaptive_batch_size()) {
590
18.0k
            _cur_reader->set_batch_size(_predict_reader_batch_rows());
591
18.0k
        }
592
29.4k
        {
593
29.4k
            SCOPED_TIMER(_get_block_timer);
594
595
            // Read next block.
596
            // Some of column in block may not be filled (column not exist in file)
597
29.4k
            RETURN_IF_ERROR(
598
29.4k
                    _cur_reader->get_next_block(_src_block_ptr, &read_rows, &_cur_reader_eof));
599
29.4k
        }
600
        // use read_rows instead of _src_block_ptr->rows(), because the first column of _src_block_ptr
601
        // may not be filled after calling `get_next_block()`, so _src_block_ptr->rows() may return wrong result.
602
29.4k
        if (read_rows > 0) {
603
20.1k
            if ((!_cur_reader->count_read_rows()) && _io_ctx) {
604
9.54k
                _io_ctx->file_reader_stats->read_rows += read_rows;
605
9.54k
            }
606
            // If the push_down_agg_type is COUNT, no need to do the rest,
607
            // because we only save a number in block.
608
20.1k
            if (_get_push_down_agg_type() != TPushAggOp::type::COUNT) {
609
20.1k
                RETURN_IF_ERROR(_process_src_block_after_read(block));
610
20.1k
            }
611
20.1k
        }
612
29.4k
        break;
613
29.4k
    } while (true);
614
615
    // Update filtered rows and unselected rows for load, reset counter.
616
    // {
617
    //     state->update_num_rows_load_filtered(_counter.num_rows_filtered);
618
    //     state->update_num_rows_load_unselected(_counter.num_rows_unselected);
619
    //     _reset_counter();
620
    // }
621
29.4k
    return Status::OK();
622
36.3k
}
623
624
/**
625
 * Check whether there are complex types in parquet/orc reader in broker/stream load.
626
 * Broker/stream load will cast any type as string type, and complex types will be casted wrong.
627
 * This is a temporary method, and will be replaced by tvf.
628
 */
629
14.8k
Status FileScanner::_check_output_block_types() {
630
    // Only called from _init_src_block_for_load, so _is_load is always true.
631
14.8k
    TFileFormatType::type format_type = _params->format_type;
632
14.8k
    if (format_type == TFileFormatType::FORMAT_PARQUET ||
633
14.8k
        format_type == TFileFormatType::FORMAT_ORC) {
634
1.84k
        for (auto slot : _output_tuple_desc->slots()) {
635
1.84k
            if (is_complex_type(slot->type()->get_primitive_type())) {
636
0
                return Status::InternalError(
637
0
                        "Parquet/orc doesn't support complex types in broker/stream load, "
638
0
                        "please use tvf(table value function) to insert complex types.");
639
0
            }
640
1.84k
        }
641
64
    }
642
14.8k
    return Status::OK();
643
14.8k
}
644
645
29.4k
Status FileScanner::_init_src_block(Block* block) {
646
29.4k
    DCHECK(_init_src_block_handler != nullptr);
647
29.4k
    return (this->*_init_src_block_handler)(block);
648
29.4k
}
649
650
14.6k
Status FileScanner::_init_src_block_for_query(Block* block) {
651
14.6k
    _src_block_ptr = block;
652
653
    // Build name to index map only once on first call.
654
14.6k
    if (_src_block_name_to_idx.empty()) {
655
4.14k
        _src_block_name_to_idx = block->get_name_to_pos_map();
656
4.14k
    }
657
14.6k
    return Status::OK();
658
14.6k
}
659
660
14.8k
Status FileScanner::_init_src_block_for_load(Block* block) {
661
14.8k
    static_cast<void>(block);
662
14.8k
    RETURN_IF_ERROR(_check_output_block_types());
663
664
    // if (_src_block_init) {
665
    //     _src_block.clear_column_data();
666
    //     _src_block_ptr = &_src_block;
667
    //     return Status::OK();
668
    // }
669
670
14.8k
    _src_block.clear();
671
14.8k
    uint32_t idx = 0;
672
    // slots in _input_tuple_desc contains all slots describe in load statement, eg:
673
    // -H "columns: k1, k2, tmp1, k3 = tmp1 + 1"
674
    // _input_tuple_desc will contains: k1, k2, tmp1
675
    // and some of them are from file, such as k1 and k2, and some of them may not exist in file, such as tmp1
676
    // _input_tuple_desc also contains columns from path
677
127k
    for (auto& slot : _input_tuple_desc->slots()) {
678
127k
        DataTypePtr data_type;
679
127k
        auto it = _slot_lower_name_to_col_type.find(slot->col_name());
680
127k
        if (slot->is_skip_bitmap_col()) {
681
358
            _skip_bitmap_col_idx = idx;
682
358
        }
683
127k
        if (_params->__isset.sequence_map_col) {
684
1.29k
            if (_params->sequence_map_col == slot->col_name()) {
685
203
                _sequence_map_col_uid = slot->col_unique_id();
686
203
            }
687
1.29k
        }
688
127k
        data_type =
689
127k
                it == _slot_lower_name_to_col_type.end() ? slot->type() : make_nullable(it->second);
690
127k
        MutableColumnPtr data_column = data_type->create_column();
691
127k
        _src_block.insert(
692
127k
                ColumnWithTypeAndName(std::move(data_column), data_type, slot->col_name()));
693
127k
        _src_block_name_to_idx.emplace(slot->col_name(), idx++);
694
127k
    }
695
14.8k
    if (_params->__isset.sequence_map_col) {
696
1.81k
        for (const auto& slot : _output_tuple_desc->slots()) {
697
            // When the target table has seqeunce map column, _input_tuple_desc will not contains __DORIS_SEQUENCE_COL__,
698
            // so we should get its column unique id from _output_tuple_desc
699
1.81k
            if (slot->is_sequence_col()) {
700
211
                _sequence_col_uid = slot->col_unique_id();
701
211
            }
702
1.81k
        }
703
247
    }
704
14.8k
    _src_block_ptr = &_src_block;
705
14.8k
    _src_block_init = true;
706
14.8k
    return Status::OK();
707
14.8k
}
708
709
9.32k
Status FileScanner::_cast_to_input_block(Block* block) {
710
    // Only called from _process_src_block_after_read_for_load, so _is_load is always true.
711
9.32k
    SCOPED_TIMER(_cast_to_input_block_timer);
712
    // cast primitive type(PT0) to primitive type(PT1)
713
9.32k
    uint32_t idx = 0;
714
92.6k
    for (auto& slot_desc : _input_tuple_desc->slots()) {
715
92.6k
        if (_slot_lower_name_to_col_type.find(slot_desc->col_name()) ==
716
92.6k
            _slot_lower_name_to_col_type.end()) {
717
            // skip columns which does not exist in file
718
3.16k
            continue;
719
3.16k
        }
720
89.4k
        auto& arg = _src_block_ptr->get_by_position(_src_block_name_to_idx[slot_desc->col_name()]);
721
89.4k
        auto return_type = slot_desc->get_data_type_ptr();
722
        // remove nullable here, let the get_function decide whether nullable
723
89.4k
        auto data_type = get_data_type_with_default_argument(remove_nullable(return_type));
724
89.4k
        ColumnsWithTypeAndName arguments {
725
89.4k
                arg, {data_type->create_column(), data_type, slot_desc->col_name()}};
726
89.4k
        auto func_cast =
727
89.4k
                SimpleFunctionFactory::instance().get_function("CAST", arguments, return_type, {});
728
89.4k
        if (!func_cast) {
729
0
            return Status::InternalError("Function CAST[arg={}, col name={}, return={}] not found!",
730
0
                                         arg.type->get_name(), slot_desc->col_name(),
731
0
                                         return_type->get_name());
732
0
        }
733
89.4k
        idx = _src_block_name_to_idx[slot_desc->col_name()];
734
89.4k
        DCHECK(_state != nullptr);
735
89.4k
        auto ctx = FunctionContext::create_context(_state, {}, {});
736
89.4k
        RETURN_IF_ERROR(
737
89.4k
                func_cast->execute(ctx.get(), *_src_block_ptr, {idx}, idx, arg.column->size()));
738
89.4k
        _src_block_ptr->get_by_position(idx).type = std::move(return_type);
739
89.4k
    }
740
9.32k
    return Status::OK();
741
9.32k
}
742
743
9.32k
Status FileScanner::_pre_filter_src_block() {
744
    // Only called from _process_src_block_after_read_for_load, so _is_load is always true.
745
9.32k
    if (!_pre_conjunct_ctxs.empty()) {
746
56
        SCOPED_TIMER(_pre_filter_timer);
747
56
        auto origin_column_num = _src_block_ptr->columns();
748
56
        auto old_rows = _src_block_ptr->rows();
749
56
        RETURN_IF_ERROR(
750
56
                VExprContext::filter_block(_pre_conjunct_ctxs, _src_block_ptr, origin_column_num));
751
56
        _counter.num_rows_unselected += old_rows - _src_block_ptr->rows();
752
56
    }
753
9.32k
    return Status::OK();
754
9.32k
}
755
756
9.32k
Status FileScanner::_convert_to_output_block(Block* block) {
757
    // Only called from _process_src_block_after_read_for_load, so _is_load is always true.
758
9.32k
    SCOPED_TIMER(_convert_to_output_block_timer);
759
    // The block is passed from scanner context's free blocks,
760
    // which is initialized by output columns
761
    // so no need to clear it
762
    // block->clear();
763
764
9.32k
    int ctx_idx = 0;
765
9.32k
    size_t rows = _src_block_ptr->rows();
766
9.32k
    auto filter_column = ColumnUInt8::create(rows, 1);
767
9.32k
    auto& filter_map = filter_column->get_data();
768
769
9.32k
    auto scoped_mutable_output_block =
770
9.32k
            VectorizedUtils::build_scoped_mutable_mem_reuse_block(block, *_dest_row_desc);
771
9.32k
    auto& mutable_output_columns = scoped_mutable_output_block.mutable_columns();
772
9.32k
    Columns output_columns(mutable_output_columns.size());
773
774
9.32k
    std::vector<BitmapValue>* skip_bitmaps {nullptr};
775
9.32k
    MutableColumnPtr skip_bitmap_column;
776
9.32k
    if (_should_process_skip_bitmap_col()) {
777
201
        skip_bitmap_column = IColumn::mutate(
778
201
                std::move(_src_block_ptr->get_by_position(_skip_bitmap_col_idx).column));
779
201
        auto* skip_bitmap_nullable_col_ptr = assert_cast<ColumnNullable*>(skip_bitmap_column.get());
780
201
        skip_bitmaps = &(assert_cast<ColumnBitmap*>(
781
201
                                 skip_bitmap_nullable_col_ptr->get_nested_column_ptr().get())
782
201
                                 ->get_data());
783
        // NOTE:
784
        // - If the table has sequence type column, __DORIS_SEQUENCE_COL__ will be put in _input_tuple_desc, so whether
785
        //   __DORIS_SEQUENCE_COL__ will be marked in skip bitmap depends on whether it's specified in that row
786
        // - If the table has sequence map column, __DORIS_SEQUENCE_COL__ will not be put in _input_tuple_desc,
787
        //   so __DORIS_SEQUENCE_COL__ will be ommited if it't specified in a row and will not be marked in skip bitmap.
788
        //   So we should mark __DORIS_SEQUENCE_COL__ in skip bitmap here if the corresponding sequence map column us marked
789
201
        if (_sequence_map_col_uid != -1) {
790
352
            for (int j = 0; j < rows; ++j) {
791
328
                if ((*skip_bitmaps)[j].contains(_sequence_map_col_uid)) {
792
137
                    (*skip_bitmaps)[j].add(_sequence_col_uid);
793
137
                }
794
328
            }
795
24
        }
796
201
        _src_block_ptr->replace_by_position(_skip_bitmap_col_idx, std::move(skip_bitmap_column));
797
201
    }
798
799
    // for (auto slot_desc : _output_tuple_desc->slots()) {
800
110k
    for (int j = 0; j < mutable_output_columns.size(); ++j) {
801
101k
        auto* slot_desc = _output_tuple_desc->slots()[j];
802
101k
        int dest_index = ctx_idx;
803
101k
        ColumnPtr column_ptr;
804
805
101k
        auto& ctx = _dest_vexpr_ctx[dest_index];
806
        // PT1 => dest primitive type
807
101k
        RETURN_IF_ERROR(ctx->execute(_src_block_ptr, column_ptr));
808
        // column_ptr maybe a ColumnConst, convert it to a normal column
809
101k
        column_ptr = column_ptr->convert_to_full_column_if_const();
810
101k
        DCHECK(column_ptr);
811
812
        // because of src_slot_desc is always be nullable, so the column_ptr after do dest_expr
813
        // is likely to be nullable
814
101k
        if (LIKELY(is_column_nullable(*column_ptr))) {
815
92.8k
            const auto* nullable_column = reinterpret_cast<const ColumnNullable*>(column_ptr.get());
816
174M
            for (int i = 0; i < rows; ++i) {
817
175M
                if (filter_map[i] && nullable_column->is_null_at(i)) {
818
                    // skip checks for non-mentioned columns in flexible partial update
819
2.16M
                    if (skip_bitmaps == nullptr ||
820
2.16M
                        !skip_bitmaps->at(i).contains(slot_desc->col_unique_id())) {
821
                        // clang-format off
822
2.11M
                        if (_strict_mode && (_src_slot_descs_order_by_dest[dest_index]) &&
823
2.11M
                            !_src_block_ptr->get_by_position(_dest_slot_to_src_slot_index[dest_index]).column->is_null_at(i)) {
824
5.49k
                            filter_map[i] = false;
825
5.49k
                            RETURN_IF_ERROR(_state->append_error_msg_to_file(
826
5.49k
                                [&]() -> std::string {
827
5.49k
                                    return _src_block_ptr->dump_one_line(i, _num_of_columns_from_file);
828
5.49k
                                },
829
5.49k
                                [&]() -> std::string {
830
5.49k
                                    auto raw_value =
831
5.49k
                                            _src_block_ptr->get_by_position(_dest_slot_to_src_slot_index[dest_index]).column->get_data_at(i);
832
5.49k
                                    std::string raw_string = raw_value.to_string();
833
5.49k
                                    fmt::memory_buffer error_msg;
834
5.49k
                                    fmt::format_to(error_msg,"column({}) value is incorrect while strict mode is {}, src value is {}",
835
5.49k
                                            slot_desc->col_name(), _strict_mode, raw_string);
836
5.49k
                                    return fmt::to_string(error_msg);
837
5.49k
                                }));
838
2.11M
                        } else if (!slot_desc->is_nullable()) {
839
648
                            filter_map[i] = false;
840
648
                            RETURN_IF_ERROR(_state->append_error_msg_to_file(
841
648
                                [&]() -> std::string {
842
648
                                    return _src_block_ptr->dump_one_line(i, _num_of_columns_from_file);
843
648
                                },
844
648
                                [&]() -> std::string {
845
648
                                    fmt::memory_buffer error_msg;
846
648
                                    fmt::format_to(error_msg, "column({}) values is null while columns is not nullable", slot_desc->col_name());
847
648
                                    return fmt::to_string(error_msg);
848
648
                                }));
849
648
                        }
850
                        // clang-format on
851
2.11M
                    }
852
2.16M
                }
853
174M
            }
854
92.8k
            if (!slot_desc->is_nullable()) {
855
49.4k
                column_ptr = remove_nullable(column_ptr);
856
49.4k
            }
857
92.8k
        } else if (slot_desc->is_nullable()) {
858
115
            column_ptr = make_nullable(column_ptr);
859
115
        }
860
101k
        DORIS_CHECK_EQ(column_ptr->size(), rows);
861
101k
        if (slot_desc->type()->get_primitive_type() == PrimitiveType::TYPE_VARIANT) {
862
370
            DORIS_CHECK_EQ(ctx->root()->data_type()->get_primitive_type(),
863
370
                           PrimitiveType::TYPE_VARIANT);
864
            // CAST to Variant may produce a scalar Variant expression result. Insert it into the
865
            // destination Variant column to normalize its internal structure before publication.
866
370
            mutable_output_columns[j]->insert_range_from(*column_ptr, 0, rows);
867
100k
        } else {
868
100k
            output_columns[j] = std::move(column_ptr);
869
100k
        }
870
101k
        ctx_idx++;
871
101k
    }
872
9.32k
    scoped_mutable_output_block.restore();
873
110k
    for (size_t i = 0; i < output_columns.size(); ++i) {
874
101k
        if (output_columns[i]) {
875
100k
            block->replace_by_position(i, std::move(output_columns[i]));
876
100k
        }
877
101k
    }
878
879
    // Clear the source references before filtering so directly published columns can become
880
    // exclusive.
881
9.32k
    _src_block_ptr->clear();
882
883
9.32k
    size_t dest_size = block->columns();
884
    // do filter
885
9.32k
    block->insert(ColumnWithTypeAndName(std::move(filter_column), std::make_shared<DataTypeUInt8>(),
886
9.32k
                                        "filter column"));
887
9.32k
    RETURN_IF_ERROR(Block::filter_block(block, dest_size, dest_size));
888
889
9.32k
    _counter.num_rows_filtered += rows - block->rows();
890
9.32k
    return Status::OK();
891
9.32k
}
892
893
20.1k
Status FileScanner::_process_src_block_after_read(Block* block) {
894
20.1k
    DCHECK(_process_src_block_after_read_handler != nullptr);
895
20.1k
    return (this->*_process_src_block_after_read_handler)(block);
896
20.1k
}
897
898
10.7k
Status FileScanner::_process_src_block_after_read_for_query(Block* block) {
899
10.7k
    _update_adaptive_batch_size_before_truncate(*block);
900
901
    // Truncate CHAR/VARCHAR columns when target size is smaller than file schema.
902
    // This is needed for external table queries with truncate_char_or_varchar_columns=true.
903
10.7k
    RETURN_IF_ERROR(_truncate_char_or_varchar_columns(block));
904
905
10.7k
    _update_adaptive_batch_size_after_truncate(*block);
906
10.7k
    return Status::OK();
907
10.7k
}
908
909
9.32k
Status FileScanner::_process_src_block_after_read_for_load(Block* block) {
910
    // Convert the src block columns type in-place.
911
9.32k
    RETURN_IF_ERROR(_cast_to_input_block(block));
912
    // All readers fill partition and missing columns inside get_next_block:
913
    //   ORC/Parquet: in _do_get_next_block via on_fill_partition_columns/on_fill_missing_columns
914
    //   CSV/JSON/others: in on_after_read_block via fill_remaining_columns
915
    // Assert all columns have consistent row counts.
916
9.32k
    if (_src_block_ptr->columns() > 0) {
917
9.32k
        size_t rows = _src_block_ptr->get_by_position(0).column->size();
918
92.6k
        for (size_t i = 1; i < _src_block_ptr->columns(); ++i) {
919
83.3k
            DCHECK_EQ(_src_block_ptr->get_by_position(i).column->size(), rows)
920
0
                    << "Column " << _src_block_ptr->get_by_position(i).name << " has "
921
0
                    << _src_block_ptr->get_by_position(i).column->size() << " rows, expected "
922
0
                    << rows;
923
83.3k
        }
924
9.32k
    }
925
    // Apply _pre_conjunct_ctxs to filter src block.
926
9.32k
    RETURN_IF_ERROR(_pre_filter_src_block());
927
928
    // Convert src block to output block (dest block), then apply filters.
929
9.32k
    RETURN_IF_ERROR(_convert_to_output_block(block));
930
931
9.32k
    _update_adaptive_batch_size_before_truncate(*block);
932
933
    // Truncate CHAR/VARCHAR columns when target size is smaller than file schema.
934
9.32k
    RETURN_IF_ERROR(_truncate_char_or_varchar_columns(block));
935
936
9.32k
    _update_adaptive_batch_size_after_truncate(*block);
937
9.32k
    return Status::OK();
938
9.32k
}
939
940
20.1k
Status FileScanner::_truncate_char_or_varchar_columns(Block* block) {
941
    // Truncate char columns or varchar columns if size is smaller than file columns
942
    // or not found in the file column schema.
943
20.1k
    if (!_state->query_options().truncate_char_or_varchar_columns) {
944
20.1k
        return Status::OK();
945
20.1k
    }
946
18.4E
    int idx = 0;
947
18.4E
    for (auto* slot_desc : _real_tuple_desc->slots()) {
948
0
        const auto& type = slot_desc->type();
949
0
        if (type->get_primitive_type() != TYPE_VARCHAR && type->get_primitive_type() != TYPE_CHAR) {
950
0
            ++idx;
951
0
            continue;
952
0
        }
953
0
        auto iter = _source_file_col_name_types.find(slot_desc->col_name());
954
0
        if (iter != _source_file_col_name_types.end()) {
955
0
            const auto file_type_desc = _source_file_col_name_types[slot_desc->col_name()];
956
0
            int l = -1;
957
0
            if (auto* ftype = check_and_get_data_type<DataTypeString>(
958
0
                        remove_nullable(file_type_desc).get())) {
959
0
                l = ftype->len();
960
0
            }
961
0
            if ((assert_cast<const DataTypeString*>(remove_nullable(type).get())->len() > 0) &&
962
0
                (assert_cast<const DataTypeString*>(remove_nullable(type).get())->len() < l ||
963
0
                 l < 0)) {
964
0
                _truncate_char_or_varchar_column(
965
0
                        block, idx,
966
0
                        assert_cast<const DataTypeString*>(remove_nullable(type).get())->len());
967
0
            }
968
0
        } else {
969
0
            _truncate_char_or_varchar_column(
970
0
                    block, idx,
971
0
                    assert_cast<const DataTypeString*>(remove_nullable(type).get())->len());
972
0
        }
973
0
        ++idx;
974
0
    }
975
18.4E
    return Status::OK();
976
20.1k
}
977
978
// VARCHAR substring(VARCHAR str, INT pos[, INT len])
979
0
void FileScanner::_truncate_char_or_varchar_column(Block* block, int idx, int len) {
980
0
    auto int_type = std::make_shared<DataTypeInt32>();
981
0
    uint32_t num_columns_without_result = block->columns();
982
0
    const ColumnNullable* col_nullable =
983
0
            assert_cast<const ColumnNullable*>(block->get_by_position(idx).column.get());
984
0
    const ColumnPtr& string_column_ptr = col_nullable->get_nested_column_ptr();
985
0
    ColumnPtr null_map_column_ptr = col_nullable->get_null_map_column_ptr();
986
0
    block->replace_by_position(idx, std::move(string_column_ptr));
987
0
    block->insert({int_type->create_column_const(block->rows(), to_field<TYPE_INT>(1)), int_type,
988
0
                   "const 1"}); // pos is 1
989
0
    block->insert({int_type->create_column_const(block->rows(), to_field<TYPE_INT>(len)), int_type,
990
0
                   fmt::format("const {}", len)});                          // len
991
0
    block->insert({nullptr, std::make_shared<DataTypeString>(), "result"}); // result column
992
0
    ColumnNumbers temp_arguments(3);
993
0
    temp_arguments[0] = idx;                            // str column
994
0
    temp_arguments[1] = num_columns_without_result;     // pos
995
0
    temp_arguments[2] = num_columns_without_result + 1; // len
996
0
    uint32_t result_column_id = num_columns_without_result + 2;
997
998
0
    SubstringUtil::substring_execute(*block, temp_arguments, result_column_id, block->rows());
999
0
    auto res = ColumnNullable::create(block->get_by_position(result_column_id).column,
1000
0
                                      null_map_column_ptr);
1001
0
    block->replace_by_position(idx, std::move(res));
1002
0
    Block::erase_useless_column(block, num_columns_without_result);
1003
0
}
1004
1005
16
std::shared_ptr<segment_v2::RowIdColumnIteratorV2> FileScanner::_create_row_id_column_iterator() {
1006
16
    auto& id_file_map = _state->get_id_file_map();
1007
16
    auto file_id = id_file_map->get_file_mapping_id(
1008
16
            std::make_shared<FileMapping>(((FileScanLocalState*)_local_state)->parent_id(),
1009
16
                                          _current_range, _should_enable_file_meta_cache()));
1010
16
    return std::make_shared<RowIdColumnIteratorV2>(IdManager::ID_VERSION,
1011
16
                                                   BackendOptions::get_backend_id(), file_id);
1012
16
}
1013
1014
10.1k
void FileScanner::_fill_base_init_context(ReaderInitContext* ctx) {
1015
10.1k
    ctx->column_descs = &_column_descs;
1016
10.1k
    ctx->col_name_to_block_idx = &_src_block_name_to_idx;
1017
10.1k
    ctx->state = _state;
1018
10.1k
    ctx->tuple_descriptor = _real_tuple_desc;
1019
10.1k
    ctx->row_descriptor = _default_val_row_desc.get();
1020
10.1k
    ctx->params = _params;
1021
10.1k
    ctx->range = &_current_range;
1022
10.1k
    ctx->table_info_node = TableSchemaChangeHelper::ConstNode::get_instance();
1023
10.1k
    ctx->push_down_agg_type = _get_push_down_agg_type();
1024
10.1k
}
1025
1026
13.8k
Status FileScanner::_get_next_reader() {
1027
13.8k
    while (true) {
1028
13.8k
        if (_cur_reader) {
1029
6.88k
            _cur_reader->collect_profile_before_close();
1030
6.88k
            RETURN_IF_ERROR(_cur_reader->close());
1031
6.88k
            _state->update_num_finished_scan_range(1);
1032
6.88k
        }
1033
13.8k
        _cur_reader.reset(nullptr);
1034
13.8k
        _reset_adaptive_batch_size_state();
1035
13.8k
        _src_block_init = false;
1036
13.8k
        bool has_next = _first_scan_range;
1037
13.8k
        if (!_first_scan_range) {
1038
7.32k
            RETURN_IF_ERROR(_split_source->get_next(&has_next, &_current_range));
1039
7.32k
        }
1040
13.8k
        _first_scan_range = false;
1041
13.8k
        if (!has_next || _should_stop) {
1042
6.87k
            _scanner_eof = true;
1043
6.87k
            return Status::OK();
1044
6.87k
        }
1045
1046
6.92k
        const TFileRangeDesc& range = _current_range;
1047
6.92k
        _current_range_path = range.path;
1048
1049
6.92k
        if (!_partition_slot_index_map.empty()) {
1050
            // we need get partition columns first for runtime filter partition pruning
1051
74
            RETURN_IF_ERROR(_generate_partition_columns());
1052
1053
74
            if (_state->query_options().enable_runtime_filter_partition_prune) {
1054
                // if enable_runtime_filter_partition_prune is true, we need to check whether this range can be filtered out
1055
                // by runtime filter partition prune
1056
74
                if (_push_down_conjuncts.size() < _conjuncts.size()) {
1057
                    // there are new runtime filters, need to re-init runtime filter partition pruning ctxs
1058
42
                    _init_runtime_filter_partition_prune_ctxs();
1059
42
                }
1060
1061
74
                bool can_filter_all = false;
1062
74
                RETURN_IF_ERROR(_process_runtime_filters_partition_prune(can_filter_all));
1063
74
                if (can_filter_all) {
1064
                    // this range can be filtered out by runtime filter partition pruning
1065
                    // so we need to skip this range
1066
0
                    COUNTER_UPDATE(_runtime_filter_partition_pruned_range_counter, 1);
1067
0
                    continue;
1068
0
                }
1069
74
            }
1070
74
        }
1071
1072
        // create reader for specific format
1073
6.92k
        Status init_status = Status::OK();
1074
6.92k
        TFileFormatType::type format_type = _get_current_format_type();
1075
6.92k
        const bool is_position_deletes_sys_table = is_iceberg_position_deletes_sys_table(range);
1076
        // for compatibility, this logic is deprecated in 3.1
1077
6.92k
        if (format_type == TFileFormatType::FORMAT_JNI && range.__isset.table_format_params) {
1078
16
            if (range.table_format_params.table_format_type == "paimon" &&
1079
16
                !range.table_format_params.paimon_params.__isset.paimon_split) {
1080
                // use native reader
1081
0
                auto format = range.table_format_params.paimon_params.file_format;
1082
0
                if (format == "orc") {
1083
0
                    format_type = TFileFormatType::FORMAT_ORC;
1084
0
                } else if (format == "parquet") {
1085
0
                    format_type = TFileFormatType::FORMAT_PARQUET;
1086
0
                } else {
1087
0
                    return Status::InternalError("Not supported paimon file format: {}", format);
1088
0
                }
1089
0
            }
1090
16
        }
1091
1092
6.92k
        bool push_down_predicates = _should_push_down_predicates(format_type);
1093
6.92k
        bool need_to_get_parsed_schema = false;
1094
6.92k
        switch (format_type) {
1095
17
        case TFileFormatType::FORMAT_JNI: {
1096
17
            ReaderInitContext jni_ctx;
1097
17
            _fill_base_init_context(&jni_ctx);
1098
1099
17
            if (range.__isset.table_format_params &&
1100
17
                range.table_format_params.table_format_type == "max_compute") {
1101
0
                const auto* mc_desc = static_cast<const MaxComputeTableDescriptor*>(
1102
0
                        _real_tuple_desc->table_desc());
1103
0
                if (!mc_desc->init_status()) {
1104
0
                    return mc_desc->init_status();
1105
0
                }
1106
0
                std::unique_ptr<MaxComputeJniReader> mc_reader = MaxComputeJniReader::create_unique(
1107
0
                        mc_desc, range.table_format_params.max_compute_params, _file_slot_descs,
1108
0
                        range, _state, _profile);
1109
0
                init_status = static_cast<GenericReader*>(mc_reader.get())->init_reader(&jni_ctx);
1110
0
                _cur_reader = std::move(mc_reader);
1111
17
            } else if (range.__isset.table_format_params &&
1112
17
                       range.table_format_params.table_format_type == "paimon") {
1113
2
                const auto& paimon_params = range.table_format_params.paimon_params;
1114
2
                bool use_paimon_cpp_reader = false;
1115
2
                if (paimon_params.__isset.reader_type) {
1116
2
                    switch (paimon_params.reader_type) {
1117
0
                    case TPaimonReaderType::PAIMON_CPP:
1118
0
                        use_paimon_cpp_reader = true;
1119
0
                        break;
1120
2
                    case TPaimonReaderType::PAIMON_JNI:
1121
2
                        break;
1122
0
                    case TPaimonReaderType::PAIMON_NATIVE:
1123
0
                        return Status::InternalError(
1124
0
                                "invalid PAIMON_NATIVE reader_type for paimon FORMAT_JNI split, "
1125
0
                                "possibly caused by FE/BE protocol mismatch");
1126
0
                    default:
1127
0
                        return Status::InternalError(
1128
0
                                "unknown paimon reader_type for paimon FORMAT_JNI split, possibly "
1129
0
                                "caused by FE/BE protocol mismatch");
1130
2
                    }
1131
2
                } else {
1132
                    // TODO: Remove this fallback after all FE versions set TPaimonReaderType.
1133
0
                    use_paimon_cpp_reader =
1134
0
                            _state->query_options().__isset.enable_paimon_cpp_reader &&
1135
0
                            _state->query_options().enable_paimon_cpp_reader;
1136
0
                }
1137
2
                if (use_paimon_cpp_reader) {
1138
0
                    auto cpp_reader = PaimonCppReader::create_unique(_file_slot_descs, _state,
1139
0
                                                                     _profile, range, _params);
1140
0
                    if (!_is_load && !_push_down_conjuncts.empty()) {
1141
0
                        PaimonPredicateConverter predicate_converter(_file_slot_descs, _state);
1142
0
                        auto predicate = predicate_converter.build(_push_down_conjuncts);
1143
0
                        if (predicate) {
1144
0
                            cpp_reader->set_predicate(std::move(predicate));
1145
0
                        }
1146
0
                    }
1147
0
                    init_status =
1148
0
                            static_cast<GenericReader*>(cpp_reader.get())->init_reader(&jni_ctx);
1149
0
                    _cur_reader = std::move(cpp_reader);
1150
2
                } else {
1151
2
                    auto paimon_reader = PaimonJniReader::create_unique(_file_slot_descs, _state,
1152
2
                                                                        _profile, range, _params);
1153
2
                    init_status =
1154
2
                            static_cast<GenericReader*>(paimon_reader.get())->init_reader(&jni_ctx);
1155
2
                    _cur_reader = std::move(paimon_reader);
1156
2
                }
1157
15
            } else if (range.__isset.table_format_params &&
1158
15
                       range.table_format_params.table_format_type == "hudi") {
1159
0
                auto hudi_reader = HudiJniReader::create_unique(
1160
0
                        *_params, range.table_format_params.hudi_params, _file_slot_descs, _state,
1161
0
                        _profile);
1162
0
                init_status = static_cast<GenericReader*>(hudi_reader.get())->init_reader(&jni_ctx);
1163
0
                _cur_reader = std::move(hudi_reader);
1164
15
            } else if (range.__isset.table_format_params &&
1165
15
                       range.table_format_params.table_format_type == "trino_connector") {
1166
0
                auto trino_reader = TrinoConnectorJniReader::create_unique(_file_slot_descs, _state,
1167
0
                                                                           _profile, range);
1168
0
                init_status =
1169
0
                        static_cast<GenericReader*>(trino_reader.get())->init_reader(&jni_ctx);
1170
0
                _cur_reader = std::move(trino_reader);
1171
15
            } else if (range.__isset.table_format_params &&
1172
15
                       range.table_format_params.table_format_type == "jdbc") {
1173
                // Extract jdbc params from table_format_params
1174
0
                std::map<std::string, std::string> jdbc_params(
1175
0
                        range.table_format_params.jdbc_params.begin(),
1176
0
                        range.table_format_params.jdbc_params.end());
1177
0
                auto jdbc_reader = JdbcJniReader::create_unique(_file_slot_descs, _state, _profile,
1178
0
                                                                jdbc_params);
1179
0
                init_status = static_cast<GenericReader*>(jdbc_reader.get())->init_reader(&jni_ctx);
1180
0
                _cur_reader = std::move(jdbc_reader);
1181
15
            } else if (range.__isset.table_format_params &&
1182
15
                       range.table_format_params.table_format_type == "iceberg") {
1183
14
                auto iceberg_sys_reader = IcebergSysTableJniReader::create_unique(
1184
14
                        _file_slot_descs, _state, _profile, range, _params);
1185
14
                init_status = static_cast<GenericReader*>(iceberg_sys_reader.get())
1186
14
                                      ->init_reader(&jni_ctx);
1187
14
                _cur_reader = std::move(iceberg_sys_reader);
1188
14
            }
1189
            // Set col_name_to_block_idx for JNI readers to avoid repeated map creation
1190
17
            if (_cur_reader) {
1191
16
                if (auto* jni_reader = dynamic_cast<JniReader*>(_cur_reader.get())) {
1192
16
                    jni_reader->set_col_name_to_block_idx(&_src_block_name_to_idx);
1193
16
                }
1194
16
            }
1195
17
            break;
1196
17
        }
1197
835
        case TFileFormatType::FORMAT_PARQUET: {
1198
835
            auto file_meta_cache_ptr = _should_enable_file_meta_cache()
1199
835
                                               ? ExecEnv::GetInstance()->file_meta_cache()
1200
835
                                               : nullptr;
1201
835
            if (is_position_deletes_sys_table) {
1202
14
                ReaderInitContext ctx;
1203
14
                _fill_base_init_context(&ctx);
1204
14
                auto reader = IcebergPositionDeleteSysTableReader::create_unique(
1205
14
                        _file_slot_descs, _state, _profile, range, _params, _io_ctx,
1206
14
                        file_meta_cache_ptr);
1207
14
                init_status = static_cast<GenericReader*>(reader.get())->init_reader(&ctx);
1208
14
                _cur_reader = std::move(reader);
1209
14
                need_to_get_parsed_schema = false;
1210
14
                break;
1211
14
            }
1212
821
            if (push_down_predicates) {
1213
795
                RETURN_IF_ERROR(_process_late_arrival_conjuncts());
1214
795
            }
1215
821
            RETURN_IF_ERROR(_init_parquet_reader(file_meta_cache_ptr));
1216
1217
815
            need_to_get_parsed_schema = true;
1218
815
            break;
1219
821
        }
1220
369
        case TFileFormatType::FORMAT_ORC: {
1221
369
            auto file_meta_cache_ptr = _should_enable_file_meta_cache()
1222
369
                                               ? ExecEnv::GetInstance()->file_meta_cache()
1223
369
                                               : nullptr;
1224
369
            if (is_position_deletes_sys_table) {
1225
2
                ReaderInitContext ctx;
1226
2
                _fill_base_init_context(&ctx);
1227
2
                auto reader = IcebergPositionDeleteSysTableReader::create_unique(
1228
2
                        _file_slot_descs, _state, _profile, range, _params, _io_ctx,
1229
2
                        file_meta_cache_ptr);
1230
2
                init_status = static_cast<GenericReader*>(reader.get())->init_reader(&ctx);
1231
2
                _cur_reader = std::move(reader);
1232
2
                need_to_get_parsed_schema = false;
1233
2
                break;
1234
2
            }
1235
367
            if (push_down_predicates) {
1236
354
                RETURN_IF_ERROR(_process_late_arrival_conjuncts());
1237
354
            }
1238
367
            RETURN_IF_ERROR(_init_orc_reader(file_meta_cache_ptr));
1239
1240
367
            need_to_get_parsed_schema = true;
1241
367
            break;
1242
367
        }
1243
2.41k
        case TFileFormatType::FORMAT_CSV_PLAIN:
1244
2.41k
        case TFileFormatType::FORMAT_CSV_GZ:
1245
2.41k
        case TFileFormatType::FORMAT_CSV_BZ2:
1246
2.41k
        case TFileFormatType::FORMAT_CSV_LZ4FRAME:
1247
2.41k
        case TFileFormatType::FORMAT_CSV_LZ4BLOCK:
1248
2.41k
        case TFileFormatType::FORMAT_CSV_LZOP:
1249
2.41k
        case TFileFormatType::FORMAT_CSV_DEFLATE:
1250
2.41k
        case TFileFormatType::FORMAT_CSV_SNAPPYBLOCK:
1251
2.49k
        case TFileFormatType::FORMAT_PROTO: {
1252
2.49k
            auto reader = CsvReader::create_unique(_state, _profile, &_counter, *_params, range,
1253
2.49k
                                                   _file_slot_descs, _state->batch_size(), nullptr,
1254
2.49k
                                                   _io_ctx);
1255
2.49k
            CsvInitContext csv_ctx;
1256
2.49k
            _fill_base_init_context(&csv_ctx);
1257
2.49k
            csv_ctx.is_load = _is_load;
1258
2.49k
            init_status = static_cast<GenericReader*>(reader.get())->init_reader(&csv_ctx);
1259
2.49k
            _cur_reader = std::move(reader);
1260
2.49k
            break;
1261
2.41k
        }
1262
3
        case TFileFormatType::FORMAT_TEXT: {
1263
3
            auto reader = TextReader::create_unique(_state, _profile, &_counter, *_params, range,
1264
3
                                                    _file_slot_descs, _state->batch_size(), nullptr,
1265
3
                                                    _io_ctx);
1266
3
            CsvInitContext text_ctx;
1267
3
            _fill_base_init_context(&text_ctx);
1268
3
            text_ctx.is_load = _is_load;
1269
3
            init_status = static_cast<GenericReader*>(reader.get())->init_reader(&text_ctx);
1270
3
            _cur_reader = std::move(reader);
1271
3
            break;
1272
2.41k
        }
1273
3.18k
        case TFileFormatType::FORMAT_JSON: {
1274
3.18k
            _cur_reader = NewJsonReader::create_unique(_state, _profile, &_counter, *_params, range,
1275
3.18k
                                                       _file_slot_descs, &_scanner_eof,
1276
3.18k
                                                       _state->batch_size(), nullptr, _io_ctx);
1277
3.18k
            JsonInitContext json_ctx;
1278
3.18k
            _fill_base_init_context(&json_ctx);
1279
3.18k
            json_ctx.col_default_value_ctx = &_col_default_value_ctx;
1280
3.18k
            json_ctx.is_load = _is_load;
1281
3.18k
            init_status = _cur_reader->init_reader(&json_ctx);
1282
3.18k
            break;
1283
2.41k
        }
1284
1285
0
        case TFileFormatType::FORMAT_WAL: {
1286
0
            _cur_reader = WalReader::create_unique(_state);
1287
0
            WalInitContext wal_ctx;
1288
0
            _fill_base_init_context(&wal_ctx);
1289
0
            wal_ctx.output_tuple_descriptor = _output_tuple_desc;
1290
0
            init_status = _cur_reader->init_reader(&wal_ctx);
1291
0
            break;
1292
2.41k
        }
1293
2
        case TFileFormatType::FORMAT_NATIVE: {
1294
2
            auto reader = NativeReader::create_unique(_profile, *_params, range, _io_ctx, _state);
1295
2
            ReaderInitContext native_ctx;
1296
2
            _fill_base_init_context(&native_ctx);
1297
2
            init_status = static_cast<GenericReader*>(reader.get())->init_reader(&native_ctx);
1298
2
            _cur_reader = std::move(reader);
1299
2
            need_to_get_parsed_schema = false;
1300
2
            break;
1301
2.41k
        }
1302
12
        case TFileFormatType::FORMAT_ARROW: {
1303
12
            ReaderInitContext arrow_ctx;
1304
12
            _fill_base_init_context(&arrow_ctx);
1305
1306
12
            if (range.__isset.table_format_params &&
1307
12
                range.table_format_params.table_format_type == "remote_doris") {
1308
0
                auto doris_reader =
1309
0
                        RemoteDorisReader::create_unique(_file_slot_descs, _state, _profile, range);
1310
0
                init_status =
1311
0
                        static_cast<GenericReader*>(doris_reader.get())->init_reader(&arrow_ctx);
1312
0
                if (doris_reader) {
1313
0
                    doris_reader->set_col_name_to_block_idx(&_src_block_name_to_idx);
1314
0
                }
1315
0
                _cur_reader = std::move(doris_reader);
1316
12
            } else {
1317
12
                auto arrow_reader =
1318
12
                        ArrowStreamReader::create_unique(_state, _profile, &_counter, *_params,
1319
12
                                                         range, _file_slot_descs, _io_ctx.get());
1320
12
                init_status =
1321
12
                        static_cast<GenericReader*>(arrow_reader.get())->init_reader(&arrow_ctx);
1322
12
                _cur_reader = std::move(arrow_reader);
1323
12
            }
1324
12
            break;
1325
2.41k
        }
1326
0
        case TFileFormatType::FORMAT_ES_HTTP: {
1327
0
            _cur_reader = EsHttpReader::create_unique(_file_slot_descs, _state, _profile, range,
1328
0
                                                      *_params, _real_tuple_desc);
1329
0
            init_status = static_cast<EsHttpReader*>(_cur_reader.get())->init_reader();
1330
0
            break;
1331
2.41k
        }
1332
0
        default:
1333
0
            return Status::NotSupported("Not supported create reader for file format: {}.",
1334
0
                                        to_string(_params->format_type));
1335
6.92k
        }
1336
1337
6.91k
        if (_cur_reader == nullptr) {
1338
1
            return Status::NotSupported(
1339
1
                    "Not supported create reader for table format: {} / file format: {}.",
1340
1
                    range.__isset.table_format_params ? range.table_format_params.table_format_type
1341
1
                                                      : "NotSet",
1342
1
                    to_string(_params->format_type));
1343
1
        }
1344
6.91k
        COUNTER_UPDATE(_file_counter, 1);
1345
        // The FileScanner for external table may try to open not exist files,
1346
        // Because FE file cache for external table may out of date.
1347
        // So, NOT_FOUND for FileScanner is not a fail case.
1348
        // Will remove this after file reader refactor.
1349
6.91k
        if (init_status.is<END_OF_FILE>()) {
1350
0
            COUNTER_UPDATE(_empty_file_counter, 1);
1351
0
            continue;
1352
6.91k
        } else if (init_status.is<ErrorCode::NOT_FOUND>()) {
1353
0
            if (config::ignore_not_found_file_in_external_table) {
1354
0
                COUNTER_UPDATE(_not_found_file_counter, 1);
1355
0
                continue;
1356
0
            }
1357
0
            return Status::InternalError("failed to find reader, err: {}", init_status.to_string());
1358
6.91k
        } else if (!init_status.ok()) {
1359
1
            return Status::InternalError("failed to init reader, err: {}", init_status.to_string());
1360
1
        }
1361
1362
        // For table-level COUNT pushdown, offsets are undefined so we must skip
1363
        // _set_fill_or_truncate_columns (it uses [start_offset, end_offset] to
1364
        // filter row groups, which would produce incorrect empty results).
1365
6.90k
        bool is_table_level_count = _get_push_down_agg_type() == TPushAggOp::type::COUNT &&
1366
6.90k
                                    range.__isset.table_format_params &&
1367
6.90k
                                    range.table_format_params.table_level_row_count >= 0;
1368
6.90k
        if (!is_table_level_count) {
1369
6.90k
            Status status = _set_fill_or_truncate_columns(need_to_get_parsed_schema);
1370
6.90k
            if (status.is<END_OF_FILE>()) { // all parquet row groups are filtered
1371
0
                continue;
1372
6.90k
            } else if (!status.ok()) {
1373
0
                return Status::InternalError("failed to set_fill_or_truncate_columns, err: {}",
1374
0
                                             status.to_string());
1375
0
            }
1376
6.90k
        }
1377
1378
        // Unified COUNT(*) pushdown: replace the real reader with CountReader
1379
        // decorator if the reader accepts COUNT and can provide a total row count.
1380
6.90k
        if (_cur_reader->get_push_down_agg_type() == TPushAggOp::type::COUNT) {
1381
22
            int64_t total_rows = -1;
1382
22
            if (is_table_level_count) {
1383
                // FE-provided count (may account for table-format deletions)
1384
0
                total_rows = range.table_format_params.table_level_row_count;
1385
22
            } else if (_cur_reader->supports_count_pushdown()) {
1386
                // File metadata count (ORC footer / Parquet row groups)
1387
22
                total_rows = _cur_reader->get_total_rows();
1388
22
            }
1389
22
            if (total_rows >= 0) {
1390
22
                auto batch_size = _state->batch_size();
1391
22
                _cur_reader = std::make_unique<CountReader>(total_rows, batch_size,
1392
22
                                                            std::move(_cur_reader));
1393
22
            }
1394
22
        }
1395
6.90k
        _cur_reader_eof = false;
1396
6.90k
        _init_adaptive_batch_size_state(format_type);
1397
6.90k
        break;
1398
6.90k
    }
1399
6.90k
    return Status::OK();
1400
13.8k
}
1401
1402
Status FileScanner::_init_parquet_reader(FileMetaCache* file_meta_cache_ptr,
1403
2.16k
                                         std::unique_ptr<ParquetReader> parquet_reader) {
1404
2.16k
    const TFileRangeDesc& range = _current_range;
1405
2.16k
    Status init_status = Status::OK();
1406
1407
2.16k
    phmap::flat_hash_map<int, std::vector<std::shared_ptr<ColumnPredicate>>> slot_id_to_predicates =
1408
2.16k
            _local_state
1409
2.16k
                    ? _local_state->cast<FileScanLocalState>()._slot_id_to_predicates
1410
2.16k
                    : phmap::flat_hash_map<int, std::vector<std::shared_ptr<ColumnPredicate>>> {};
1411
1412
    // Build unified ParquetInitContext (shared by all Parquet reader variants)
1413
2.16k
    ParquetInitContext pctx;
1414
2.16k
    _fill_base_init_context(&pctx);
1415
2.16k
    pctx.conjuncts = &_push_down_conjuncts;
1416
2.16k
    pctx.slot_id_to_predicates = &slot_id_to_predicates;
1417
2.16k
    pctx.colname_to_slot_id = _col_name_to_slot_id;
1418
2.16k
    pctx.not_single_slot_filter_conjuncts = &_not_single_slot_filter_conjuncts;
1419
2.16k
    pctx.slot_id_to_filter_conjuncts = &_slot_id_to_filter_conjuncts;
1420
1421
2.16k
    if (range.__isset.table_format_params &&
1422
2.16k
        range.table_format_params.table_format_type == "iceberg") {
1423
        // IcebergParquetReader IS-A ParquetReader (CRTP mixin), no wrapping needed
1424
780
        std::unique_ptr<IcebergParquetReader> iceberg_reader = IcebergParquetReader::create_unique(
1425
780
                _kv_cache, _profile, *_params, range, _state->batch_size(), &_state->timezone_obj(),
1426
780
                _io_ctx, _state, file_meta_cache_ptr);
1427
780
        iceberg_reader->set_create_row_id_column_iterator_func(
1428
780
                [this]() -> std::shared_ptr<segment_v2::RowIdColumnIteratorV2> {
1429
0
                    return _create_row_id_column_iterator();
1430
0
                });
1431
780
        init_status = static_cast<GenericReader*>(iceberg_reader.get())->init_reader(&pctx);
1432
780
        _cur_reader = std::move(iceberg_reader);
1433
1.38k
    } else if (range.__isset.table_format_params &&
1434
1.38k
               range.table_format_params.table_format_type == "paimon") {
1435
        // PaimonParquetReader IS-A ParquetReader, no wrapping needed
1436
0
        auto paimon_reader = PaimonParquetReader::create_unique(
1437
0
                _profile, *_params, range, _state->batch_size(), &_state->timezone_obj(), _kv_cache,
1438
0
                _io_ctx, _state, file_meta_cache_ptr);
1439
0
        init_status = static_cast<GenericReader*>(paimon_reader.get())->init_reader(&pctx);
1440
0
        _cur_reader = std::move(paimon_reader);
1441
1.38k
    } else if (range.__isset.table_format_params &&
1442
1.38k
               range.table_format_params.table_format_type == "hudi") {
1443
        // HudiParquetReader IS-A ParquetReader, no wrapping needed
1444
0
        auto hudi_reader = HudiParquetReader::create_unique(
1445
0
                _profile, *_params, range, _state->batch_size(), &_state->timezone_obj(), _io_ctx,
1446
0
                _state, file_meta_cache_ptr);
1447
0
        init_status = static_cast<GenericReader*>(hudi_reader.get())->init_reader(&pctx);
1448
0
        _cur_reader = std::move(hudi_reader);
1449
1.38k
    } else if (range.table_format_params.table_format_type == "hive") {
1450
1.22k
        auto hive_reader = HiveParquetReader::create_unique(
1451
1.22k
                _profile, *_params, range, _state->batch_size(), &_state->timezone_obj(), _io_ctx,
1452
1.22k
                _state, &_is_file_slot, file_meta_cache_ptr,
1453
1.22k
                _state->query_options().enable_parquet_lazy_mat);
1454
1.22k
        hive_reader->set_create_row_id_column_iterator_func(
1455
1.22k
                [this]() -> std::shared_ptr<segment_v2::RowIdColumnIteratorV2> {
1456
0
                    return _create_row_id_column_iterator();
1457
0
                });
1458
1.22k
        init_status = static_cast<GenericReader*>(hive_reader.get())->init_reader(&pctx);
1459
1.22k
        _cur_reader = std::move(hive_reader);
1460
1.22k
    } else if (range.table_format_params.table_format_type == "tvf") {
1461
132
        if (!parquet_reader) {
1462
63
            parquet_reader = ParquetReader::create_unique(
1463
63
                    _profile, *_params, range, _state->batch_size(), &_state->timezone_obj(),
1464
63
                    _io_ctx, _state, file_meta_cache_ptr,
1465
63
                    _state->query_options().enable_parquet_lazy_mat);
1466
63
        }
1467
132
        parquet_reader->set_create_row_id_column_iterator_func(
1468
132
                [this]() -> std::shared_ptr<segment_v2::RowIdColumnIteratorV2> {
1469
9
                    return _create_row_id_column_iterator();
1470
9
                });
1471
132
        init_status = static_cast<GenericReader*>(parquet_reader.get())->init_reader(&pctx);
1472
132
        _cur_reader = std::move(parquet_reader);
1473
132
    } else if (_is_load) {
1474
24
        if (!parquet_reader) {
1475
24
            parquet_reader = ParquetReader::create_unique(
1476
24
                    _profile, *_params, range, _state->batch_size(), &_state->timezone_obj(),
1477
24
                    _io_ctx, _state, file_meta_cache_ptr,
1478
24
                    _state->query_options().enable_parquet_lazy_mat);
1479
24
        }
1480
24
        init_status = static_cast<GenericReader*>(parquet_reader.get())->init_reader(&pctx);
1481
24
        _cur_reader = std::move(parquet_reader);
1482
24
    }
1483
1484
2.16k
    return init_status;
1485
2.16k
}
1486
1487
Status FileScanner::_init_orc_reader(FileMetaCache* file_meta_cache_ptr,
1488
2.18k
                                     std::unique_ptr<OrcReader> orc_reader) {
1489
2.18k
    const TFileRangeDesc& range = _current_range;
1490
2.18k
    Status init_status = Status::OK();
1491
1492
    // Build unified OrcInitContext (shared by all ORC reader variants)
1493
2.18k
    OrcInitContext octx;
1494
2.18k
    _fill_base_init_context(&octx);
1495
2.18k
    octx.conjuncts = &_push_down_conjuncts;
1496
2.18k
    octx.not_single_slot_filter_conjuncts = &_not_single_slot_filter_conjuncts;
1497
2.18k
    octx.slot_id_to_filter_conjuncts = &_slot_id_to_filter_conjuncts;
1498
1499
2.18k
    if (range.__isset.table_format_params &&
1500
2.18k
        range.table_format_params.table_format_type == "transactional_hive") {
1501
        // TransactionalHiveReader IS-A OrcReader, no wrapping needed
1502
0
        auto tran_orc_reader = TransactionalHiveReader::create_unique(
1503
0
                _profile, _state, *_params, range, _state->batch_size(), _state->timezone(),
1504
0
                _io_ctx, file_meta_cache_ptr);
1505
0
        tran_orc_reader->set_create_row_id_column_iterator_func(
1506
0
                [this]() -> std::shared_ptr<segment_v2::RowIdColumnIteratorV2> {
1507
0
                    return _create_row_id_column_iterator();
1508
0
                });
1509
0
        init_status = static_cast<GenericReader*>(tran_orc_reader.get())->init_reader(&octx);
1510
1511
0
        _cur_reader = std::move(tran_orc_reader);
1512
2.18k
    } else if (range.__isset.table_format_params &&
1513
2.18k
               range.table_format_params.table_format_type == "iceberg") {
1514
        // IcebergOrcReader IS-A OrcReader (CRTP mixin), no wrapping needed
1515
372
        std::unique_ptr<IcebergOrcReader> iceberg_reader = IcebergOrcReader::create_unique(
1516
372
                _kv_cache, _profile, _state, *_params, range, _state->batch_size(),
1517
372
                _state->timezone(), _io_ctx, file_meta_cache_ptr);
1518
372
        iceberg_reader->set_create_row_id_column_iterator_func(
1519
372
                [this]() -> std::shared_ptr<segment_v2::RowIdColumnIteratorV2> {
1520
0
                    return _create_row_id_column_iterator();
1521
0
                });
1522
372
        init_status = static_cast<GenericReader*>(iceberg_reader.get())->init_reader(&octx);
1523
1524
372
        _cur_reader = std::move(iceberg_reader);
1525
1.81k
    } else if (range.__isset.table_format_params &&
1526
1.81k
               range.table_format_params.table_format_type == "paimon") {
1527
        // PaimonOrcReader IS-A OrcReader, no wrapping needed
1528
0
        auto paimon_reader = PaimonOrcReader::create_unique(
1529
0
                _profile, _state, *_params, range, _state->batch_size(), _state->timezone(),
1530
0
                _kv_cache, _io_ctx, file_meta_cache_ptr);
1531
0
        init_status = static_cast<GenericReader*>(paimon_reader.get())->init_reader(&octx);
1532
1533
0
        _cur_reader = std::move(paimon_reader);
1534
1.81k
    } else if (range.__isset.table_format_params &&
1535
1.81k
               range.table_format_params.table_format_type == "hudi") {
1536
        // HudiOrcReader IS-A OrcReader, no wrapping needed
1537
0
        auto hudi_reader = HudiOrcReader::create_unique(_profile, _state, *_params, range,
1538
0
                                                        _state->batch_size(), _state->timezone(),
1539
0
                                                        _io_ctx, file_meta_cache_ptr);
1540
0
        init_status = static_cast<GenericReader*>(hudi_reader.get())->init_reader(&octx);
1541
1542
0
        _cur_reader = std::move(hudi_reader);
1543
1.81k
    } else if (range.__isset.table_format_params &&
1544
1.81k
               range.table_format_params.table_format_type == "hive") {
1545
1.58k
        auto hive_reader = HiveOrcReader::create_unique(
1546
1.58k
                _profile, _state, *_params, range, _state->batch_size(), _state->timezone(),
1547
1.58k
                _io_ctx, &_is_file_slot, file_meta_cache_ptr,
1548
1.58k
                _state->query_options().enable_orc_lazy_mat);
1549
1.58k
        hive_reader->set_create_row_id_column_iterator_func(
1550
1.58k
                [this]() -> std::shared_ptr<segment_v2::RowIdColumnIteratorV2> {
1551
0
                    return _create_row_id_column_iterator();
1552
0
                });
1553
1.58k
        init_status = static_cast<GenericReader*>(hive_reader.get())->init_reader(&octx);
1554
1555
1.58k
        _cur_reader = std::move(hive_reader);
1556
1.58k
    } else if (range.__isset.table_format_params &&
1557
232
               range.table_format_params.table_format_type == "tvf") {
1558
203
        if (!orc_reader) {
1559
68
            orc_reader = OrcReader::create_unique(
1560
68
                    _profile, _state, *_params, range, _state->batch_size(), _state->timezone(),
1561
68
                    _io_ctx, file_meta_cache_ptr, _state->query_options().enable_orc_lazy_mat);
1562
68
        }
1563
203
        orc_reader->set_create_row_id_column_iterator_func(
1564
203
                [this]() -> std::shared_ptr<segment_v2::RowIdColumnIteratorV2> {
1565
7
                    return _create_row_id_column_iterator();
1566
7
                });
1567
203
        init_status = static_cast<GenericReader*>(orc_reader.get())->init_reader(&octx);
1568
203
        _cur_reader = std::move(orc_reader);
1569
203
    } else if (_is_load) {
1570
13
        if (!orc_reader) {
1571
13
            orc_reader = OrcReader::create_unique(
1572
13
                    _profile, _state, *_params, range, _state->batch_size(), _state->timezone(),
1573
13
                    _io_ctx, file_meta_cache_ptr, _state->query_options().enable_orc_lazy_mat);
1574
13
        }
1575
13
        init_status = static_cast<GenericReader*>(orc_reader.get())->init_reader(&octx);
1576
13
        _cur_reader = std::move(orc_reader);
1577
13
    }
1578
1579
2.18k
    return init_status;
1580
2.18k
}
1581
1582
10.1k
Status FileScanner::_set_fill_or_truncate_columns(bool need_to_get_parsed_schema) {
1583
10.1k
    _slot_lower_name_to_col_type.clear();
1584
1585
10.1k
    std::unordered_map<std::string, DataTypePtr> name_to_col_type;
1586
10.1k
    RETURN_IF_ERROR(_cur_reader->get_columns(&name_to_col_type));
1587
1588
87.5k
    for (const auto& [col_name, col_type] : name_to_col_type) {
1589
87.5k
        auto col_name_lower = to_lower(col_name);
1590
87.5k
        if (_partition_col_descs.contains(col_name_lower)) {
1591
            /*
1592
             * `_slot_lower_name_to_col_type` is used by `_init_src_block` and `_cast_to_input_block` during LOAD to
1593
             * generate columns of the corresponding type, which records the columns existing in the file.
1594
             *
1595
             * When a column in `COLUMNS FROM PATH` exists in a file column, the column type in the block will
1596
             * not match the slot type in `_output_tuple_desc`, causing an error when
1597
             * Serde `deserialize_one_cell_from_json` fills the partition values.
1598
             *
1599
             * So for partition column not need fill _slot_lower_name_to_col_type.
1600
             */
1601
104
            continue;
1602
104
        }
1603
87.4k
        _slot_lower_name_to_col_type.emplace(col_name_lower, col_type);
1604
87.4k
    }
1605
1606
10.1k
    RETURN_IF_ERROR(_generate_truncate_columns(need_to_get_parsed_schema));
1607
10.1k
    return Status::OK();
1608
10.1k
}
1609
1610
10.1k
Status FileScanner::_generate_truncate_columns(bool need_to_get_parsed_schema) {
1611
10.1k
    _source_file_col_name_types.clear();
1612
    //  The col names and types of source file, such as parquet, orc files.
1613
10.1k
    if (_state->query_options().truncate_char_or_varchar_columns && need_to_get_parsed_schema) {
1614
0
        std::vector<std::string> source_file_col_names;
1615
0
        std::vector<DataTypePtr> source_file_col_types;
1616
0
        Status status =
1617
0
                _cur_reader->get_parsed_schema(&source_file_col_names, &source_file_col_types);
1618
0
        if (!status.ok() && status.code() != TStatusCode::NOT_IMPLEMENTED_ERROR) {
1619
0
            return status;
1620
0
        }
1621
0
        DCHECK_EQ(source_file_col_names.size(), source_file_col_types.size());
1622
0
        for (int i = 0; i < source_file_col_names.size(); ++i) {
1623
0
            _source_file_col_name_types[to_lower(source_file_col_names[i])] =
1624
0
                    source_file_col_types[i];
1625
0
        }
1626
0
    }
1627
10.1k
    return Status::OK();
1628
10.1k
}
1629
1630
3.19k
Status FileScanner::prepare_for_read_lines(const TFileRangeDesc& range) {
1631
3.19k
    _current_range = range;
1632
1633
3.19k
    _file_cache_statistics.reset(new io::FileCacheStatistics());
1634
3.19k
    _file_reader_stats.reset(new io::FileReaderStats());
1635
1636
3.19k
    _file_read_bytes_counter =
1637
3.19k
            ADD_COUNTER_WITH_LEVEL(_profile, FileReadBytesProfile, TUnit::BYTES, 1);
1638
3.19k
    _file_read_time_counter = ADD_TIMER_WITH_LEVEL(_profile, FileReadTimeProfile, 1);
1639
1640
3.19k
    RETURN_IF_ERROR(_init_io_ctx());
1641
3.19k
    _io_ctx->file_cache_stats = _file_cache_statistics.get();
1642
3.19k
    _io_ctx->file_reader_stats = _file_reader_stats.get();
1643
3.19k
    _default_val_row_desc.reset(new RowDescriptor((TupleDescriptor*)_real_tuple_desc));
1644
3.19k
    RETURN_IF_ERROR(_init_expr_ctxes());
1645
1646
    // Since only one column is read from the file, there is no need to filter, so set these variables to empty.
1647
3.19k
    _push_down_conjuncts.clear();
1648
3.19k
    _not_single_slot_filter_conjuncts.clear();
1649
3.19k
    _slot_id_to_filter_conjuncts.clear();
1650
3.19k
    _kv_cache = nullptr;
1651
3.19k
    return Status::OK();
1652
3.19k
}
1653
1654
Status FileScanner::read_lines_from_range(const TFileRangeDesc& range,
1655
                                          const std::list<int64_t>& row_ids, Block* result_block,
1656
                                          const ExternalFileMappingInfo& external_info,
1657
3.10k
                                          int64_t* init_reader_ms, int64_t* get_block_ms) {
1658
3.10k
    _current_range = range;
1659
3.10k
    RETURN_IF_ERROR(_generate_partition_columns());
1660
1661
3.10k
    TFileFormatType::type format_type = _get_current_format_type();
1662
3.10k
    Status init_status = Status::OK();
1663
1664
3.10k
    auto file_meta_cache_ptr = external_info.enable_file_meta_cache
1665
3.14k
                                       ? ExecEnv::GetInstance()->file_meta_cache()
1666
18.4E
                                       : nullptr;
1667
1668
3.10k
    RETURN_IF_ERROR(scope_timer_run(
1669
3.10k
            [&]() -> Status {
1670
3.10k
                switch (format_type) {
1671
3.10k
                case TFileFormatType::FORMAT_PARQUET: {
1672
3.10k
                    std::unique_ptr<ParquetReader> parquet_reader = ParquetReader::create_unique(
1673
3.10k
                            _profile, *_params, range, 1, &_state->timezone_obj(), _io_ctx, _state,
1674
3.10k
                            file_meta_cache_ptr, false);
1675
3.10k
                    RETURN_IF_ERROR(
1676
3.10k
                            _init_parquet_reader(file_meta_cache_ptr, std::move(parquet_reader)));
1677
                    // _init_parquet_reader may create a new table-format specific reader
1678
                    // (e.g., HiveParquetReader) that replaces the original parquet_reader.
1679
                    // We need to re-apply read_by_rows to the actual _cur_reader.
1680
3.10k
                    RETURN_IF_ERROR(_cur_reader->read_by_rows(row_ids));
1681
3.10k
                    break;
1682
3.10k
                }
1683
3.10k
                case TFileFormatType::FORMAT_ORC: {
1684
3.10k
                    std::unique_ptr<OrcReader> orc_reader = OrcReader::create_unique(
1685
3.10k
                            _profile, _state, *_params, range, 1, _state->timezone(), _io_ctx,
1686
3.10k
                            file_meta_cache_ptr, false);
1687
3.10k
                    RETURN_IF_ERROR(_init_orc_reader(file_meta_cache_ptr, std::move(orc_reader)));
1688
                    // Same as above: re-apply read_by_rows to the actual _cur_reader.
1689
3.10k
                    RETURN_IF_ERROR(_cur_reader->read_by_rows(row_ids));
1690
3.10k
                    break;
1691
3.10k
                }
1692
3.10k
                default: {
1693
3.10k
                    return Status::NotSupported(
1694
3.10k
                            "Not support create lines reader for file format: {},"
1695
3.10k
                            "only support parquet and orc.",
1696
3.10k
                            to_string(_params->format_type));
1697
3.10k
                }
1698
3.10k
                }
1699
3.10k
                return Status::OK();
1700
3.10k
            },
1701
3.10k
            init_reader_ms));
1702
1703
3.10k
    RETURN_IF_ERROR(_set_fill_or_truncate_columns(true));
1704
3.10k
    _cur_reader_eof = false;
1705
1706
3.10k
    RETURN_IF_ERROR(scope_timer_run(
1707
3.10k
            [&]() -> Status {
1708
3.10k
                while (!_cur_reader_eof) {
1709
3.10k
                    bool eof = false;
1710
3.10k
                    RETURN_IF_ERROR(_get_block_impl(_state, result_block, &eof));
1711
3.10k
                }
1712
3.10k
                return Status::OK();
1713
3.10k
            },
1714
3.10k
            get_block_ms));
1715
1716
3.10k
    _cur_reader->collect_profile_before_close();
1717
3.10k
    RETURN_IF_ERROR(_cur_reader->close());
1718
1719
3.10k
    COUNTER_UPDATE(_file_read_bytes_counter, _file_reader_stats->read_bytes);
1720
3.10k
    COUNTER_UPDATE(_file_read_time_counter, _file_reader_stats->read_time_ns);
1721
3.10k
    return Status::OK();
1722
3.10k
}
1723
1724
3.24k
Status FileScanner::_generate_partition_columns() {
1725
3.24k
    _partition_col_descs.clear();
1726
3.24k
    _partition_value_is_null.clear();
1727
3.24k
    const TFileRangeDesc& range = _current_range;
1728
3.24k
    if (!range.__isset.columns_from_path_keys) {
1729
1.83k
        return Status::OK();
1730
1.83k
    }
1731
1.40k
    DORIS_CHECK(range.__isset.columns_from_path);
1732
1.40k
    DORIS_CHECK(range.columns_from_path.size() == range.columns_from_path_keys.size());
1733
1.40k
    const bool has_null_flags = range.__isset.columns_from_path_is_null;
1734
1.43k
    if (has_null_flags) {
1735
1.43k
        DORIS_CHECK(range.columns_from_path_is_null.size() == range.columns_from_path_keys.size());
1736
1.43k
    }
1737
1738
1.40k
    std::unordered_map<std::string, int> partition_name_to_key_index;
1739
1.40k
    int index = 0;
1740
2.46k
    for (const auto& key : range.columns_from_path_keys) {
1741
2.46k
        partition_name_to_key_index.emplace(key, index++);
1742
2.46k
    }
1743
1744
    // Iterate _column_descs to find PARTITION_KEY columns instead of _partition_slot_descs.
1745
17.8k
    for (const auto& col_desc : _column_descs) {
1746
17.8k
        if (col_desc.category != ColumnCategory::PARTITION_KEY) {
1747
15.8k
            continue;
1748
15.8k
        }
1749
2.01k
        auto pit = partition_name_to_key_index.find(col_desc.name);
1750
2.15k
        if (pit != partition_name_to_key_index.end()) {
1751
2.15k
            auto values_index = cast_set<size_t>(pit->second);
1752
2.15k
            _partition_col_descs.emplace(
1753
2.15k
                    col_desc.name,
1754
2.15k
                    std::make_tuple(range.columns_from_path[values_index], col_desc.slot_desc));
1755
2.15k
            _partition_value_is_null.emplace(
1756
2.15k
                    col_desc.name,
1757
18.4E
                    has_null_flags ? range.columns_from_path_is_null[values_index] : false);
1758
2.15k
        }
1759
2.01k
    }
1760
1.40k
    return Status::OK();
1761
3.24k
}
1762
1763
9.63k
Status FileScanner::_init_expr_ctxes() {
1764
9.63k
    std::map<SlotId, int> full_src_index_map;
1765
9.63k
    std::map<SlotId, SlotDescriptor*> full_src_slot_map;
1766
9.63k
    std::map<std::string, int> partition_name_to_key_index_map;
1767
9.63k
    int index = 0;
1768
64.1k
    for (const auto& slot_desc : _real_tuple_desc->slots()) {
1769
64.1k
        full_src_slot_map.emplace(slot_desc->id(), slot_desc);
1770
64.1k
        full_src_index_map.emplace(slot_desc->id(), index++);
1771
64.1k
    }
1772
1773
    // For external table query, find the index of column in path.
1774
    // Because query doesn't always search for all columns in a table
1775
    // and the order of selected columns is random.
1776
    // All ranges in _ranges vector should have identical columns_from_path_keys
1777
    // because they are all file splits for the same external table.
1778
    // So here use the first element of _ranges to fill the partition_name_to_key_index_map
1779
9.63k
    if (_current_range.__isset.columns_from_path_keys) {
1780
1.43k
        std::vector<std::string> key_map = _current_range.columns_from_path_keys;
1781
1.43k
        if (!key_map.empty()) {
1782
3.90k
            for (size_t i = 0; i < key_map.size(); i++) {
1783
2.47k
                partition_name_to_key_index_map.emplace(key_map[i], i);
1784
2.47k
            }
1785
1.42k
        }
1786
1.43k
    }
1787
1788
9.63k
    _num_of_columns_from_file = _params->num_of_columns_from_file;
1789
64.1k
    for (const auto& slot_info : _params->required_slots) {
1790
64.1k
        auto slot_id = slot_info.slot_id;
1791
64.1k
        auto it = full_src_slot_map.find(slot_id);
1792
64.1k
        if (it == std::end(full_src_slot_map)) {
1793
0
            return Status::InternalError(
1794
0
                    fmt::format("Unknown source slot descriptor, slot_id={}", slot_id));
1795
0
        }
1796
1797
64.1k
        ColumnDescriptor col_desc;
1798
64.1k
        col_desc.name = it->second->col_name();
1799
64.1k
        col_desc.slot_desc = it->second;
1800
1801
        // Read category from Thrift if available (new FE), otherwise fall back
1802
        // to slot_info.is_file_slot + partition_name_to_key_index_map for broker/stream load
1803
        // where the FE does not set TColumnCategory.
1804
64.1k
        if (slot_info.__isset.category) {
1805
4.48k
            switch (slot_info.category) {
1806
4.39k
            case TColumnCategory::REGULAR:
1807
4.39k
                col_desc.category = ColumnCategory::REGULAR;
1808
4.39k
                break;
1809
74
            case TColumnCategory::PARTITION_KEY:
1810
74
                col_desc.category = ColumnCategory::PARTITION_KEY;
1811
74
                break;
1812
16
            case TColumnCategory::SYNTHESIZED:
1813
16
                col_desc.category = ColumnCategory::SYNTHESIZED;
1814
16
                break;
1815
0
            case TColumnCategory::GENERATED:
1816
0
                col_desc.category = ColumnCategory::GENERATED;
1817
0
                break;
1818
4.48k
            }
1819
59.6k
        } else if (partition_name_to_key_index_map.contains(it->second->col_name()) &&
1820
59.6k
                   !slot_info.is_file_slot) {
1821
2.14k
            col_desc.category = ColumnCategory::PARTITION_KEY;
1822
2.14k
        }
1823
1824
        // Derive is_file_slot from category
1825
64.1k
        bool is_file_slot = (col_desc.category == ColumnCategory::REGULAR ||
1826
64.1k
                             col_desc.category == ColumnCategory::GENERATED);
1827
1828
64.1k
        if (partition_name_to_key_index_map.contains(it->second->col_name())) {
1829
2.18k
            if (_is_load) {
1830
11
                auto iti = full_src_index_map.find(slot_id);
1831
11
                _partition_slot_index_map.emplace(slot_id, iti->second - _num_of_columns_from_file);
1832
2.17k
            } else {
1833
2.17k
                auto kit = partition_name_to_key_index_map.find(it->second->col_name());
1834
2.17k
                _partition_slot_index_map.emplace(slot_id, kit->second);
1835
2.17k
            }
1836
2.18k
        }
1837
1838
64.1k
        if (is_file_slot) {
1839
61.9k
            _is_file_slot.emplace(slot_id);
1840
61.9k
            _file_slot_descs.emplace_back(it->second);
1841
61.9k
        }
1842
1843
64.1k
        _column_descs.push_back(col_desc);
1844
64.1k
    }
1845
1846
    // set column name to default value expr map
1847
    // new inline TFileScanSlotInfo.default_value_expr (preferred)
1848
64.1k
    for (const auto& slot_info : _params->required_slots) {
1849
64.1k
        auto slot_id = slot_info.slot_id;
1850
64.1k
        auto it = full_src_slot_map.find(slot_id);
1851
64.1k
        if (it == std::end(full_src_slot_map)) {
1852
0
            continue;
1853
0
        }
1854
64.1k
        const std::string& col_name = it->second->col_name();
1855
1856
64.1k
        VExprContextSPtr ctx;
1857
64.1k
        bool has_default = false;
1858
1859
        // Prefer inline default_value_expr from TFileScanSlotInfo (new FE)
1860
64.1k
        if (slot_info.__isset.default_value_expr && !slot_info.default_value_expr.nodes.empty()) {
1861
32.8k
            RETURN_IF_ERROR(VExpr::create_expr_tree(slot_info.default_value_expr, ctx));
1862
32.8k
            RETURN_IF_ERROR(ctx->prepare(_state, *_default_val_row_desc));
1863
32.8k
            RETURN_IF_ERROR(ctx->open(_state));
1864
32.8k
            has_default = true;
1865
32.8k
        } else if (slot_info.__isset.default_value_expr) {
1866
            // Empty nodes means null default (same as legacy empty TExpr)
1867
6.90k
            has_default = true;
1868
6.90k
        }
1869
1870
        // Fall back to legacy default_value_of_src_slot map for mixed-version FE/BE
1871
        // and callers that have not preserved inline default_value_expr yet.
1872
64.1k
        if (!has_default) {
1873
24.4k
            auto legacy_it = _params->default_value_of_src_slot.find(slot_id);
1874
24.4k
            if (legacy_it != std::end(_params->default_value_of_src_slot)) {
1875
23.8k
                if (!legacy_it->second.nodes.empty()) {
1876
0
                    RETURN_IF_ERROR(VExpr::create_expr_tree(legacy_it->second, ctx));
1877
0
                    RETURN_IF_ERROR(ctx->prepare(_state, *_default_val_row_desc));
1878
0
                    RETURN_IF_ERROR(ctx->open(_state));
1879
0
                }
1880
23.8k
                has_default = true;
1881
23.8k
            }
1882
24.4k
        }
1883
1884
64.1k
        if (has_default) {
1885
            // if expr is empty, the default value will be null
1886
63.6k
            _col_default_value_ctx.emplace(col_name, ctx);
1887
63.6k
        }
1888
64.1k
    }
1889
1890
    // Populate default_expr in each ColumnDescriptor from _col_default_value_ctx.
1891
    // This makes default values available to readers via column_descs, eliminating the
1892
    // need for the separate _generate_missing_columns roundtrip.
1893
63.9k
    for (auto& col_desc : _column_descs) {
1894
63.9k
        auto it = _col_default_value_ctx.find(col_desc.name);
1895
63.9k
        if (it != _col_default_value_ctx.end()) {
1896
63.3k
            col_desc.default_expr = it->second;
1897
63.3k
        }
1898
63.9k
    }
1899
1900
9.63k
    if (_is_load) {
1901
        // follow desc expr map is only for load task.
1902
5.52k
        bool has_slot_id_map = _params->__isset.dest_sid_to_src_sid_without_trans;
1903
5.52k
        int idx = 0;
1904
44.4k
        for (auto* slot_desc : _output_tuple_desc->slots()) {
1905
44.4k
            auto it = _params->expr_of_dest_slot.find(slot_desc->id());
1906
44.4k
            if (it == std::end(_params->expr_of_dest_slot)) {
1907
0
                return Status::InternalError("No expr for dest slot, id={}, name={}",
1908
0
                                             slot_desc->id(), slot_desc->col_name());
1909
0
            }
1910
1911
44.4k
            VExprContextSPtr ctx;
1912
44.4k
            if (!it->second.nodes.empty()) {
1913
44.4k
                RETURN_IF_ERROR(VExpr::create_expr_tree(it->second, ctx));
1914
44.4k
                RETURN_IF_ERROR(ctx->prepare(_state, *_src_row_desc));
1915
44.4k
                RETURN_IF_ERROR(ctx->open(_state));
1916
44.4k
            }
1917
44.4k
            _dest_vexpr_ctx.emplace_back(ctx);
1918
44.4k
            _dest_slot_name_to_idx[slot_desc->col_name()] = idx++;
1919
1920
44.4k
            if (has_slot_id_map) {
1921
44.3k
                auto it1 = _params->dest_sid_to_src_sid_without_trans.find(slot_desc->id());
1922
44.3k
                if (it1 == std::end(_params->dest_sid_to_src_sid_without_trans)) {
1923
9.06k
                    _src_slot_descs_order_by_dest.emplace_back(nullptr);
1924
35.2k
                } else {
1925
35.2k
                    auto _src_slot_it = full_src_slot_map.find(it1->second);
1926
35.2k
                    if (_src_slot_it == std::end(full_src_slot_map)) {
1927
0
                        return Status::InternalError("No src slot {} in src slot descs",
1928
0
                                                     it1->second);
1929
0
                    }
1930
35.2k
                    _dest_slot_to_src_slot_index.emplace(_src_slot_descs_order_by_dest.size(),
1931
35.2k
                                                         full_src_index_map[_src_slot_it->first]);
1932
35.2k
                    _src_slot_descs_order_by_dest.emplace_back(_src_slot_it->second);
1933
35.2k
                }
1934
44.3k
            }
1935
44.4k
        }
1936
5.52k
    }
1937
9.63k
    return Status::OK();
1938
9.63k
}
1939
1940
34.4k
bool FileScanner::_should_enable_condition_cache() {
1941
34.4k
    DCHECK(_should_enable_condition_cache_handler != nullptr);
1942
34.4k
    if (_condition_cache_digest == 0 || !(this->*_should_enable_condition_cache_handler)()) {
1943
27.9k
        return false;
1944
27.9k
    }
1945
1946
    // Condition cache starts as all-false and is turned true only by native readers when a
1947
    // row-level predicate leaves at least one row in the granule. COUNT pushdown may replace the
1948
    // native reader with CountReader, which only emits row counts and never runs that marking path.
1949
6.53k
    if (_get_push_down_agg_type() == TPushAggOp::type::COUNT) {
1950
214
        return false;
1951
214
    }
1952
1953
    // The cache is populated by native readers while evaluating pushed-down predicates.
1954
    // Scanner-only predicates cannot mark reader granules, so there is nothing useful to cache.
1955
6.32k
    if (_push_down_conjuncts.empty()) {
1956
4.80k
        return false;
1957
4.80k
    }
1958
1959
1.51k
    return true;
1960
6.32k
}
1961
1962
0
bool FileScanner::_should_enable_condition_cache_for_load() const {
1963
0
    return false;
1964
0
}
1965
1966
6.53k
bool FileScanner::_should_enable_condition_cache_for_query() const {
1967
6.53k
    return true;
1968
6.53k
}
1969
1970
6.92k
bool FileScanner::_should_push_down_predicates(TFileFormatType::type format_type) const {
1971
6.92k
    DCHECK(_should_push_down_predicates_handler != nullptr);
1972
6.92k
    return (this->*_should_push_down_predicates_handler)(format_type);
1973
6.92k
}
1974
1975
5.52k
bool FileScanner::_should_push_down_predicates_for_load(TFileFormatType::type format_type) const {
1976
5.52k
    static_cast<void>(format_type);
1977
5.52k
    return false;
1978
5.52k
}
1979
1980
1.39k
bool FileScanner::_should_push_down_predicates_for_query(TFileFormatType::type format_type) const {
1981
    // JNI readers handle predicate conversion in their own paths.
1982
1.39k
    return format_type != TFileFormatType::FORMAT_JNI;
1983
1.39k
}
1984
1985
13.7k
void FileScanner::_init_reader_condition_cache() {
1986
13.7k
    _condition_cache = nullptr;
1987
13.7k
    _condition_cache_ctx = nullptr;
1988
1989
    // _process_late_arrival_conjuncts() runs before the Parquet/ORC reader is initialized. Rebuild
1990
    // the key here so a newly arrived cache-safe RF is represented by both its semantics and its
1991
    // payload. If any current conjunct cannot produce a reliable digest, this becomes zero and the
1992
    // existing safety gate below disables condition cache.
1993
13.7k
    _condition_cache_digest = _current_condition_cache_digest();
1994
1995
13.7k
    if (!_should_enable_condition_cache() || !_cur_reader) {
1996
13.2k
        return;
1997
13.2k
    }
1998
1999
    // Disable condition cache when delete operations exist (e.g. Iceberg position/equality
2000
    // deletes, Hive ACID deletes). Cached granule results may become stale if delete files
2001
    // change between queries while the data file's cache key remains the same.
2002
485
    if (_cur_reader->has_delete_operations()) {
2003
24
        return;
2004
24
    }
2005
2006
461
    auto* cache = segment_v2::ConditionCache::instance();
2007
461
    _condition_cache_key = segment_v2::ConditionCache::ExternalCacheKey(
2008
461
            _current_range.path,
2009
18.4E
            _current_range.__isset.modification_time ? _current_range.modification_time : 0,
2010
18.4E
            _current_range.__isset.file_size ? _current_range.file_size : -1,
2011
461
            _condition_cache_digest,
2012
18.4E
            _current_range.__isset.start_offset ? _current_range.start_offset : 0,
2013
18.4E
            _current_range.__isset.size ? _current_range.size : -1);
2014
2015
461
    segment_v2::ConditionCacheHandle handle;
2016
461
    auto condition_cache_hit = cache->lookup(_condition_cache_key, &handle);
2017
461
    if (condition_cache_hit) {
2018
16
        _condition_cache = handle.get_filter_result();
2019
16
        _condition_cache_hit_count++;
2020
445
    } else {
2021
        // Allocate cache pre-sized to total number of granules.
2022
        // We add +1 as a safety margin: when a file is split across multiple scanners
2023
        // and the first row of this scanner's range is not aligned to a granule boundary,
2024
        // the data may span one more granule than ceil(total_rows / GRANULE_SIZE).
2025
        // The extra element costs only 1 bit and never affects correctness (an extra
2026
        // false-granule beyond the actual data range won't overlap any real row range).
2027
445
        int64_t total_rows = _cur_reader->get_total_rows();
2028
445
        if (total_rows > 0) {
2029
264
            size_t num_granules = (total_rows + ConditionCacheContext::GRANULE_SIZE - 1) /
2030
264
                                  ConditionCacheContext::GRANULE_SIZE;
2031
264
            _condition_cache = std::make_shared<std::vector<bool>>(num_granules + 1, false);
2032
264
        }
2033
445
    }
2034
2035
461
    if (_condition_cache) {
2036
        // Create context to pass to readers (native readers use it; non-native readers ignore it)
2037
280
        _condition_cache_ctx = std::make_shared<ConditionCacheContext>();
2038
280
        _condition_cache_ctx->is_hit = condition_cache_hit;
2039
280
        _condition_cache_ctx->filter_result = _condition_cache;
2040
280
        _cur_reader->set_condition_cache_context(_condition_cache_ctx);
2041
280
    }
2042
461
}
2043
2044
20.7k
void FileScanner::_finalize_reader_condition_cache() {
2045
20.7k
    if (!_should_enable_condition_cache() || !_condition_cache_ctx ||
2046
20.7k
        _condition_cache_ctx->is_hit) {
2047
20.4k
        _condition_cache = nullptr;
2048
20.4k
        _condition_cache_ctx = nullptr;
2049
20.4k
        return;
2050
20.4k
    }
2051
    // Only store the cache if the reader was fully consumed. If the scan was
2052
    // truncated early (e.g. by LIMIT), the cache is incomplete — unread granules
2053
    // would remain false and cause surviving rows to be incorrectly skipped on HIT.
2054
264
    if (!_cur_reader_eof) {
2055
0
        _condition_cache = nullptr;
2056
0
        _condition_cache_ctx = nullptr;
2057
0
        return;
2058
0
    }
2059
2060
264
    auto* cache = segment_v2::ConditionCache::instance();
2061
264
    cache->insert(_condition_cache_key, std::move(_condition_cache));
2062
264
    _condition_cache = nullptr;
2063
264
    _condition_cache_ctx = nullptr;
2064
264
}
2065
2066
6.92k
Status FileScanner::close(RuntimeState* state) {
2067
6.92k
    if (!_try_close()) {
2068
0
        return Status::OK();
2069
0
    }
2070
2071
6.92k
    _finalize_reader_condition_cache();
2072
2073
6.92k
    if (_cur_reader) {
2074
39
        RETURN_IF_ERROR(_cur_reader->close());
2075
39
    }
2076
2077
6.92k
    RETURN_IF_ERROR(Scanner::close(state));
2078
6.92k
    return Status::OK();
2079
6.92k
}
2080
2081
6.92k
void FileScanner::try_stop() {
2082
6.92k
    Scanner::try_stop();
2083
6.92k
    if (_io_ctx) {
2084
6.92k
        _io_ctx->should_stop = true;
2085
6.92k
    }
2086
6.92k
}
2087
2088
16.1k
void FileScanner::update_realtime_counters() {
2089
16.1k
    FileScanLocalState* local_state = static_cast<FileScanLocalState*>(_local_state);
2090
2091
16.1k
    COUNTER_UPDATE(local_state->_scan_bytes, _file_reader_stats->read_bytes);
2092
16.1k
    COUNTER_UPDATE(local_state->_scan_rows, _file_reader_stats->read_rows);
2093
2094
16.1k
    _state->get_query_ctx()->resource_ctx()->io_context()->update_scan_rows(
2095
16.1k
            _file_reader_stats->read_rows);
2096
16.1k
    _state->get_query_ctx()->resource_ctx()->io_context()->update_scan_bytes(
2097
16.1k
            _file_reader_stats->read_bytes);
2098
2099
16.1k
    int64_t delta_bytes_read_from_local =
2100
16.1k
            _file_cache_statistics->bytes_read_from_local - _last_bytes_read_from_local;
2101
16.1k
    int64_t delta_bytes_read_from_remote =
2102
16.1k
            _file_cache_statistics->bytes_read_from_remote - _last_bytes_read_from_remote;
2103
16.1k
    if (_file_cache_statistics->bytes_read_from_local == 0 &&
2104
16.1k
        _file_cache_statistics->bytes_read_from_remote == 0) {
2105
16.1k
        _state->get_query_ctx()
2106
16.1k
                ->resource_ctx()
2107
16.1k
                ->io_context()
2108
16.1k
                ->update_scan_bytes_from_remote_storage(_file_reader_stats->read_bytes);
2109
16.1k
        DorisMetrics::instance()->query_scan_bytes_from_local->increment(
2110
16.1k
                _file_reader_stats->read_bytes);
2111
18.4E
    } else {
2112
18.4E
        _state->get_query_ctx()->resource_ctx()->io_context()->update_scan_bytes_from_local_storage(
2113
18.4E
                delta_bytes_read_from_local);
2114
18.4E
        _state->get_query_ctx()
2115
18.4E
                ->resource_ctx()
2116
18.4E
                ->io_context()
2117
18.4E
                ->update_scan_bytes_from_remote_storage(delta_bytes_read_from_remote);
2118
18.4E
        DorisMetrics::instance()->query_scan_bytes_from_local->increment(
2119
18.4E
                delta_bytes_read_from_local);
2120
18.4E
        DorisMetrics::instance()->query_scan_bytes_from_remote->increment(
2121
18.4E
                delta_bytes_read_from_remote);
2122
18.4E
    }
2123
2124
16.1k
    COUNTER_UPDATE(_file_read_bytes_counter, _file_reader_stats->read_bytes);
2125
2126
16.1k
    DorisMetrics::instance()->query_scan_bytes->increment(_file_reader_stats->read_bytes);
2127
16.1k
    DorisMetrics::instance()->query_scan_rows->increment(_file_reader_stats->read_rows);
2128
2129
16.1k
    _file_reader_stats->read_bytes = 0;
2130
16.1k
    _file_reader_stats->read_rows = 0;
2131
2132
16.1k
    _last_bytes_read_from_local = _file_cache_statistics->bytes_read_from_local;
2133
16.1k
    _last_bytes_read_from_remote = _file_cache_statistics->bytes_read_from_remote;
2134
16.1k
}
2135
2136
6.91k
bool FileScanner::_should_update_load_counters() const {
2137
6.91k
    if (_is_load) {
2138
5.52k
        return true;
2139
5.52k
    }
2140
    // TVF based loads (e.g. http_stream, group commit relay) plan the load source as a
2141
    // tvf query scan without src tuple desc, so _is_load is false. But rows filtered by
2142
    // the load's WHERE clause still need to be reported as unselected rows. FILE_STREAM
2143
    // is only reachable from such load entries, never from normal queries, so use it to
2144
    // identify these scanners.
2145
1.39k
    return (_params->__isset.file_type && _params->file_type == TFileType::FILE_STREAM) ||
2146
1.39k
           (_current_range.__isset.file_type && _current_range.file_type == TFileType::FILE_STREAM);
2147
6.91k
}
2148
2149
6.91k
void FileScanner::_collect_profile_before_close() {
2150
6.91k
    Scanner::_collect_profile_before_close();
2151
6.91k
    if (config::enable_file_cache && _state->query_options().enable_file_cache &&
2152
6.91k
        _profile != nullptr) {
2153
0
        io::FileCacheProfileReporter cache_profile(_profile);
2154
0
        cache_profile.update(_file_cache_statistics.get());
2155
0
        _state->get_query_ctx()->resource_ctx()->io_context()->update_bytes_write_into_cache(
2156
0
                _file_cache_statistics->bytes_write_into_cache);
2157
0
    }
2158
2159
6.91k
    if (_cur_reader != nullptr) {
2160
39
        _cur_reader->collect_profile_before_close();
2161
39
    }
2162
2163
6.91k
    FileScanLocalState* local_state = static_cast<FileScanLocalState*>(_local_state);
2164
6.91k
    COUNTER_UPDATE(local_state->_scan_bytes, _file_reader_stats->read_bytes);
2165
6.91k
    COUNTER_UPDATE(local_state->_scan_rows, _file_reader_stats->read_rows);
2166
2167
6.91k
    COUNTER_UPDATE(_file_read_bytes_counter, _file_reader_stats->read_bytes);
2168
6.91k
    COUNTER_UPDATE(_file_read_calls_counter, _file_reader_stats->read_calls);
2169
6.91k
    COUNTER_UPDATE(_file_read_time_counter, _file_reader_stats->read_time_ns);
2170
6.91k
    COUNTER_UPDATE(local_state->_condition_cache_hit_counter, _condition_cache_hit_count);
2171
6.91k
    if (_io_ctx) {
2172
6.91k
        COUNTER_UPDATE(local_state->_condition_cache_filtered_rows_counter,
2173
6.91k
                       _io_ctx->condition_cache_filtered_rows);
2174
6.91k
    }
2175
2176
6.91k
    DorisMetrics::instance()->query_scan_bytes->increment(_file_reader_stats->read_bytes);
2177
6.91k
    DorisMetrics::instance()->query_scan_rows->increment(_file_reader_stats->read_rows);
2178
6.91k
}
2179
2180
} // namespace doris