Coverage Report

Created: 2026-06-18 00:05

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