Coverage Report

Created: 2026-10-18 23:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/exec/scan/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 <stdint.h>
21
22
#include <algorithm>
23
#include <atomic>
24
#include <functional>
25
#include <optional>
26
#include <utility>
27
#include <vector>
28
29
#include "common/status.h"
30
#include "core/block/block.h"
31
#include "exec/scan/task_executor/listenable_future.h"
32
#include "runtime/exec_env.h"
33
#include "runtime/runtime_state.h"
34
#include "storage/tablet/tablet.h"
35
#include "util/stopwatch.hpp"
36
37
namespace doris {
38
class RuntimeProfile;
39
class TupleDescriptor;
40
41
class VExprContext;
42
43
class ScanLocalStateBase;
44
} // namespace doris
45
46
namespace doris {
47
48
// Counter for load
49
struct ScannerCounter {
50
949k
    ScannerCounter() : num_rows_filtered(0), num_rows_unselected(0) {}
51
52
    int64_t num_rows_filtered;   // unqualified rows (unmatched the dest schema, or no partition)
53
    int64_t num_rows_unselected; // rows filtered by predicates
54
};
55
56
class Scanner {
57
public:
58
    Scanner(RuntimeState* state, ScanLocalStateBase* local_state, int64_t limit,
59
            RuntimeProfile* profile);
60
61
    //only used for FileScanner read one line.
62
    Scanner(RuntimeState* state, RuntimeProfile* profile)
63
37
            : _state(state), _limit(1), _profile(profile), _total_rf_num(0), _has_prepared(false) {
64
37
        DorisMetrics::instance()->scanner_cnt->increment(1);
65
37
    };
66
67
948k
    virtual ~Scanner() {
68
948k
        SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(_state->query_mem_tracker());
69
948k
        _input_block.clear();
70
948k
        _conjuncts.clear();
71
948k
        _projections.clear();
72
948k
        _origin_block.clear();
73
948k
        _common_expr_ctxs_push_down.clear();
74
948k
        DorisMetrics::instance()->scanner_cnt->increment(-1);
75
948k
    }
76
77
    virtual Status init(RuntimeState* state, const VExprContextSPtrs& conjuncts);
78
947k
    Status prepare() {
79
947k
        SCOPED_RAW_TIMER(&_per_scanner_timer);
80
947k
        SCOPED_RAW_TIMER(&_per_scanner_prepare_timer);
81
947k
        return _prepare_impl();
82
947k
    }
83
84
934k
    Status open(RuntimeState* state) {
85
934k
        SCOPED_RAW_TIMER(&_per_scanner_timer);
86
934k
        SCOPED_RAW_TIMER(&_per_scanner_open_timer);
87
934k
        return _open_impl(state);
88
934k
    }
89
90
    Status get_block(RuntimeState* state, Block* block, bool* eos);
91
    Status get_block_after_projects(RuntimeState* state, Block* block, bool* eos);
92
93
    virtual Status close(RuntimeState* state);
94
95
    // Try to stop scanner, and all running readers.
96
950k
    virtual void try_stop() { _should_stop = true; };
97
98
    // What the turn that just ended without a block waits for, if anything: get_block() returned no
99
    // rows and no eof because the scanner cannot read on until this future is done - for now, the
100
    // JNI heap gate admitting the Java scanner of the split it is on. The scheduler parks the scanner
101
    // instead of keeping a worker meanwhile, and runs it again once the future is done.
102
1.00M
    std::optional<SharedListenableFuture<Void>> take_waiting_for() {
103
1.00M
        return std::exchange(_waiting_for, std::nullopt);
104
1.00M
    }
105
106
0
    virtual std::string get_name() { return ""; }
107
108
    // return the readable name of current scan range.
109
    // eg, for file scanner, return the current file path.
110
0
    virtual std::string get_current_scan_range_name() { return "not implemented"; }
111
112
#ifdef BE_TEST
113
    static uint64_t TEST_build_condition_cache_digest(uint64_t seed,
114
                                                      const VExprContextSPtrs& conjuncts);
115
#endif
116
117
protected:
118
    // Rebuild the condition-cache digest from the scanner's current conjunct snapshot. The local
119
    // state's digest is used only as a safety gate: zero means condition cache was disabled during
120
    // scan-node open (for example by TopN or an expression without a reliable digest).
121
    uint64_t _current_condition_cache_digest() const;
122
    static uint64_t _build_condition_cache_digest(uint64_t seed,
123
                                                  const VExprContextSPtrs& conjuncts);
124
125
13.9k
    virtual Status _prepare_impl() {
126
13.9k
        _has_prepared = true;
127
13.9k
        return Status::OK();
128
13.9k
    }
129
130
937k
    virtual Status _open_impl(RuntimeState* state) {
131
937k
        _block_avg_bytes = state->batch_size() * 8;
132
937k
        return Status::OK();
133
937k
    }
134
135
    // Subclass should implement this to return data.
136
    virtual Status _get_block_impl(RuntimeState* state, Block* block, bool* eof) = 0;
137
138
896k
    virtual bool _can_merge_padding_blocks(const Block& /*left*/, const Block& /*right*/) const {
139
896k
        return true;
140
896k
    }
141
142
1.11M
    Status _merge_padding_block() {
143
1.11M
        if (_padding_block.empty()) {
144
876k
            _padding_block.swap(_origin_block);
145
876k
        } else if (_origin_block.rows()) {
146
30.8k
            ScopedMutableBlock scoped_mutable_block(&_padding_block);
147
30.8k
            auto& mutable_block = scoped_mutable_block.mutable_block();
148
30.8k
            RETURN_IF_ERROR(mutable_block.merge(_origin_block));
149
30.8k
        }
150
1.11M
        return Status::OK();
151
1.11M
    }
152
153
    // Update the counters before closing this scanner
154
    virtual void _collect_profile_before_close();
155
156
    // Whether rows filtered/unselected by this scanner should be reported to the load
157
    // counters in RuntimeState. Only the scanner reading the load source data should
158
    // report, otherwise rows filtered by query predicates (e.g. in INSERT INTO ... SELECT
159
    // or DELETE FROM ... WHERE) would be mixed into load counters and make
160
    // num_rows_load_success() negative.
161
942k
    virtual bool _should_update_load_counters() const { return _is_load; }
162
163
    // Check if scanner is already closed, if not, mark it as closed.
164
    // Returns true if the scanner was successfully marked as closed (first time).
165
    // Returns false if the scanner was already closed.
166
    bool _try_close();
167
168
    // Filter the output block finally.
169
    virtual Status _filter_output_block(Block* block);
170
171
    Status _do_projections(Block* origin_block, Block* output_block);
172
173
private:
174
1.00M
    void _start_scan_cpu_timer() {
175
1.00M
        _cpu_watch.reset();
176
1.00M
        _cpu_watch.start();
177
1.00M
    }
178
179
1.00M
    void _update_wait_worker_timer() { _scanner_wait_worker_timer += _watch.elapsed_time(); }
180
    void _update_scan_cpu_timer();
181
182
public:
183
    // Call start_wait_worker_timer() when submit the scanner to the thread pool.
184
    // And call update_wait_worker_timer() when it is actually being executed.
185
2.00M
    void start_wait_worker_timer() {
186
2.00M
        _watch.reset();
187
2.00M
        _watch.start();
188
2.00M
    }
189
190
1.00M
    void resume() {
191
1.00M
        _update_wait_worker_timer();
192
1.00M
        _start_scan_cpu_timer();
193
1.00M
    }
194
1.00M
    void pause() {
195
1.00M
        _update_scan_cpu_timer();
196
1.00M
        start_wait_worker_timer();
197
1.00M
    }
198
5.76k
    int64_t get_time_cost_ns() const { return _per_scanner_timer; }
199
5.76k
    int64_t get_prepare_time_cost_ns() const { return _per_scanner_prepare_timer; }
200
5.76k
    int64_t get_open_time_cost_ns() const { return _per_scanner_open_timer; }
201
202
5.76k
    int64_t projection_time() const { return _projection_timer; }
203
5.76k
    int64_t get_rows_read() const { return _num_rows_read; }
204
205
1.00M
    bool has_prepared() const { return _has_prepared; }
206
207
    Status try_append_late_arrival_runtime_filter();
208
209
5.76k
    int64_t get_scanner_wait_worker_timer() const { return _scanner_wait_worker_timer; }
210
211
    // Some counters need to be updated realtime, for example, workload group policy need
212
    // scan bytes to cancel the query exceed limit.
213
10.8k
    virtual void update_realtime_counters() {}
214
215
1.90M
    RuntimeState* runtime_state() { return _state; }
216
217
1.99M
    bool is_open() const { return _is_open; }
218
946k
    void set_opened() { _is_open = true; }
219
220
20.4k
    virtual doris::TabletStorageType get_storage_type() {
221
20.4k
        return doris::TabletStorageType::STORAGE_TYPE_REMOTE;
222
20.4k
    }
223
224
    // Returns true if this scanner's scan range has been pruned by a runtime filter.
225
54.9k
    virtual bool is_pruned_by_runtime_filter() const { return false; }
226
227
    // Releases resources owned by a scanner that runtime-filter pruning makes unnecessary before
228
    // open(). The scanner will not be scheduled again after this call.
229
12
    virtual void release_unopened_resources() {
230
12
        DORIS_CHECK(!_is_open);
231
12
        _has_prepared = false;
232
12
    }
233
234
0
    bool need_to_close() const { return _need_to_close; }
235
236
944k
    void mark_to_need_to_close() {
237
        // If the scanner is failed during init or open, then not need update counters
238
        // because the query is fail and the counter is useless. And it may core during
239
        // update counters. For example, update counters depend on scanner's tablet, but
240
        // the tablet == null when init failed.
241
946k
        if (_is_open) {
242
946k
            _collect_profile_before_close();
243
946k
        }
244
944k
        _need_to_close = true;
245
944k
    }
246
247
0
    void set_status_on_failure(const Status& st) { _status = st; }
248
249
1.00M
    int64_t limit() const { return _limit; }
250
251
0
    auto get_block_avg_bytes() const { return _block_avg_bytes; }
252
253
265k
    void update_block_avg_bytes(size_t block_avg_bytes) { _block_avg_bytes = block_avg_bytes; }
254
255
protected:
256
    RuntimeState* _state = nullptr;
257
    ScanLocalStateBase* _local_state = nullptr;
258
259
    // Set if scan node has sort limit info
260
    int64_t _limit = -1;
261
262
    RuntimeProfile* _profile = nullptr;
263
264
    const TupleDescriptor* _output_tuple_desc = nullptr;
265
    std::optional<std::reference_wrapper<const RowDescriptor>> _projection_output_row_descriptor;
266
    bool _has_projection = false;
267
268
    // If _input_tuple_desc is set, the scanner will read data into
269
    // this _input_block first, then convert to the output block.
270
    Block _input_block;
271
272
    bool _is_open = false;
273
    std::atomic<bool> _is_closed {false};
274
    bool _need_to_close = false;
275
    Status _status;
276
277
    // If _applied_rf_num == _total_rf_num
278
    // means all runtime filters are arrived and applied.
279
    int _applied_rf_num = 0;
280
    int _total_rf_num = 0;
281
    // Cloned from _conjuncts of scan node.
282
    // It includes predicate in SQL and runtime filters.
283
    VExprContextSPtrs _conjuncts;
284
    VExprContextSPtrs _projections;
285
    // Used in common subexpression elimination to compute intermediate results.
286
    std::vector<VExprContextSPtrs> _intermediate_projections;
287
    Block _origin_block;
288
    Block _padding_block;
289
290
    VExprContextSPtrs _common_expr_ctxs_push_down;
291
292
    // num of rows read from scanner
293
    int64_t _num_rows_read = 0;
294
295
    int64_t _num_byte_read = 0;
296
297
    // num of rows return from scanner, after filter block
298
    int64_t _num_rows_return = 0;
299
300
    size_t _block_avg_bytes = 0;
301
302
    // Set true after counter is updated finally
303
    bool _has_updated_counter = false;
304
305
    // watch to count the time wait for scanner thread
306
    MonotonicStopWatch _watch;
307
    // Do not use ScopedTimer. There is no guarantee that, the counter
308
    ThreadCpuStopWatch _cpu_watch;
309
    int64_t _scanner_wait_worker_timer = 0;
310
    int64_t _scan_cpu_timer = 0;
311
312
    bool _is_load = false;
313
314
    bool _has_prepared = false;
315
316
    ScannerCounter _counter;
317
    int64_t _per_scanner_timer = 0;
318
    int64_t _per_scanner_prepare_timer = 0;
319
    int64_t _per_scanner_open_timer = 0;
320
    int64_t _projection_timer = 0;
321
322
    bool _should_stop = false;
323
324
    // Set by _get_block_impl() when it returns without a block because the scanner has to wait; see
325
    // take_waiting_for().
326
    std::optional<SharedListenableFuture<Void>> _waiting_for;
327
328
    // Cached pointer to ScanOperator's remaining-limit counter. Null when
329
    // this scanner is on the topn path or the query has no LIMIT.
330
    std::atomic<int64_t>* _shared_scan_limit = nullptr;
331
};
332
333
using ScannerSPtr = std::shared_ptr<Scanner>;
334
335
} // namespace doris