be/src/exec/scan/file_scanner.h
Line | Count | Source |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | |
18 | | #pragma once |
19 | | |
20 | | #include <stddef.h> |
21 | | #include <stdint.h> |
22 | | |
23 | | #include <memory> |
24 | | #include <optional> |
25 | | #include <string> |
26 | | #include <unordered_map> |
27 | | #include <unordered_set> |
28 | | #include <vector> |
29 | | |
30 | | #include "common/factory_creator.h" |
31 | | #include "common/global_types.h" |
32 | | #include "common/status.h" |
33 | | #include "core/block/block.h" |
34 | | #include "exec/operator/file_scan_operator.h" |
35 | | #include "exec/scan/file_scan_io_context.h" |
36 | | #include "exprs/vexpr_fwd.h" |
37 | | #include "format/generic_reader.h" |
38 | | #include "format/orc/vorc_reader.h" |
39 | | #include "format/parquet/vparquet_reader.h" |
40 | | #include "format/table/iceberg_reader.h" |
41 | | #include "io/io_common.h" |
42 | | #include "runtime/descriptors.h" |
43 | | #include "runtime/runtime_profile.h" |
44 | | #include "storage/olap_common.h" |
45 | | #include "storage/olap_scan_common.h" |
46 | | #include "storage/segment/adaptive_block_size_predictor.h" |
47 | | #include "storage/segment/condition_cache.h" |
48 | | |
49 | | namespace doris { |
50 | | class RuntimeState; |
51 | | class TFileRangeDesc; |
52 | | class TFileScanRange; |
53 | | class TFileScanRangeParams; |
54 | | |
55 | | class ShardedKVCache; |
56 | | class VExpr; |
57 | | class VExprContext; |
58 | | } // namespace doris |
59 | | |
60 | | namespace doris { |
61 | | |
62 | | class FileScanner : public Scanner { |
63 | | ENABLE_FACTORY_CREATOR(FileScanner); |
64 | | |
65 | | public: |
66 | | static constexpr const char* NAME = "FileScanner"; |
67 | | static constexpr size_t ADAPTIVE_BATCH_INITIAL_PROBE_ROWS = 32; |
68 | | |
69 | | // sub profile name (for parquet/orc) |
70 | | static const std::string FileReadBytesProfile; |
71 | | static const std::string FileReadTimeProfile; |
72 | | |
73 | | #ifdef BE_TEST |
74 | | void TEST_init_runtime_filter_partition_prune_ctxs( |
75 | | const VExprContextSPtrs& conjuncts, |
76 | | const std::unordered_map<SlotId, int>& partition_slot_index_map) { |
77 | | _conjuncts = conjuncts; |
78 | | _partition_slot_index_map = partition_slot_index_map; |
79 | | _init_runtime_filter_partition_prune_ctxs(); |
80 | | } |
81 | | const VExprContextSPtrs& TEST_runtime_filter_partition_prune_ctxs() const { |
82 | | return _runtime_filter_partition_prune_ctxs; |
83 | | } |
84 | | static TPushAggOp::type TEST_effective_push_down_agg_type( |
85 | | TPushAggOp::type agg_type, const std::optional<std::vector<int32_t>>& count_slot_ids) { |
86 | | return _effective_push_down_agg_type(agg_type, count_slot_ids); |
87 | | } |
88 | | #endif |
89 | | |
90 | | FileScanner(RuntimeState* state, FileScanLocalState* parent, int64_t limit, |
91 | | std::shared_ptr<SplitSourceConnector> split_source, RuntimeProfile* profile, |
92 | | ShardedKVCache* kv_cache, |
93 | | const std::unordered_map<std::string, int>* colname_to_slot_id); |
94 | | |
95 | | Status _open_impl(RuntimeState* state) override; |
96 | | |
97 | | Status close(RuntimeState* state) override; |
98 | | |
99 | | void try_stop() override; |
100 | | |
101 | | Status init(RuntimeState* state, const VExprContextSPtrs& conjuncts) override; |
102 | | |
103 | 0 | std::string get_name() override { return FileScanner::NAME; } |
104 | | |
105 | 22 | std::string get_current_scan_range_name() override { return _current_range_path; } |
106 | | |
107 | | //only used for read one line. |
108 | | FileScanner(RuntimeState* state, RuntimeProfile* profile, const TFileScanRangeParams* params, |
109 | | const std::unordered_map<std::string, int>* colname_to_slot_id, |
110 | | TupleDescriptor* tuple_desc) |
111 | 3.20k | : Scanner(state, profile), |
112 | 3.20k | _params(params), |
113 | 3.20k | _col_name_to_slot_id(colname_to_slot_id), |
114 | 3.20k | _real_tuple_desc(tuple_desc) { |
115 | 3.20k | _configure_file_scan_handlers(); |
116 | 3.20k | }; |
117 | | |
118 | | Status read_lines_from_range(const TFileRangeDesc& range, const std::list<int64_t>& row_ids, |
119 | | Block* result_block, const ExternalFileMappingInfo& external_info, |
120 | | int64_t* init_reader_ms, int64_t* get_block_ms); |
121 | | |
122 | | Status prepare_for_read_lines(const TFileRangeDesc& range); |
123 | | |
124 | | void update_realtime_counters() override; |
125 | | |
126 | | protected: |
127 | | Status _get_block_impl(RuntimeState* state, Block* block, bool* eof) override; |
128 | | |
129 | | Status _get_block_wrapped(RuntimeState* state, Block* block, bool* eof); |
130 | | |
131 | | Status _get_next_reader(); |
132 | | |
133 | | // Build a ReaderInitContext with shared fields from FileScanner members. |
134 | | void _fill_base_init_context(ReaderInitContext* ctx); |
135 | | |
136 | | // TODO: cast input block columns type to string. |
137 | 0 | Status _cast_src_block(Block* block) { return Status::OK(); } |
138 | | |
139 | | void _collect_profile_before_close() override; |
140 | | |
141 | | bool _should_update_load_counters() const override; |
142 | | |
143 | | // fe will add skip_bitmap_col to _input_tuple_desc iff the target olaptable has skip_bitmap_col |
144 | | // and the current load is a flexible partial update |
145 | 9.12k | bool _should_process_skip_bitmap_col() const { return _skip_bitmap_col_idx != -1; } |
146 | | |
147 | | protected: |
148 | | const TFileScanRangeParams* _params = nullptr; |
149 | | std::shared_ptr<SplitSourceConnector> _split_source; |
150 | | bool _first_scan_range = false; |
151 | | TFileRangeDesc _current_range; |
152 | | |
153 | | std::unique_ptr<GenericReader> _cur_reader; |
154 | | bool _cur_reader_eof = false; |
155 | | // File source slot descriptors |
156 | | std::vector<SlotDescriptor*> _file_slot_descs; |
157 | | // Unified column descriptors for init_reader (includes file, partition, missing, synthesized cols) |
158 | | std::vector<ColumnDescriptor> _column_descs; |
159 | | |
160 | | // Partition slot id to partition key index (for matching columns_from_path) |
161 | | std::unordered_map<SlotId, int> _partition_slot_index_map; |
162 | | // created from param.expr_of_dest_slot |
163 | | // For query, it saves default value expr of all dest columns, or nullptr for NULL. |
164 | | // For load, it saves conversion expr/default value of all dest columns. |
165 | | VExprContextSPtrs _dest_vexpr_ctx; |
166 | | // dest slot name to index in _dest_vexpr_ctx; |
167 | | std::unordered_map<std::string, int> _dest_slot_name_to_idx; |
168 | | // col name to default value expr |
169 | | // TODO: only used by json reader. Could we delete this? |
170 | | std::unordered_map<std::string, VExprContextSPtr> _col_default_value_ctx; |
171 | | // the map values of dest slot id to src slot desc |
172 | | // if there is not key of dest slot id in dest_sid_to_src_sid_without_trans, it will be set to nullptr |
173 | | std::vector<SlotDescriptor*> _src_slot_descs_order_by_dest; |
174 | | // dest slot desc index to src slot desc index |
175 | | std::unordered_map<int, int> _dest_slot_to_src_slot_index; |
176 | | |
177 | | std::unordered_map<std::string, uint32_t> _src_block_name_to_idx; |
178 | | |
179 | | // Get from GenericReader, save the existing columns in file to their type. |
180 | | std::unordered_map<std::string, DataTypePtr> _slot_lower_name_to_col_type; |
181 | | // Get from GenericReader, save columns that required by scan but not exist in file. |
182 | | |
183 | | // The col lowercase name of source file to type of source file. |
184 | | std::map<std::string, DataTypePtr> _source_file_col_name_types; |
185 | | |
186 | | // For load task |
187 | | VExprContextSPtrs _pre_conjunct_ctxs; |
188 | | std::unique_ptr<RowDescriptor> _src_row_desc; |
189 | | std::unique_ptr<RowDescriptor> _dest_row_desc; |
190 | | // row desc for default exprs |
191 | | std::unique_ptr<RowDescriptor> _default_val_row_desc; |
192 | | // owned by scan node |
193 | | ShardedKVCache* _kv_cache = nullptr; |
194 | | |
195 | | std::set<TSlotId> _is_file_slot; |
196 | | bool _scanner_eof = false; |
197 | | int _rows = 0; |
198 | | int _num_of_columns_from_file; |
199 | | |
200 | | bool _src_block_mem_reuse = false; |
201 | | bool _strict_mode; |
202 | | |
203 | | bool _src_block_init = false; |
204 | | Block* _src_block_ptr = nullptr; |
205 | | Block _src_block; |
206 | | |
207 | | VExprContextSPtrs _push_down_conjuncts; |
208 | | VExprContextSPtrs _runtime_filter_partition_prune_ctxs; |
209 | | Block _runtime_filter_partition_prune_block; |
210 | | |
211 | | std::unique_ptr<io::FileCacheStatistics> _file_cache_statistics; |
212 | | std::unique_ptr<io::FileReaderStats> _file_reader_stats; |
213 | | std::shared_ptr<io::IOContext> _io_ctx; |
214 | | |
215 | | // Whether to fill partition columns from path, default is true. |
216 | | std::unordered_map<std::string, std::tuple<std::string, const SlotDescriptor*>> |
217 | | _partition_col_descs; |
218 | | std::unordered_map<std::string, bool> _partition_value_is_null; |
219 | | |
220 | | // idx of skip_bitmap_col in _input_tuple_desc |
221 | | int32_t _skip_bitmap_col_idx {-1}; |
222 | | int32_t _sequence_map_col_uid {-1}; |
223 | | int32_t _sequence_col_uid {-1}; |
224 | | |
225 | | private: |
226 | | RuntimeProfile::Counter* _get_block_timer = nullptr; |
227 | | RuntimeProfile::Counter* _cast_to_input_block_timer = nullptr; |
228 | | RuntimeProfile::Counter* _fill_missing_columns_timer = nullptr; |
229 | | RuntimeProfile::Counter* _pre_filter_timer = nullptr; |
230 | | RuntimeProfile::Counter* _convert_to_output_block_timer = nullptr; |
231 | | RuntimeProfile::Counter* _runtime_filter_partition_prune_timer = nullptr; |
232 | | RuntimeProfile::Counter* _empty_file_counter = nullptr; |
233 | | RuntimeProfile::Counter* _not_found_file_counter = nullptr; |
234 | | RuntimeProfile::Counter* _fully_skipped_file_counter = nullptr; |
235 | | RuntimeProfile::Counter* _file_counter = nullptr; |
236 | | RuntimeProfile::Counter* _file_read_bytes_counter = nullptr; |
237 | | RuntimeProfile::Counter* _file_read_calls_counter = nullptr; |
238 | | RuntimeProfile::Counter* _file_read_time_counter = nullptr; |
239 | | RuntimeProfile::Counter* _runtime_filter_partition_pruned_range_counter = nullptr; |
240 | | RuntimeProfile::Counter* _adaptive_batch_predicted_rows_counter = nullptr; |
241 | | RuntimeProfile::Counter* _adaptive_batch_actual_bytes_before_truncate_counter = nullptr; |
242 | | RuntimeProfile::Counter* _adaptive_batch_actual_bytes_after_truncate_counter = nullptr; |
243 | | RuntimeProfile::Counter* _adaptive_batch_probe_count_counter = nullptr; |
244 | | |
245 | | const std::unordered_map<std::string, int>* _col_name_to_slot_id = nullptr; |
246 | | // single slot filter conjuncts |
247 | | std::unordered_map<int, VExprContextSPtrs> _slot_id_to_filter_conjuncts; |
248 | | // not single(zero or multi) slot filter conjuncts |
249 | | VExprContextSPtrs _not_single_slot_filter_conjuncts; |
250 | | // save the path of current scan range |
251 | | std::string _current_range_path = ""; |
252 | | |
253 | | // Only for load scan node. |
254 | | const TupleDescriptor* _input_tuple_desc = nullptr; |
255 | | // If _input_tuple_desc is set, |
256 | | // the _real_tuple_desc will point to _input_tuple_desc, |
257 | | // otherwise, point to _output_tuple_desc |
258 | | const TupleDescriptor* _real_tuple_desc = nullptr; |
259 | | |
260 | | int64_t _last_bytes_read_from_local = 0; |
261 | | int64_t _last_bytes_read_from_remote = 0; |
262 | | |
263 | | Status (FileScanner::*_init_src_block_handler)(Block* block) = nullptr; |
264 | | Status (FileScanner::*_process_src_block_after_read_handler)(Block* block) = nullptr; |
265 | | bool (FileScanner::*_should_push_down_predicates_handler)( |
266 | | TFileFormatType::type format_type) const = nullptr; |
267 | | bool (FileScanner::*_should_enable_condition_cache_handler)() const = nullptr; |
268 | | |
269 | | // Condition cache for external tables |
270 | | uint64_t _condition_cache_digest = 0; |
271 | | segment_v2::ConditionCache::ExternalCacheKey _condition_cache_key; |
272 | | std::shared_ptr<std::vector<bool>> _condition_cache; |
273 | | std::shared_ptr<ConditionCacheContext> _condition_cache_ctx; |
274 | | int64_t _condition_cache_hit_count = 0; |
275 | | std::unique_ptr<AdaptiveBlockSizePredictor> _block_size_predictor; |
276 | | |
277 | | void _configure_file_scan_handlers(); |
278 | | |
279 | | Status _init_expr_ctxes(); |
280 | | Status _init_src_block(Block* block); |
281 | | Status _init_src_block_for_load(Block* block); |
282 | | Status _init_src_block_for_query(Block* block); |
283 | | Status _process_src_block_after_read(Block* block); |
284 | | Status _process_src_block_after_read_for_load(Block* block); |
285 | | Status _process_src_block_after_read_for_query(Block* block); |
286 | | Status _check_output_block_types(); |
287 | | Status _cast_to_input_block(Block* block); |
288 | | Status _pre_filter_src_block(); |
289 | | Status _convert_to_output_block(Block* block); |
290 | | Status _truncate_char_or_varchar_columns(Block* block); |
291 | | void _truncate_char_or_varchar_column(Block* block, int idx, int len); |
292 | | Status _generate_partition_columns(); |
293 | | |
294 | | bool _check_partition_prune_expr(const VExprSPtr& expr); |
295 | | bool _contains_runtime_filter(const VExprContextSPtrs& conjuncts) const; |
296 | | void _init_runtime_filter_partition_prune_ctxs(); |
297 | | void _init_runtime_filter_partition_prune_block(); |
298 | | Status _process_runtime_filters_partition_prune(bool& is_partition_pruned); |
299 | | Status _process_conjuncts(); |
300 | | Status _process_late_arrival_conjuncts(); |
301 | | void _get_slot_ids(VExpr* expr, std::vector<int>* slot_ids); |
302 | | Status _generate_truncate_columns(bool need_to_get_parsed_schema); |
303 | | Status _set_fill_or_truncate_columns(bool need_to_get_parsed_schema); |
304 | | Status _init_orc_reader(FileMetaCache* file_meta_cache_ptr, |
305 | | std::unique_ptr<OrcReader> orc_reader = nullptr); |
306 | | Status _init_parquet_reader(FileMetaCache* file_meta_cache_ptr, |
307 | | std::unique_ptr<ParquetReader> parquet_reader = nullptr); |
308 | | std::shared_ptr<segment_v2::RowIdColumnIteratorV2> _create_row_id_column_iterator(); |
309 | | |
310 | 72.1k | TFileFormatType::type _get_current_format_type() { |
311 | | // for compatibility, if format_type is not set in range, use the format type of params |
312 | 72.1k | const TFileRangeDesc& range = _current_range; |
313 | 72.1k | return range.__isset.format_type ? range.format_type : _params->format_type; |
314 | 72.1k | }; |
315 | | |
316 | 22.1k | Status _init_io_ctx() { |
317 | 22.1k | _io_ctx = create_file_scan_io_context(_state); |
318 | 22.1k | return Status::OK(); |
319 | 22.1k | }; |
320 | | |
321 | 0 | void _reset_counter() { |
322 | 0 | _counter.num_rows_unselected = 0; |
323 | 0 | _counter.num_rows_filtered = 0; |
324 | 0 | } |
325 | | |
326 | | bool _should_enable_condition_cache(); |
327 | | bool _should_enable_condition_cache_for_load() const; |
328 | | bool _should_enable_condition_cache_for_query() const; |
329 | | bool _should_push_down_predicates(TFileFormatType::type format_type) const; |
330 | | bool _should_push_down_predicates_for_load(TFileFormatType::type format_type) const; |
331 | | bool _should_push_down_predicates_for_query(TFileFormatType::type format_type) const; |
332 | | void _init_reader_condition_cache(); |
333 | | void _finalize_reader_condition_cache(); |
334 | | void _reset_adaptive_batch_size_state(); |
335 | | void _init_adaptive_batch_size_state(TFileFormatType::type format_type); |
336 | | bool _should_enable_adaptive_batch_size(TFileFormatType::type format_type) const; |
337 | | bool _should_run_adaptive_batch_size() const; |
338 | | size_t _predict_reader_batch_rows(); |
339 | | void _update_adaptive_batch_size_before_truncate(const Block& block); |
340 | | void _update_adaptive_batch_size_after_truncate(const Block& block); |
341 | | |
342 | | static TPushAggOp::type _effective_push_down_agg_type( |
343 | 441k | TPushAggOp::type agg_type, const std::optional<std::vector<int32_t>>& count_slot_ids) { |
344 | 441k | if (agg_type != TPushAggOp::type::COUNT) { |
345 | 436k | return agg_type; |
346 | 436k | } |
347 | | // V1's CountReader receives only the file's total row count and emits that many synthetic |
348 | | // rows. This is exact for COUNT(*)/COUNT(1), but it has no column metadata for NULL or CAST |
349 | | // semantics. For example, a 10,000-row file with 9,015 non-null values must return 10,000 |
350 | | // for COUNT(*) and 9,015 for COUNT(nullable_col); CountReader can produce only the former. |
351 | | // Therefore a non-empty argument list must use the normal reader. nullopt is an old FE plan |
352 | | // that predates the argument field; treating it as empty would silently reinterpret unknown |
353 | | // semantics as COUNT(*). |
354 | 5.84k | return count_slot_ids.has_value() && count_slot_ids->empty() ? TPushAggOp::type::COUNT |
355 | 18.4E | : TPushAggOp::type::NONE; |
356 | 441k | } |
357 | | |
358 | 454k | TPushAggOp::type _get_push_down_agg_type() const { |
359 | 454k | if (_local_state == nullptr) { |
360 | 12.3k | return TPushAggOp::type::NONE; |
361 | 12.3k | } |
362 | 442k | return _effective_push_down_agg_type(_local_state->get_push_down_agg_type(), |
363 | 442k | _local_state->get_push_down_count_slot_ids()); |
364 | 454k | } |
365 | | |
366 | | // enable the file meta cache only when |
367 | | // 1. max_external_file_meta_cache_num is > 0 |
368 | | // 2. the file number is less than 1/3 of cache's capacibility |
369 | | // Otherwise, the cache miss rate will be high |
370 | 58.1k | bool _should_enable_file_meta_cache() { |
371 | 58.1k | return ExecEnv::GetInstance()->file_meta_cache()->enabled() && |
372 | 58.1k | _split_source->num_scan_ranges() < config::max_external_file_meta_cache_num / 3; |
373 | 58.1k | } |
374 | | }; |
375 | | } // namespace doris |