20
20
#include < fmt/format.h>
21
21
#include < gen_cpp/Exprs_types.h>
22
22
#include < gen_cpp/Metrics_types.h>
23
+ #include < gen_cpp/Opcodes_types.h>
23
24
#include < gen_cpp/PaloInternalService_types.h>
24
25
#include < gen_cpp/PlanNodes_types.h>
26
+ #include < glog/logging.h>
25
27
28
+ #include < algorithm>
26
29
#include < boost/iterator/iterator_facade.hpp>
27
30
#include < iterator>
28
31
#include < map>
32
+ #include < ranges>
29
33
#include < tuple>
34
+ #include < unordered_map>
30
35
#include < utility>
31
36
32
37
#include " common/compiler_util.h" // IWYU pragma: keep
33
38
#include " common/config.h"
34
39
#include " common/logging.h"
40
+ #include " common/status.h"
35
41
#include " io/cache/block_file_cache_profile.h"
36
42
#include " runtime/descriptors.h"
37
43
#include " runtime/runtime_state.h"
38
44
#include " runtime/types.h"
45
+ #include " util/runtime_profile.h"
39
46
#include " vec/aggregate_functions/aggregate_function.h"
40
47
#include " vec/columns/column.h"
41
48
#include " vec/columns/column_nullable.h"
67
74
#include " vec/exec/scan/vscan_node.h"
68
75
#include " vec/exprs/vexpr.h"
69
76
#include " vec/exprs/vexpr_context.h"
77
+ #include " vec/exprs/vexpr_fwd.h"
70
78
#include " vec/exprs/vslot_ref.h"
71
79
#include " vec/functions/function.h"
72
80
#include " vec/functions/function_string.h"
@@ -130,12 +138,17 @@ Status VFileScanner::prepare(RuntimeState* state, const VExprContextSPtrs& conju
130
138
ADD_TIMER_WITH_LEVEL (_local_state->scanner_profile (), " FileScannerPreFilterTimer" , 1 );
131
139
_convert_to_output_block_timer = ADD_TIMER_WITH_LEVEL (_local_state->scanner_profile (),
132
140
" FileScannerConvertOuputBlockTime" , 1 );
141
+ _runtime_filter_partition_prune_timer = ADD_TIMER_WITH_LEVEL (
142
+ _local_state->scanner_profile (), " FileScannerRuntimeFilterPartitionPruningTime" , 1 );
133
143
_empty_file_counter =
134
144
ADD_COUNTER_WITH_LEVEL (_local_state->scanner_profile (), " EmptyFileNum" , TUnit::UNIT, 1 );
135
145
_not_found_file_counter = ADD_COUNTER_WITH_LEVEL (_local_state->scanner_profile (),
136
146
" NotFoundFileNum" , TUnit::UNIT, 1 );
137
147
_file_counter =
138
148
ADD_COUNTER_WITH_LEVEL (_local_state->scanner_profile (), " FileNumber" , TUnit::UNIT, 1 );
149
+ _runtime_filter_partition_pruned_range_counter =
150
+ ADD_COUNTER_WITH_LEVEL (_local_state->scanner_profile (),
151
+ " RuntimeFilterPartitionPrunedRangeNum" , TUnit::UNIT, 1 );
139
152
140
153
_file_cache_statistics.reset (new io::FileCacheStatistics ());
141
154
_io_ctx.reset (new io::IOContext ());
@@ -174,6 +187,113 @@ Status VFileScanner::prepare(RuntimeState* state, const VExprContextSPtrs& conju
174
187
return Status::OK ();
175
188
}
176
189
190
+ // check if the expr is a partition pruning expr
191
+ bool VFileScanner::_check_partition_prune_expr (const VExprSPtr& expr) {
192
+ if (expr->is_slot_ref ()) {
193
+ auto * slot_ref = static_cast <VSlotRef*>(expr.get ());
194
+ return _partition_slot_index_map.find (slot_ref->slot_id ()) !=
195
+ _partition_slot_index_map.end ();
196
+ }
197
+ if (expr->is_literal ()) {
198
+ return true ;
199
+ }
200
+ return std::ranges::all_of (expr->children (), [this ](const auto & child) {
201
+ return _check_partition_prune_expr (child);
202
+ });
203
+ }
204
+
205
+ void VFileScanner::_init_runtime_filter_partition_prune_ctxs () {
206
+ _runtime_filter_partition_prune_ctxs.clear ();
207
+ for (auto & conjunct : _conjuncts) {
208
+ auto impl = conjunct->root ()->get_impl ();
209
+ // If impl is not null, which means this a conjuncts from runtime filter.
210
+ auto expr = impl ? impl : conjunct->root ();
211
+ if (_check_partition_prune_expr (expr)) {
212
+ _runtime_filter_partition_prune_ctxs.emplace_back (conjunct);
213
+ }
214
+ }
215
+ }
216
+
217
+ void VFileScanner::_init_runtime_filter_partition_prune_block () {
218
+ // init block with empty column
219
+ for (auto const * slot_desc : _real_tuple_desc->slots ()) {
220
+ if (!slot_desc->need_materialize ()) {
221
+ // should be ignored from reading
222
+ continue ;
223
+ }
224
+ _runtime_filter_partition_prune_block.insert (
225
+ ColumnWithTypeAndName (slot_desc->get_empty_mutable_column (),
226
+ slot_desc->get_data_type_ptr (), slot_desc->col_name ()));
227
+ }
228
+ }
229
+
230
+ Status VFileScanner::_process_runtime_filters_partition_prune (bool & can_filter_all) {
231
+ SCOPED_TIMER (_runtime_filter_partition_prune_timer);
232
+ if (_runtime_filter_partition_prune_ctxs.empty () || _partition_col_descs.empty ()) {
233
+ return Status::OK ();
234
+ }
235
+ size_t partition_value_column_size = 1 ;
236
+
237
+ // 1. Get partition key values to string columns.
238
+ std::unordered_map<SlotId, MutableColumnPtr> parititon_slot_id_to_column;
239
+ for (auto const & partition_col_desc : _partition_col_descs) {
240
+ const auto & [partition_value, partition_slot_desc] = partition_col_desc.second ;
241
+ auto test_serde = partition_slot_desc->get_data_type_ptr ()->get_serde ();
242
+ auto partition_value_column = partition_slot_desc->get_data_type_ptr ()->create_column ();
243
+ auto * col_ptr = static_cast <IColumn*>(partition_value_column.get ());
244
+ Slice slice (partition_value.data (), partition_value.size ());
245
+ int num_deserialized = 0 ;
246
+ RETURN_IF_ERROR (test_serde->deserialize_column_from_fixed_json (
247
+ *col_ptr, slice, partition_value_column_size, &num_deserialized, {}));
248
+ parititon_slot_id_to_column[partition_slot_desc->id ()] = std::move (partition_value_column);
249
+ }
250
+
251
+ // 2. Fill _runtime_filter_partition_prune_block from the partition column, then execute conjuncts and filter block.
252
+ // 2.1 Fill _runtime_filter_partition_prune_block from the partition column to match the conjuncts executing.
253
+ size_t index = 0 ;
254
+ bool first_column_filled = false ;
255
+ for (auto const * slot_desc : _real_tuple_desc->slots ()) {
256
+ if (!slot_desc->need_materialize ()) {
257
+ // should be ignored from reading
258
+ continue ;
259
+ }
260
+ if (parititon_slot_id_to_column.find (slot_desc->id ()) !=
261
+ parititon_slot_id_to_column.end ()) {
262
+ auto data_type = slot_desc->get_data_type_ptr ();
263
+ auto partition_value_column = std::move (parititon_slot_id_to_column[slot_desc->id ()]);
264
+ if (data_type->is_nullable ()) {
265
+ _runtime_filter_partition_prune_block.insert (
266
+ index , ColumnWithTypeAndName (
267
+ ColumnNullable::create (
268
+ std::move (partition_value_column),
269
+ ColumnUInt8::create (partition_value_column_size, 0 )),
270
+ data_type, slot_desc->col_name ()));
271
+ } else {
272
+ _runtime_filter_partition_prune_block.insert (
273
+ index , ColumnWithTypeAndName (std::move (partition_value_column), data_type,
274
+ slot_desc->col_name ()));
275
+ }
276
+ if (index == 0 ) {
277
+ first_column_filled = true ;
278
+ }
279
+ }
280
+ index ++;
281
+ }
282
+
283
+ // 2.2 Execute conjuncts.
284
+ if (!first_column_filled) {
285
+ // VExprContext.execute has an optimization, the filtering is executed when block->rows() > 0
286
+ // The following process may be tricky and time-consuming, but we have no other way.
287
+ _runtime_filter_partition_prune_block.get_by_position (0 ).column ->assume_mutable ()->resize (
288
+ partition_value_column_size);
289
+ }
290
+ IColumn::Filter result_filter (_runtime_filter_partition_prune_block.rows (), 1 );
291
+ RETURN_IF_ERROR (VExprContext::execute_conjuncts (_runtime_filter_partition_prune_ctxs, nullptr ,
292
+ &_runtime_filter_partition_prune_block,
293
+ &result_filter, &can_filter_all));
294
+ return Status::OK ();
295
+ }
296
+
177
297
Status VFileScanner::_process_conjuncts_for_dict_filter () {
178
298
_slot_id_to_filter_conjuncts.clear ();
179
299
_not_single_slot_filter_conjuncts.clear ();
@@ -237,6 +357,11 @@ Status VFileScanner::open(RuntimeState* state) {
237
357
RETURN_IF_ERROR (_split_source->get_next (&_first_scan_range, &_current_range));
238
358
if (_first_scan_range) {
239
359
RETURN_IF_ERROR (_init_expr_ctxes ());
360
+ if (_state->query_options ().enable_runtime_filter_partition_prune &&
361
+ !_partition_slot_index_map.empty ()) {
362
+ _init_runtime_filter_partition_prune_ctxs ();
363
+ _init_runtime_filter_partition_prune_block ();
364
+ }
240
365
} else {
241
366
// there's no scan range in split source. stop scanner directly.
242
367
_scanner_eof = true ;
@@ -752,6 +877,29 @@ Status VFileScanner::_get_next_reader() {
752
877
const TFileRangeDesc& range = _current_range;
753
878
_current_range_path = range.path ;
754
879
880
+ if (!_partition_slot_descs.empty ()) {
881
+ // we need get partition columns first for runtime filter partition pruning
882
+ RETURN_IF_ERROR (_generate_parititon_columns ());
883
+
884
+ if (_state->query_options ().enable_runtime_filter_partition_prune ) {
885
+ // if enable_runtime_filter_partition_prune is true, we need to check whether this range can be filtered out
886
+ // by runtime filter partition prune
887
+ if (_push_down_conjuncts.size () < _conjuncts.size ()) {
888
+ // there are new runtime filters, need to re-init runtime filter partition pruning ctxs
889
+ _init_runtime_filter_partition_prune_ctxs ();
890
+ }
891
+
892
+ bool can_filter_all = false ;
893
+ RETURN_IF_ERROR (_process_runtime_filters_partition_prune (can_filter_all));
894
+ if (can_filter_all) {
895
+ // this range can be filtered out by runtime filter partition pruning
896
+ // so we need to skip this range
897
+ COUNTER_UPDATE (_runtime_filter_partition_pruned_range_counter, 1 );
898
+ continue ;
899
+ }
900
+ }
901
+ }
902
+
755
903
// create reader for specific format
756
904
Status init_status;
757
905
// for compatibility, if format_type is not set in range, use the format type of params
@@ -1012,7 +1160,8 @@ Status VFileScanner::_get_next_reader() {
1012
1160
_missing_cols.clear ();
1013
1161
RETURN_IF_ERROR (_cur_reader->get_columns (&_name_to_col_type, &_missing_cols));
1014
1162
_cur_reader->set_push_down_agg_type (_get_push_down_agg_type ());
1015
- RETURN_IF_ERROR (_generate_fill_columns ());
1163
+ RETURN_IF_ERROR (_generate_missing_columns ());
1164
+ RETURN_IF_ERROR (_cur_reader->set_fill_columns (_partition_col_descs, _missing_col_descs));
1016
1165
if (VLOG_NOTICE_IS_ON && !_missing_cols.empty () && _is_load) {
1017
1166
fmt::memory_buffer col_buf;
1018
1167
for (auto & col : _missing_cols) {
@@ -1042,10 +1191,8 @@ Status VFileScanner::_get_next_reader() {
1042
1191
return Status::OK ();
1043
1192
}
1044
1193
1045
- Status VFileScanner::_generate_fill_columns () {
1194
+ Status VFileScanner::_generate_parititon_columns () {
1046
1195
_partition_col_descs.clear ();
1047
- _missing_col_descs.clear ();
1048
-
1049
1196
const TFileRangeDesc& range = _current_range;
1050
1197
if (range.__isset .columns_from_path && !_partition_slot_descs.empty ()) {
1051
1198
for (const auto & slot_desc : _partition_slot_descs) {
@@ -1066,7 +1213,11 @@ Status VFileScanner::_generate_fill_columns() {
1066
1213
}
1067
1214
}
1068
1215
}
1216
+ return Status::OK ();
1217
+ }
1069
1218
1219
+ Status VFileScanner::_generate_missing_columns () {
1220
+ _missing_col_descs.clear ();
1070
1221
if (!_missing_cols.empty ()) {
1071
1222
for (auto slot_desc : _real_tuple_desc->slots ()) {
1072
1223
if (!slot_desc->is_materialized ()) {
@@ -1084,8 +1235,7 @@ Status VFileScanner::_generate_fill_columns() {
1084
1235
_missing_col_descs.emplace (slot_desc->col_name (), it->second );
1085
1236
}
1086
1237
}
1087
-
1088
- return _cur_reader->set_fill_columns (_partition_col_descs, _missing_col_descs);
1238
+ return Status::OK ();
1089
1239
}
1090
1240
1091
1241
Status VFileScanner::_init_expr_ctxes () {
0 commit comments