forked from apache/doris
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnested_loop_join_probe_operator.h
More file actions
311 lines (285 loc) · 15 KB
/
Copy pathnested_loop_join_probe_operator.h
File metadata and controls
311 lines (285 loc) · 15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#pragma once
#include <cstdint>
#include <set>
#include <vector>
#include "common/cast_set.h"
#include "common/status.h"
#include "core/column/column.h"
#include "exec/operator/join_probe_operator.h"
#include "exec/operator/operator.h"
#include "util/simd/bits.h"
namespace doris {
class RuntimeState;
class NestedLoopJoinProbeOperatorX;
/// TODO: Long-term task — the current implementation of this class
/// is not ideal. Many variables are in a global state, and changing
/// one may affect other functions. In the future, we should pass the
/// required variables through function parameters to avoid issues
/// caused by global state.
class NestedLoopJoinProbeLocalState final
: public JoinProbeLocalState<NestedLoopJoinSharedState, NestedLoopJoinProbeLocalState> {
public:
using Parent = NestedLoopJoinProbeOperatorX;
ENABLE_FACTORY_CREATOR(NestedLoopJoinProbeLocalState);
NestedLoopJoinProbeLocalState(RuntimeState* state, OperatorXBase* parent);
~NestedLoopJoinProbeLocalState() override = default;
#define CLEAR_BLOCK \
for (size_t i = 0; i < column_to_keep; ++i) { \
auto& column = block->get_by_position(i).column; \
if (column->is_exclusive()) { \
column->assert_mutable()->clear(); \
} else { \
column = column->clone_empty(); \
} \
}
Status init(RuntimeState* state, LocalStateInfo& info) override;
Status open(RuntimeState* state) override;
Status close(RuntimeState* state) override;
// For some complex join types, after generating data on the build/probe side,
// we need to update visited flags.
template <typename JoinOpType, bool set_build_side_flag, bool set_probe_side_flag>
Status generate_other_join_block_data(RuntimeState* state, JoinOpType& join_op_variants);
// Effective only for inner/cross join
Status generate_inner_join_block_data(RuntimeState* state);
// Generate data based on the probe side; applicable to all join types
template <bool set_build_side_flag, bool set_probe_side_flag>
void _generate_block_base_probe(RuntimeState* state, Block* probe_block);
// Currently only inner join calls this; both set_build_side_flag and
// set_probe_side_flag are false, so visited flags will not be updated.
void _generate_block_base_build(RuntimeState* state, Block* probe_block);
private:
// Whether to generate data based on the build side
bool use_generate_block_base_build() const;
Status _advance_lazy_probe_row(RuntimeState* state, const Block& probe_block);
Status _generate_lazy_block_base_probe(RuntimeState* state, Block* probe_block,
bool ignore_null);
Status _generate_lazy_block_base_build(RuntimeState* state, Block* probe_block);
bool _should_delay_lazy_probe_build_block(size_t candidate_rows, size_t batch_size) const;
bool _lazy_should_output_matched_rows() const;
Status _process_lazy_probe_build_block(Block* probe_block, const Block& build_block,
size_t build_block_idx, bool ignore_null);
void _mark_lazy_build_rows_visited(size_t build_block_idx, const IColumn::Filter& filter);
void _update_lazy_mark_join_state(const IColumn::Filter& mark_filter,
const ColumnUInt8& mark_null_map,
const IColumn::Filter& other_filter);
Status _append_lazy_rows(const IColumn::Filter& filter, size_t selected_rows,
bool fixed_side_probe, int64_t fixed_side_pos,
const Block& probe_block, const Block& build_block);
Status _append_lazy_probe_row_with_build_defaults(const Block& probe_block,
int64_t probe_row_pos);
Status _append_lazy_mark_probe_row_with_build_defaults(const Block& probe_block,
int64_t probe_row_pos,
int8_t mark_value);
Status _finalize_lazy_probe_row(RuntimeState* state, const Block& probe_block,
int64_t probe_row_pos, bool* consumed);
Status _append_lazy_build_rows_with_probe_defaults(const Block& build_block,
const IColumn::Filter& filter,
size_t selected_rows);
Status _finalize_lazy_build_side(RuntimeState* state);
void _replace_lazy_placeholder_columns(size_t rows);
void _append_lazy_probe_eval_columns(ColumnsWithTypeAndName& eval_columns,
const Block& probe_block, bool fixed_side_probe,
int64_t fixed_side_pos, size_t rows);
void _append_lazy_build_eval_columns(ColumnsWithTypeAndName& eval_columns,
const Block& build_block, bool fixed_side_probe,
int64_t fixed_side_pos, size_t rows);
friend class NestedLoopJoinProbeOperatorX;
void _update_additional_flags(Block* block);
template <bool BuildSide, bool IsSemi>
void _finalize_current_phase(Block& block, size_t batch_size);
void _reset_with_next_probe_row();
void _append_probe_data_with_null(Block& block) const;
template <typename Filter, bool SetBuildSideFlag, bool SetProbeSideFlag>
void _do_filtering_and_update_visited_flags_impl(Block* block, uint32_t column_to_keep,
size_t build_block_idx,
size_t processed_blocks_num, bool materialize,
Filter& filter) {
{
SCOPED_TIMER(_update_visited_flags_timer);
if constexpr (SetBuildSideFlag) {
for (size_t i = 0; i < processed_blocks_num; i++) {
auto& build_side_flag =
assert_cast<ColumnUInt8*>(
_shared_state->build_side_visited_flags[build_block_idx].get())
->get_data();
auto* __restrict build_side_flag_data = build_side_flag.data();
auto cur_sz = build_side_flag.size();
const size_t offset = _build_offset_stack.top();
_build_offset_stack.pop();
for (size_t j = 0; j < cur_sz; j++) {
build_side_flag_data[j] |= filter[offset + j];
}
build_block_idx = build_block_idx == 0 ? _shared_state->build_blocks.size() - 1
: build_block_idx - 1;
}
}
if constexpr (SetProbeSideFlag) {
int64_t end = filter.size();
for (int i = _probe_block_pos == _child_block->rows() ? _probe_block_pos - 1
: _probe_block_pos;
i >= _probe_block_start_pos; i--) {
int64_t offset = 0;
if (!_probe_offset_stack.empty()) {
offset = _probe_offset_stack.top();
_probe_offset_stack.pop();
}
if (!_cur_probe_row_visited_flags[i]) {
_cur_probe_row_visited_flags[i] =
simd::contain_one(filter.data() + offset, end - offset);
}
end = offset;
}
}
}
if (materialize) {
SCOPED_TIMER(_filtered_by_join_conjuncts_timer);
Block::filter_block_internal(block, filter, column_to_keep);
} else {
CLEAR_BLOCK
}
}
// need exception safety
template <bool SetBuildSideFlag, bool SetProbeSideFlag, bool IgnoreNull>
Status _do_filtering_and_update_visited_flags(Block* block, bool materialize) {
// The number of columns will not exceed the range of u32.
auto column_to_keep = cast_set<uint32_t>(block->columns());
// If we need to set visited flags for build side,
// 1. Execute conjuncts and get a column with bool type to do filtering.
// 2. Use bool column to update build-side visited flags.
// 3. Use bool column to do filtering.
size_t build_block_idx = _current_build_pos == 0 ? _shared_state->build_blocks.size() - 1
: _current_build_pos - 1;
size_t processed_blocks_num = _build_offset_stack.size();
if (LIKELY(!_join_conjuncts.empty() && block->rows() > 0)) {
IColumn::Filter filter(block->rows(), 1);
bool can_filter_all = false;
{
SCOPED_TIMER(_join_conjuncts_evaluation_timer);
RETURN_IF_ERROR(VExprContext::execute_conjuncts(
_join_conjuncts, nullptr, IgnoreNull, block, &filter, &can_filter_all));
}
if (can_filter_all) {
CLEAR_BLOCK
std::stack<uint16_t> empty1;
_probe_offset_stack.swap(empty1);
std::stack<uint16_t> empty2;
_build_offset_stack.swap(empty2);
} else {
_do_filtering_and_update_visited_flags_impl<decltype(filter), SetBuildSideFlag,
SetProbeSideFlag>(
block, column_to_keep, build_block_idx, processed_blocks_num, materialize,
filter);
}
} else if (block->rows() > 0) {
if constexpr (SetBuildSideFlag) {
for (size_t i = 0; i < processed_blocks_num; i++) {
auto& build_side_flag =
assert_cast<ColumnUInt8*>(
_shared_state->build_side_visited_flags[build_block_idx].get())
->get_data();
auto* __restrict build_side_flag_data = build_side_flag.data();
auto cur_sz = build_side_flag.size();
_build_offset_stack.pop();
memset(reinterpret_cast<void*>(build_side_flag_data), 1, cur_sz);
build_block_idx = build_block_idx == 0 ? _shared_state->build_blocks.size() - 1
: build_block_idx - 1;
}
}
if constexpr (SetProbeSideFlag) {
std::stack<uint16_t> empty;
_probe_offset_stack.swap(empty);
std::fill(_cur_probe_row_visited_flags.begin(), _cur_probe_row_visited_flags.end(),
1);
}
if (!materialize) {
CLEAR_BLOCK
}
}
Block::erase_useless_column(block, column_to_keep);
return Status::OK();
}
bool _matched_rows_done;
int _probe_block_start_pos = 0;
int _probe_block_pos; // current scan pos in _probe_block
int _probe_side_process_count = 0;
bool _need_more_input_data = true;
// Visited flags for current row in probe side.
std::vector<int8_t> _cur_probe_row_visited_flags;
std::vector<int8_t> _cur_probe_row_mark_flags;
size_t _current_build_pos = 0;
size_t _current_build_row_pos =
0; // current row pos in build block, used by _generate_block_base_build
MutableColumns _dst_columns;
std::stack<uint16_t> _build_offset_stack;
std::stack<uint16_t> _probe_offset_stack;
uint64_t _output_null_idx_build_side = 0;
uint64_t _output_null_row_idx_build_side = 0;
VExprContextSPtrs _join_conjuncts;
VExprContextSPtrs _mark_join_conjuncts;
RuntimeProfile::Counter* _loop_join_timer = nullptr;
RuntimeProfile::Counter* _output_temp_blocks_timer = nullptr;
RuntimeProfile::Counter* _update_visited_flags_timer = nullptr;
RuntimeProfile::Counter* _join_conjuncts_evaluation_timer = nullptr;
RuntimeProfile::Counter* _filtered_by_join_conjuncts_timer = nullptr;
};
class NestedLoopJoinProbeOperatorX final
: public JoinProbeOperatorX<NestedLoopJoinProbeLocalState> {
public:
NestedLoopJoinProbeOperatorX(ObjectPool* pool, const TPlanNode& tnode, int operator_id,
const DescriptorTbl& descs);
Status init(const TPlanNode& tnode, RuntimeState* state) override;
Status prepare(RuntimeState* state) override;
Status push(RuntimeState* state, Block* input_block, bool eos) const override;
Status pull(doris::RuntimeState* state, Block* output_block, bool* eos) const override;
const RowDescriptor& intermediate_row_desc() const override {
DORIS_CHECK(_intermediate_row_desc != nullptr);
return *_intermediate_row_desc;
}
DataDistribution required_data_distribution(RuntimeState* /*state*/) const override {
if (_join_op == TJoinOp::NULL_AWARE_LEFT_ANTI_JOIN ||
_join_op == TJoinOp::RIGHT_OUTER_JOIN || _join_op == TJoinOp::RIGHT_ANTI_JOIN ||
_join_op == TJoinOp::RIGHT_SEMI_JOIN || _join_op == TJoinOp::FULL_OUTER_JOIN) {
return {TLocalPartitionType::NOOP};
}
return {TLocalPartitionType::ADAPTIVE_PASSTHROUGH};
}
const RowDescriptor& row_desc() const override {
if (_output_row_descriptor) {
return *_output_row_descriptor;
}
DORIS_CHECK(_output_row_desc != nullptr);
return *_output_row_desc;
}
bool need_more_input_data(RuntimeState* state) const override;
private:
friend class NestedLoopJoinProbeLocalState;
VExprContextSPtrs _join_conjuncts;
VExprContextSPtrs _mark_join_conjuncts;
size_t _num_probe_side_columns = 0;
size_t _num_build_side_columns = 0;
bool _has_materialized_slot_ids = false;
std::vector<SlotId> _materialized_slot_ids;
bool _enable_lazy_materialize = false;
bool _enable_lazy_probe_finalize = false;
bool _enable_lazy_build_finalize = false;
bool _enable_lazy_mark_finalize = false;
std::set<int> _lazy_eval_column_ids;
std::set<int> _materialize_column_ids;
};
} // namespace doris