Coverage Report

Created: 2026-07-14 21:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/format_v2/table_reader.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 "format_v2/table_reader.h"
19
20
#include <gen_cpp/ExternalTableSchema_types.h>
21
#include <gen_cpp/PlanNodes_types.h>
22
#include <gen_cpp/Types_types.h>
23
24
#include <algorithm>
25
#include <memory>
26
#include <ranges>
27
#include <set>
28
#include <sstream>
29
#include <utility>
30
#include <vector>
31
32
#include "common/cast_set.h"
33
#include "common/status.h"
34
#include "core/assert_cast.h"
35
#include "core/data_type/data_type_array.h"
36
#include "core/data_type/data_type_factory.hpp"
37
#include "core/data_type/data_type_map.h"
38
#include "core/data_type/data_type_struct.h"
39
#include "core/data_type/primitive_type.h"
40
#include "exprs/vexpr_context.h"
41
#include "exprs/vslot_ref.h"
42
#include "format/table/deletion_vector_reader.h"
43
#include "format/table/iceberg_delete_file_reader_helper.h"
44
#include "format/table/paimon_reader.h"
45
#include "format_v2/column_mapper.h"
46
#include "format_v2/delimited_text/csv_reader.h"
47
#include "format_v2/delimited_text/text_reader.h"
48
#include "format_v2/json/json_reader.h"
49
#include "format_v2/native/native_reader.h"
50
#include "format_v2/orc/orc_reader.h"
51
#include "format_v2/parquet/parquet_reader.h"
52
#include "storage/segment/condition_cache.h"
53
#include "util/debug_points.h"
54
#include "util/string_util.h"
55
56
namespace doris::format {
57
namespace {
58
59
template <typename T, typename Formatter>
60
61
std::string join_table_reader_debug_strings(const std::vector<T>& values, Formatter formatter) {
61
61
    std::ostringstream out;
62
61
    out << "[";
63
70
    for (size_t i = 0; i < values.size(); ++i) {
64
9
        if (i > 0) {
65
3
            out << ", ";
66
3
        }
67
9
        out << formatter(values[i]);
68
9
    }
69
61
    out << "]";
70
61
    return out.str();
71
61
}
table_reader.cpp:_ZN5doris6format12_GLOBAL__N_131join_table_reader_debug_stringsINS0_16ColumnDefinitionEZNKS0_11TableReader12debug_stringB5cxx11EvE3$_0EENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt6vectorIT_SaISD_EET0_
Line
Count
Source
60
12
std::string join_table_reader_debug_strings(const std::vector<T>& values, Formatter formatter) {
61
12
    std::ostringstream out;
62
12
    out << "[";
63
14
    for (size_t i = 0; i < values.size(); ++i) {
64
2
        if (i > 0) {
65
1
            out << ", ";
66
1
        }
67
2
        out << formatter(values[i]);
68
2
    }
69
12
    out << "]";
70
12
    return out.str();
71
12
}
table_reader.cpp:_ZN5doris6format12_GLOBAL__N_131join_table_reader_debug_stringsINS0_11TableFilterEZNKS0_11TableReader12debug_stringB5cxx11EvE3$_1EENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt6vectorIT_SaISD_EET0_
Line
Count
Source
60
12
std::string join_table_reader_debug_strings(const std::vector<T>& values, Formatter formatter) {
61
12
    std::ostringstream out;
62
12
    out << "[";
63
13
    for (size_t i = 0; i < values.size(); ++i) {
64
1
        if (i > 0) {
65
0
            out << ", ";
66
0
        }
67
1
        out << formatter(values[i]);
68
1
    }
69
12
    out << "]";
70
12
    return out.str();
71
12
}
table_reader.cpp:_ZN5doris6format12_GLOBAL__N_131join_table_reader_debug_stringsINS0_11GlobalIndexEZNS1_25table_filter_debug_stringB5cxx11ERKNS0_11TableFilterEE3$_0EENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt6vectorIT_SaISF_EET0_
Line
Count
Source
60
1
std::string join_table_reader_debug_strings(const std::vector<T>& values, Formatter formatter) {
61
1
    std::ostringstream out;
62
1
    out << "[";
63
2
    for (size_t i = 0; i < values.size(); ++i) {
64
1
        if (i > 0) {
65
0
            out << ", ";
66
0
        }
67
1
        out << formatter(values[i]);
68
1
    }
69
1
    out << "]";
70
1
    return out.str();
71
1
}
table_reader.cpp:_ZN5doris6format12_GLOBAL__N_131join_table_reader_debug_stringsISt10shared_ptrINS_12VExprContextEEZNKS0_11TableReader12debug_stringB5cxx11EvE3$_2EENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt6vectorIT_SaISF_EET0_
Line
Count
Source
60
12
std::string join_table_reader_debug_strings(const std::vector<T>& values, Formatter formatter) {
61
12
    std::ostringstream out;
62
12
    out << "[";
63
13
    for (size_t i = 0; i < values.size(); ++i) {
64
1
        if (i > 0) {
65
0
            out << ", ";
66
0
        }
67
1
        out << formatter(values[i]);
68
1
    }
69
12
    out << "]";
70
12
    return out.str();
71
12
}
table_reader.cpp:_ZN5doris6format12_GLOBAL__N_131join_table_reader_debug_stringsINS0_16ColumnDefinitionEZNKS0_11TableReader12debug_stringB5cxx11EvE3$_3EENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt6vectorIT_SaISD_EET0_
Line
Count
Source
60
12
std::string join_table_reader_debug_strings(const std::vector<T>& values, Formatter formatter) {
61
12
    std::ostringstream out;
62
12
    out << "[";
63
14
    for (size_t i = 0; i < values.size(); ++i) {
64
2
        if (i > 0) {
65
1
            out << ", ";
66
1
        }
67
2
        out << formatter(values[i]);
68
2
    }
69
12
    out << "]";
70
12
    return out.str();
71
12
}
table_reader.cpp:_ZN5doris6format12_GLOBAL__N_131join_table_reader_debug_stringsINS0_11TableReader15FileBlockColumnEZNKS3_12debug_stringB5cxx11EvE3$_4EENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKSt6vectorIT_SaISD_EET0_
Line
Count
Source
60
12
std::string join_table_reader_debug_strings(const std::vector<T>& values, Formatter formatter) {
61
12
    std::ostringstream out;
62
12
    out << "[";
63
14
    for (size_t i = 0; i < values.size(); ++i) {
64
2
        if (i > 0) {
65
1
            out << ", ";
66
1
        }
67
2
        out << formatter(values[i]);
68
2
    }
69
12
    out << "]";
70
12
    return out.str();
71
12
}
72
73
12
std::string file_format_to_string(FileFormat format) {
74
12
    switch (format) {
75
5
    case FileFormat::PARQUET:
76
5
        return "PARQUET";
77
1
    case FileFormat::ORC:
78
1
        return "ORC";
79
1
    case FileFormat::CSV:
80
1
        return "CSV";
81
1
    case FileFormat::JSON:
82
1
        return "JSON";
83
1
    case FileFormat::TEXT:
84
1
        return "TEXT";
85
1
    case FileFormat::JNI:
86
1
        return "JNI";
87
1
    case FileFormat::NATIVE:
88
1
        return "NATIVE";
89
1
    case FileFormat::ARROW:
90
1
        return "ARROW";
91
12
    }
92
0
    return "UNKNOWN";
93
12
}
94
95
12
std::string push_down_agg_to_string(TPushAggOp::type op) {
96
12
    switch (op) {
97
8
    case TPushAggOp::NONE:
98
8
        return "NONE";
99
1
    case TPushAggOp::COUNT:
100
1
        return "COUNT";
101
1
    case TPushAggOp::MINMAX:
102
1
        return "MINMAX";
103
1
    case TPushAggOp::MIX:
104
1
        return "MIX";
105
1
    case TPushAggOp::COUNT_ON_INDEX:
106
1
        return "COUNT_ON_INDEX";
107
12
    }
108
0
    return "UNKNOWN";
109
12
}
110
111
12
std::string current_file_debug_string(const std::unique_ptr<ScanTask>& task) {
112
12
    if (task == nullptr || task->data_file == nullptr) {
113
11
        return "null";
114
11
    }
115
1
    const auto& file = *task->data_file;
116
1
    std::ostringstream out;
117
1
    out << "FileDescription{path=" << file.path << ", file_size=" << file.file_size
118
1
        << ", range_start_offset=" << file.range_start_offset << ", range_size=" << file.range_size
119
1
        << ", mtime=" << file.mtime << ", fs_name=" << file.fs_name
120
1
        << ", is_immutable=" << file.is_immutable
121
1
        << ", file_cache_admission=" << file.file_cache_admission << "}";
122
1
    return out.str();
123
12
}
124
125
12
std::string partition_values_debug_string(const std::map<std::string, Field>& partition_values) {
126
12
    std::ostringstream out;
127
12
    out << "{";
128
12
    size_t idx = 0;
129
12
    for (const auto& [key, _] : partition_values) {
130
1
        if (idx++ > 0) {
131
0
            out << ", ";
132
0
        }
133
1
        out << key;
134
1
    }
135
12
    out << "}";
136
12
    return out.str();
137
12
}
138
139
679k
const schema::external::TField* get_field_ptr(const schema::external::TFieldPtr& field_ptr) {
140
679k
    if (!field_ptr.__isset.field_ptr || field_ptr.field_ptr == nullptr) {
141
0
        return nullptr;
142
0
    }
143
679k
    return field_ptr.field_ptr.get();
144
679k
}
145
146
666k
bool external_field_matches_name(const schema::external::TField& field, const std::string& name) {
147
667k
    if (field.__isset.name && to_lower(field.name) == to_lower(name)) {
148
77.7k
        return true;
149
77.7k
    }
150
588k
    return field.__isset.name_mapping &&
151
588k
           std::ranges::any_of(field.name_mapping, [&](const std::string& alias) {
152
16
               return to_lower(alias) == to_lower(name);
153
16
           });
154
666k
}
155
156
DataTypePtr find_struct_child_type_by_external_field(const DataTypeStruct& struct_type,
157
21.1k
                                                     const schema::external::TField& field) {
158
37.4k
    for (size_t field_idx = 0; field_idx < struct_type.get_elements().size(); ++field_idx) {
159
35.2k
        if (external_field_matches_name(field, struct_type.get_element_name(field_idx))) {
160
18.9k
            return struct_type.get_element(field_idx);
161
18.9k
        }
162
35.2k
    }
163
2.24k
    return nullptr;
164
21.1k
}
165
166
DataTypePtr restore_current_primitive_type(const schema::external::TField& field,
167
102k
                                           DataTypePtr fallback_type) {
168
102k
    if (!field.__isset.type) {
169
8
        return fallback_type;
170
8
    }
171
102k
    const auto primitive_type = thrift_to_type(field.type.type);
172
102k
    if (is_complex_type(primitive_type)) {
173
26.3k
        return fallback_type;
174
26.3k
    }
175
    // The delete file can expose an older physical type, but initial defaults belong to the
176
    // current table field. Restore that type from FE before parsing the default and let the table
177
    // reader apply the normal promotion cast to the delete-key type.
178
76.3k
    return DataTypeFactory::instance().create_data_type(
179
76.3k
            primitive_type, false, field.type.__isset.precision ? field.type.precision : 0,
180
76.3k
            field.type.__isset.scale ? field.type.scale : 0,
181
76.3k
            field.type.__isset.len ? field.type.len : -1);
182
102k
}
183
184
ColumnDefinition build_schema_column_from_external_field(const schema::external::TField& field,
185
102k
                                                         DataTypePtr type) {
186
102k
    type = restore_current_primitive_type(field, std::move(type));
187
102k
    ColumnDefinition column {
188
18.4E
            .identifier = field.__isset.id ? Field::create_field<TYPE_INT>(field.id) : Field {},
189
102k
            .name = field.__isset.name ? field.name : "",
190
102k
            .name_mapping =
191
102k
                    field.__isset.name_mapping ? field.name_mapping : std::vector<std::string> {},
192
102k
            .type = std::move(type),
193
102k
            .children = {},
194
102k
            .default_expr = nullptr,
195
102k
            .initial_default_value = field.__isset.initial_default_value
196
102k
                                             ? std::make_optional(field.initial_default_value)
197
102k
                                             : std::nullopt,
198
102k
            .initial_default_value_is_base64 = field.__isset.initial_default_value_is_base64 &&
199
102k
                                               field.initial_default_value_is_base64,
200
102k
            .is_partition_key = false,
201
102k
    };
202
102k
    if (column.type == nullptr || !field.__isset.nestedField) {
203
76.3k
        return column;
204
76.3k
    }
205
206
26.3k
    const auto nested_type = remove_nullable(column.type);
207
26.3k
    switch (nested_type->get_primitive_type()) {
208
8.99k
    case TYPE_STRUCT: {
209
8.99k
        if (!field.nestedField.__isset.struct_field ||
210
8.99k
            !field.nestedField.struct_field.__isset.fields) {
211
0
            return column;
212
0
        }
213
8.99k
        const auto& struct_type = assert_cast<const DataTypeStruct&>(*nested_type);
214
21.1k
        for (const auto& child_ptr : field.nestedField.struct_field.fields) {
215
21.1k
            const auto* child_field = get_field_ptr(child_ptr);
216
21.1k
            if (child_field == nullptr || !child_field->__isset.name) {
217
0
                continue;
218
0
            }
219
21.1k
            auto child_type = find_struct_child_type_by_external_field(struct_type, *child_field);
220
21.1k
            if (child_type == nullptr) {
221
2.24k
                continue;
222
2.24k
            }
223
18.9k
            column.children.push_back(
224
18.9k
                    build_schema_column_from_external_field(*child_field, child_type));
225
18.9k
        }
226
8.99k
        break;
227
8.99k
    }
228
9.74k
    case TYPE_ARRAY: {
229
9.74k
        if (!field.nestedField.__isset.array_field ||
230
9.74k
            !field.nestedField.array_field.__isset.item_field) {
231
0
            return column;
232
0
        }
233
9.74k
        const auto* item_field = get_field_ptr(field.nestedField.array_field.item_field);
234
9.74k
        if (item_field == nullptr) {
235
0
            return column;
236
0
        }
237
9.74k
        const auto& array_type = assert_cast<const DataTypeArray&>(*nested_type);
238
9.74k
        auto child =
239
9.74k
                build_schema_column_from_external_field(*item_field, array_type.get_nested_type());
240
9.74k
        child.name = "element";
241
9.74k
        if (child.has_identifier_name()) {
242
0
            child.identifier = Field::create_field<TYPE_STRING>(child.name);
243
0
        }
244
9.74k
        column.children.push_back(std::move(child));
245
9.74k
        break;
246
9.74k
    }
247
7.66k
    case TYPE_MAP: {
248
7.66k
        if (!field.nestedField.__isset.map_field ||
249
7.66k
            !field.nestedField.map_field.__isset.key_field ||
250
7.66k
            !field.nestedField.map_field.__isset.value_field) {
251
0
            return column;
252
0
        }
253
7.66k
        const auto& map_type = assert_cast<const DataTypeMap&>(*nested_type);
254
7.66k
        const auto* key_field = get_field_ptr(field.nestedField.map_field.key_field);
255
7.66k
        if (key_field != nullptr) {
256
7.66k
            auto child =
257
7.66k
                    build_schema_column_from_external_field(*key_field, map_type.get_key_type());
258
7.66k
            child.name = "key";
259
7.66k
            if (child.has_identifier_name()) {
260
0
                child.identifier = Field::create_field<TYPE_STRING>(child.name);
261
0
            }
262
7.66k
            column.children.push_back(std::move(child));
263
7.66k
        }
264
7.66k
        const auto* value_field = get_field_ptr(field.nestedField.map_field.value_field);
265
7.66k
        if (value_field != nullptr) {
266
7.66k
            auto child = build_schema_column_from_external_field(*value_field,
267
7.66k
                                                                 map_type.get_value_type());
268
7.66k
            child.name = "value";
269
7.66k
            if (child.has_identifier_name()) {
270
0
                child.identifier = Field::create_field<TYPE_STRING>(child.name);
271
0
            }
272
7.66k
            column.children.push_back(std::move(child));
273
7.66k
        }
274
7.66k
        break;
275
7.66k
    }
276
0
    default:
277
0
        break;
278
26.3k
    }
279
26.4k
    return column;
280
26.3k
}
281
282
const schema::external::TField* find_external_root_field(const TFileScanRangeParams* params,
283
233k
                                                         const ColumnDefinition& column) {
284
233k
    if (params == nullptr || !params->__isset.history_schema_info ||
285
233k
        params->history_schema_info.empty()) {
286
174k
        return nullptr;
287
174k
    }
288
59.1k
    const auto* schema = &params->history_schema_info.front();
289
59.1k
    if (params->__isset.current_schema_id) {
290
59.1k
        for (const auto& candidate_schema : params->history_schema_info) {
291
59.1k
            if (candidate_schema.__isset.schema_id &&
292
59.1k
                candidate_schema.schema_id == params->current_schema_id) {
293
59.1k
                schema = &candidate_schema;
294
59.1k
                break;
295
59.1k
            }
296
59.1k
        }
297
59.1k
    }
298
59.1k
    if (!schema->__isset.root_field || !schema->root_field.__isset.fields) {
299
0
        return nullptr;
300
0
    }
301
633k
    for (const auto& field_ptr : schema->root_field.fields) {
302
633k
        const auto* field = get_field_ptr(field_ptr);
303
633k
        if (field == nullptr) {
304
0
            continue;
305
0
        }
306
633k
        if (external_field_matches_name(*field, column.name)) {
307
58.7k
            return field;
308
58.7k
        }
309
633k
    }
310
368
    return nullptr;
311
59.1k
}
312
313
2
std::string expr_context_debug_string(const VExprContextSPtr& context) {
314
2
    if (context == nullptr) {
315
0
        return "null";
316
0
    }
317
2
    const auto root = context->root();
318
2
    if (root == nullptr) {
319
0
        return "VExprContext{root=null}";
320
0
    }
321
2
    std::ostringstream out;
322
2
    out << "VExprContext{root_name=" << root->expr_name() << ", root_debug=" << root->debug_string()
323
2
        << "}";
324
2
    return out.str();
325
2
}
326
327
1
std::string table_filter_debug_string(const TableFilter& filter) {
328
1
    std::ostringstream out;
329
1
    out << "TableFilter{conjunct=" << expr_context_debug_string(filter.conjunct)
330
1
        << ", global_indices="
331
1
        << join_table_reader_debug_strings(
332
1
                   filter.global_indices,
333
1
                   [](GlobalIndex global_index) { return std::to_string(global_index.value()); })
334
1
        << "}";
335
1
    return out.str();
336
1
}
337
338
9.70k
bool contains_runtime_filter(const VExprContextSPtrs& conjuncts) {
339
10.6k
    return std::ranges::any_of(conjuncts, [](const auto& conjunct) {
340
10.6k
        return conjunct != nullptr && conjunct->root() != nullptr &&
341
10.6k
               conjunct->root()->is_rf_wrapper();
342
10.6k
    });
343
9.70k
}
344
345
146k
void collect_global_indices(const VExprSPtr& expr, std::set<GlobalIndex>* global_indices) {
346
146k
    if (expr == nullptr) {
347
0
        return;
348
0
    }
349
146k
    if (expr->is_rf_wrapper()) {
350
        // RuntimeFilterExpr wraps a real predicate expression but its own thrift node can still
351
        // look like SLOT_REF. Collect indices from the wrapped predicate; do not cast the wrapper
352
        // itself to VSlotRef.
353
6.06k
        collect_global_indices(expr->get_impl(), global_indices);
354
6.06k
        return;
355
6.06k
    }
356
140k
    if (expr->is_slot_ref()) {
357
45.4k
        const auto* slot_ref = assert_cast<const VSlotRef*>(expr.get());
358
45.4k
        DORIS_CHECK(slot_ref->column_id() >= 0);
359
45.4k
        global_indices->insert(GlobalIndex(cast_set<size_t>(slot_ref->column_id())));
360
45.4k
    }
361
140k
    for (const auto& child : expr->children()) {
362
97.1k
        collect_global_indices(child, global_indices);
363
97.1k
    }
364
140k
}
365
366
Status build_table_filters_from_conjunct(const VExprContextSPtr& conjunct, RuntimeState* state,
367
40.0k
                                         std::vector<TableFilter>* table_filters) {
368
40.0k
    if (conjunct == nullptr) {
369
0
        return Status::OK();
370
0
    }
371
40.0k
    std::set<GlobalIndex> global_indices;
372
40.0k
    collect_global_indices(conjunct->root(), &global_indices);
373
40.0k
    if (!global_indices.empty()) {
374
39.9k
        TableFilter table_filter;
375
39.9k
        VExprSPtr filter_root;
376
39.9k
        RETURN_IF_ERROR(clone_table_expr_tree(conjunct->root(), &filter_root));
377
39.9k
        table_filter.conjunct = VExprContext::create_shared(std::move(filter_root));
378
41.0k
        for (const auto global_index : global_indices) {
379
41.0k
            table_filter.global_indices.push_back(global_index);
380
41.0k
        }
381
39.9k
        table_filters->push_back(std::move(table_filter));
382
39.9k
    }
383
40.0k
    return Status::OK();
384
40.0k
}
385
386
Status parse_deletion_vector(const char* buf, size_t buffer_size, DeleteFileDesc::Format format,
387
1.43k
                             DeletionVector* deletion_vector) {
388
1.43k
    DORIS_CHECK(buf != nullptr);
389
1.43k
    DORIS_CHECK(deletion_vector != nullptr);
390
1.43k
    DORIS_CHECK(format == DeleteFileDesc::Format::PAIMON ||
391
1.43k
                format == DeleteFileDesc::Format::ICEBERG);
392
393
1.43k
    if (format == DeleteFileDesc::Format::PAIMON) {
394
1
        RETURN_IF_ERROR(decode_paimon_deletion_vector_buffer(buf, buffer_size, deletion_vector));
395
1
        return Status::OK();
396
1
    }
397
398
1.43k
    return decode_iceberg_deletion_vector_buffer(buf, buffer_size, deletion_vector);
399
1.43k
}
400
401
} // namespace
402
403
std::shared_ptr<io::FileSystemProperties> create_system_properties(
404
38.9k
        const TFileScanRangeParams* scan_params) {
405
38.9k
    auto system_properties = std::make_shared<io::FileSystemProperties>();
406
38.9k
    if (scan_params == nullptr || !scan_params->__isset.file_type) {
407
587
        system_properties->system_type = TFileType::FILE_LOCAL;
408
587
        return system_properties;
409
587
    }
410
38.3k
    system_properties->system_type = scan_params->file_type;
411
38.3k
    system_properties->properties = scan_params->properties;
412
38.3k
    system_properties->hdfs_params = scan_params->hdfs_params;
413
38.3k
    if (scan_params->__isset.broker_addresses) {
414
0
        system_properties->broker_addresses.assign(scan_params->broker_addresses.begin(),
415
0
                                                   scan_params->broker_addresses.end());
416
0
    }
417
38.3k
    return system_properties;
418
38.9k
}
419
420
12
std::string TableReader::debug_string() const {
421
12
    std::ostringstream out;
422
12
    out << "TableReader{format=" << file_format_to_string(_format)
423
12
        << ", push_down_agg_type=" << push_down_agg_to_string(_push_down_agg_type)
424
12
        << ", aggregate_pushdown_tried=" << _aggregate_pushdown_tried
425
12
        << ", has_current_reader=" << (_data_reader.reader != nullptr)
426
12
        << ", has_current_task=" << (_current_task != nullptr)
427
12
        << ", current_file=" << current_file_debug_string(_current_task)
428
12
        << ", has_delete_rows=" << (_delete_rows != nullptr)
429
12
        << ", delete_row_count=" << (_delete_rows == nullptr ? 0 : _delete_rows->size())
430
12
        << ", has_deletion_vector=" << (_deletion_vector != nullptr)
431
12
        << ", deletion_vector_cardinality="
432
12
        << (_deletion_vector == nullptr ? 0 : _deletion_vector->cardinality())
433
12
        << ", has_system_properties=" << (_system_properties != nullptr) << ", system_type="
434
12
        << (_system_properties == nullptr ? static_cast<int>(TFileType::FILE_LOCAL)
435
12
                                          : static_cast<int>(_system_properties->system_type))
436
12
        << ", has_scan_params=" << (_scan_params != nullptr)
437
12
        << ", has_io_ctx=" << (_io_ctx != nullptr)
438
12
        << ", has_runtime_state=" << (_runtime_state != nullptr)
439
12
        << ", has_scanner_profile=" << (_scanner_profile != nullptr)
440
12
        << ", mapper_options=" << _mapper_options.debug_string() << ", projected_columns="
441
12
        << join_table_reader_debug_strings(
442
12
                   _projected_columns,
443
12
                   [](const ColumnDefinition& column) { return column.debug_string(); })
444
12
        << ", partition_values=" << partition_values_debug_string(_partition_values)
445
12
        << ", table_filters="
446
12
        << join_table_reader_debug_strings(
447
12
                   _table_filters,
448
12
                   [](const TableFilter& filter) { return table_filter_debug_string(filter); })
449
12
        << ", conjunct_count=" << _conjuncts.size() << ", conjuncts="
450
12
        << join_table_reader_debug_strings(_conjuncts,
451
12
                                           [](const VExprContextSPtr& conjunct) {
452
1
                                               return expr_context_debug_string(conjunct);
453
1
                                           })
454
12
        << ", file_schema="
455
12
        << join_table_reader_debug_strings(
456
12
                   _data_reader.file_schema,
457
12
                   [](const ColumnDefinition& field) { return field.debug_string(); })
458
12
        << ", file_block_layout="
459
12
        << join_table_reader_debug_strings(
460
12
                   _data_reader.file_block_layout,
461
12
                   [](const FileBlockColumn& column) {
462
2
                       std::ostringstream column_out;
463
2
                       column_out << "FileBlockColumn{file_column_id=" << column.file_column_id
464
2
                                  << ", name=" << column.name << ", type="
465
2
                                  << (column.type == nullptr ? "null" : column.type->get_name())
466
2
                                  << "}";
467
2
                       return column_out.str();
468
2
                   })
469
12
        << ", block_template_columns=" << _data_reader.block_template.columns()
470
12
        << ", column_mapper="
471
12
        << (_data_reader.column_mapper == nullptr ? "null"
472
12
                                                  : _data_reader.column_mapper->debug_string())
473
12
        << "}";
474
12
    return out.str();
475
12
}
476
477
Status TableReader::annotate_projected_column(const TFileScanSlotInfo& slot_info,
478
                                              ProjectedColumnBuildContext* context,
479
233k
                                              ColumnDefinition* column) const {
480
233k
    (void)slot_info;
481
233k
    DORIS_CHECK(context != nullptr);
482
233k
    DORIS_CHECK(column != nullptr);
483
233k
    context->schema_column.reset();
484
233k
    const auto* schema_field = find_external_root_field(context->scan_params, *column);
485
233k
    if (schema_field == nullptr) {
486
174k
        return Status::OK();
487
174k
    }
488
58.7k
    context->schema_column = build_schema_column_from_external_field(*schema_field, column->type);
489
58.7k
    column->identifier = context->schema_column->identifier;
490
58.7k
    column->name_mapping = context->schema_column->name_mapping;
491
58.7k
    return Status::OK();
492
233k
}
493
494
std::optional<ColumnDefinition> TableReader::_find_current_table_column_by_field_id(
495
9
        int32_t field_id, DataTypePtr type) const {
496
9
    if (_scan_params == nullptr || !_scan_params->__isset.history_schema_info ||
497
9
        _scan_params->history_schema_info.empty()) {
498
3
        return std::nullopt;
499
3
    }
500
6
    const auto* schema = &_scan_params->history_schema_info.front();
501
6
    if (_scan_params->__isset.current_schema_id) {
502
6
        for (const auto& candidate_schema : _scan_params->history_schema_info) {
503
6
            if (candidate_schema.__isset.schema_id &&
504
6
                candidate_schema.schema_id == _scan_params->current_schema_id) {
505
6
                schema = &candidate_schema;
506
6
                break;
507
6
            }
508
6
        }
509
6
    }
510
6
    if (!schema->__isset.root_field || !schema->root_field.__isset.fields) {
511
0
        return std::nullopt;
512
0
    }
513
11
    for (const auto& field_ptr : schema->root_field.fields) {
514
11
        const auto* field = get_field_ptr(field_ptr);
515
11
        if (field != nullptr && field->__isset.id && field->id == field_id) {
516
6
            return build_schema_column_from_external_field(*field, std::move(type));
517
6
        }
518
11
    }
519
0
    return std::nullopt;
520
6
}
521
522
38.9k
Status TableReader::init(TableReadOptions&& options) {
523
38.9k
    _scan_params = options.scan_params;
524
38.9k
    _format = options.format;
525
38.9k
    _io_ctx = options.io_ctx;
526
38.9k
    _runtime_state = options.runtime_state;
527
38.9k
    _scanner_profile = options.scanner_profile;
528
38.9k
    _file_slot_descs = options.file_slot_descs;
529
38.9k
    _push_down_agg_type = options.push_down_agg_type;
530
38.9k
    _condition_cache_digest = options.condition_cache_digest;
531
38.9k
    _projected_columns = std::move(options.projected_columns);
532
38.9k
    _system_properties = create_system_properties(_scan_params);
533
38.9k
    _mapper_options.mode = TableColumnMappingMode::BY_NAME;
534
38.9k
    _conjuncts = std::move(options.conjuncts);
535
536
38.9k
    if (_scanner_profile != nullptr) {
537
38.8k
        static const char* table_profile = "TableReader";
538
38.8k
        ADD_TIMER_WITH_LEVEL(_scanner_profile, table_profile, 1);
539
38.8k
        _profile.num_delete_files = ADD_CHILD_COUNTER_WITH_LEVEL(_scanner_profile, "NumDeleteFiles",
540
38.8k
                                                                 TUnit::UNIT, table_profile, 1);
541
38.8k
        _profile.num_delete_rows = ADD_CHILD_COUNTER_WITH_LEVEL(_scanner_profile, "NumDeleteRows",
542
38.8k
                                                                TUnit::UNIT, table_profile, 1);
543
38.8k
        _profile.parse_delete_file_time = ADD_CHILD_TIMER_WITH_LEVEL(
544
38.8k
                _scanner_profile, "ParseDeleteFileTime", table_profile, 1);
545
38.8k
        _profile.decoded_dv_cache_hit_count =
546
38.8k
                ADD_CHILD_COUNTER_WITH_LEVEL(_scanner_profile, "DeletionVectorDecodedCacheHitCount",
547
38.8k
                                             TUnit::UNIT, table_profile, 1);
548
38.8k
        _profile.decoded_dv_cache_miss_count = ADD_CHILD_COUNTER_WITH_LEVEL(
549
38.8k
                _scanner_profile, "DeletionVectorDecodedCacheMissCount", TUnit::UNIT, table_profile,
550
38.8k
                1);
551
38.8k
        _profile.dv_file_cache_hit_count = ADD_CHILD_COUNTER_WITH_LEVEL(
552
38.8k
                _scanner_profile, "DeletionVectorFileCacheHitCount", TUnit::UNIT, table_profile, 1);
553
38.8k
        _profile.dv_file_cache_miss_count =
554
38.8k
                ADD_CHILD_COUNTER_WITH_LEVEL(_scanner_profile, "DeletionVectorFileCacheMissCount",
555
38.8k
                                             TUnit::UNIT, table_profile, 1);
556
38.8k
        _profile.dv_file_cache_peer_read_count = ADD_CHILD_COUNTER_WITH_LEVEL(
557
38.8k
                _scanner_profile, "DeletionVectorFileCachePeerReadCount", TUnit::UNIT,
558
38.8k
                table_profile, 1);
559
38.8k
        _profile.exec_timer =
560
38.8k
                ADD_CHILD_TIMER_WITH_LEVEL(_scanner_profile, "GetBlockTime", table_profile, 1);
561
38.8k
        _profile.prepare_split_timer =
562
38.8k
                ADD_CHILD_TIMER_WITH_LEVEL(_scanner_profile, "PrepareSplitTime", table_profile, 1);
563
38.8k
        _profile.finalize_timer =
564
38.8k
                ADD_CHILD_TIMER_WITH_LEVEL(_scanner_profile, "FinalizeBlockTime", table_profile, 1);
565
38.8k
        _profile.create_reader_timer =
566
38.8k
                ADD_CHILD_TIMER_WITH_LEVEL(_scanner_profile, "CreateReaderTime", table_profile, 1);
567
38.8k
        _profile.pushdown_agg_timer =
568
38.8k
                ADD_CHILD_TIMER_WITH_LEVEL(_scanner_profile, "PushDownAggTime", table_profile, 1);
569
38.8k
        _profile.open_reader_timer =
570
38.8k
                ADD_CHILD_TIMER_WITH_LEVEL(_scanner_profile, "OpenReaderTime", table_profile, 1);
571
38.8k
        _profile.runtime_filter_partition_prune_timer = ADD_TIMER_WITH_LEVEL(
572
38.8k
                _scanner_profile, "FileScannerRuntimeFilterPartitionPruningTime", 1);
573
38.8k
        _profile.runtime_filter_partition_pruned_range_counter = ADD_COUNTER_WITH_LEVEL(
574
38.8k
                _scanner_profile, "RuntimeFilterPartitionPrunedRangeNum", TUnit::UNIT, 1);
575
38.8k
    }
576
38.9k
    return Status::OK();
577
38.9k
}
578
579
59.2k
Status TableReader::_build_table_filters_from_conjuncts() {
580
59.2k
    _table_filters.clear();
581
59.2k
    _constant_pruning_safe_filter_count = 0;
582
59.2k
    bool in_safe_prefix = true;
583
59.2k
    for (const auto& conjunct : _conjuncts) {
584
39.9k
        DORIS_CHECK(conjunct != nullptr);
585
39.9k
        DORIS_CHECK(conjunct->root() != nullptr);
586
        // `_table_filters` omits expressions without slot references, but such an expression still
587
        // occupies a position in the row-level conjunct order. Record how many localized filters
588
        // precede the first unsafe original conjunct so constant pruning cannot jump over a
589
        // slotless non-deterministic/error-preserving barrier.
590
39.9k
        if (in_safe_prefix && !_is_safe_to_pre_execute(conjunct)) {
591
3
            in_safe_prefix = false;
592
3
        }
593
39.9k
        if (!in_safe_prefix) {
594
5
            continue;
595
5
        }
596
39.9k
        RETURN_IF_ERROR(
597
39.9k
                build_table_filters_from_conjunct(conjunct, _runtime_state, &_table_filters));
598
39.9k
        _constant_pruning_safe_filter_count = _table_filters.size();
599
39.9k
    }
600
59.2k
    return Status::OK();
601
59.2k
}
602
603
58.0k
Status TableReader::_open_local_filter_exprs(const FileScanRequest& file_request) {
604
58.0k
    RowDescriptor row_desc;
605
58.0k
    for (const auto& conjunct : file_request.conjuncts) {
606
32.7k
        RETURN_IF_ERROR(conjunct->prepare(_runtime_state, row_desc));
607
32.7k
        RETURN_IF_ERROR(conjunct->open(_runtime_state));
608
32.7k
    }
609
58.0k
    for (const auto& delete_conjunct : file_request.delete_conjuncts) {
610
8.01k
        RETURN_IF_ERROR(delete_conjunct->prepare(_runtime_state, row_desc));
611
8.01k
        RETURN_IF_ERROR(delete_conjunct->open(_runtime_state));
612
8.01k
    }
613
58.0k
    return Status::OK();
614
58.0k
}
615
616
58.1k
bool TableReader::_should_enable_condition_cache(const FileScanRequest& file_request) const {
617
58.1k
    if (_condition_cache_digest == 0 || _push_down_agg_type == TPushAggOp::type::COUNT ||
618
58.1k
        _current_file_description == std::nullopt || _data_reader.reader == nullptr) {
619
13.0k
        return false;
620
13.0k
    }
621
    // Condition cache is populated by file readers after evaluating file-local row-level
622
    // conjuncts. Metadata pruning can skip row groups/pages, but it does not produce a per-row
623
    // survivor bitmap that can safely populate the cache.
624
45.0k
    if (file_request.conjuncts.empty()) {
625
32.3k
        return false;
626
32.3k
    }
627
    // Delete files/deletion vectors are table-format state. They may change independently of the
628
    // data file path/mtime/size used by the external cache key, so caching their result can become
629
    // stale. Keep delete filtering enabled, but do not read or write condition cache.
630
12.7k
    if (_delete_rows != nullptr || _deletion_vector != nullptr ||
631
12.7k
        !file_request.delete_conjuncts.empty()) {
632
2.80k
        return false;
633
2.80k
    }
634
    // Runtime filters can arrive late and their payload is not guaranteed to be represented by the
635
    // scan-local digest. Without a read-only mode, a MISS could insert a bitmap for P AND RF under
636
    // the digest for only P. This mirrors the old FileScanner guard.
637
9.93k
    return !contains_runtime_filter(file_request.conjuncts);
638
12.7k
}
639
640
58.0k
Status TableReader::_init_reader_condition_cache(const FileScanRequest& file_request) {
641
58.0k
    _condition_cache = nullptr;
642
58.0k
    _condition_cache_ctx = nullptr;
643
58.0k
    if (!_should_enable_condition_cache(file_request)) {
644
49.3k
        return Status::OK();
645
49.3k
    }
646
647
8.67k
    auto* cache = segment_v2::ConditionCache::instance();
648
8.67k
    if (cache == nullptr) {
649
0
        return Status::OK();
650
0
    }
651
8.67k
    const auto& file = *_current_file_description;
652
8.67k
    _condition_cache_key = segment_v2::ConditionCache::ExternalCacheKey(
653
8.67k
            file.path, file.mtime, file.file_size, _condition_cache_digest, file.range_start_offset,
654
8.67k
            file.range_size,
655
8.67k
            segment_v2::ConditionCache::ExternalCacheKey::BASE_GRANULE_AWARE_VERSION);
656
657
8.67k
    segment_v2::ConditionCacheHandle handle;
658
8.67k
    const bool condition_cache_hit = cache->lookup(_condition_cache_key, &handle);
659
8.67k
    if (condition_cache_hit) {
660
3.14k
        _condition_cache = handle.get_filter_result();
661
3.14k
        ++_condition_cache_hit_count;
662
5.52k
    } else {
663
5.52k
        const int64_t total_rows = _data_reader.reader->get_total_rows();
664
5.52k
        if (total_rows <= 0) {
665
1.71k
            return Status::OK();
666
1.71k
        }
667
        // Add one guard granule for split ranges that start in the middle of a granule. A guard
668
        // false bit beyond the real range never overlaps real rows, but avoids boundary overflow
669
        // when a reader marks the last partial granule.
670
3.81k
        const size_t num_granules = (total_rows + ConditionCacheContext::GRANULE_SIZE - 1) /
671
3.81k
                                    ConditionCacheContext::GRANULE_SIZE;
672
3.81k
        _condition_cache = std::make_shared<std::vector<bool>>(num_granules + 1, false);
673
3.81k
    }
674
675
6.96k
    if (_condition_cache != nullptr) {
676
6.89k
        _condition_cache_ctx = std::make_shared<ConditionCacheContext>();
677
6.89k
        _condition_cache_ctx->is_hit = condition_cache_hit;
678
6.89k
        _condition_cache_ctx->filter_result = _condition_cache;
679
6.89k
        _condition_cache_ctx->num_granules = _condition_cache->size();
680
6.89k
        if (condition_cache_hit) {
681
3.15k
            _condition_cache_ctx->base_granule = handle.get_base_granule();
682
3.15k
        }
683
6.89k
        _data_reader.reader->set_condition_cache_context(_condition_cache_ctx);
684
6.89k
    }
685
6.96k
    return Status::OK();
686
8.67k
}
687
688
59.6k
void TableReader::_finalize_reader_condition_cache() {
689
59.6k
    if (_condition_cache_ctx == nullptr || _condition_cache_ctx->is_hit) {
690
55.9k
        _condition_cache = nullptr;
691
55.9k
        _condition_cache_ctx = nullptr;
692
55.9k
        return;
693
55.9k
    }
694
    // LIMIT or scanner cancellation may close a reader before all selected row ranges are visited.
695
    // Unvisited granules remain false in a MISS bitmap, so inserting a partial bitmap would make a
696
    // later HIT skip valid rows. Only publish cache entries after the physical reader reaches EOF.
697
3.73k
    if (!_current_reader_reached_eof) {
698
78
        _condition_cache = nullptr;
699
78
        _condition_cache_ctx = nullptr;
700
78
        return;
701
78
    }
702
3.65k
    DORIS_CHECK(_condition_cache_ctx->num_granules <= _condition_cache->size());
703
3.65k
    _condition_cache->resize(_condition_cache_ctx->num_granules);
704
3.65k
    segment_v2::ConditionCache::instance()->insert(
705
3.65k
            _condition_cache_key, std::move(_condition_cache), _condition_cache_ctx->base_granule);
706
3.65k
    _condition_cache = nullptr;
707
3.65k
    _condition_cache_ctx = nullptr;
708
3.65k
}
709
710
117k
Status TableReader::create_next_reader(bool* eos) {
711
117k
    SCOPED_TIMER(_profile.create_reader_timer);
712
117k
    DCHECK(_data_reader.reader == nullptr);
713
117k
    if (_current_task == nullptr) {
714
57.7k
        *eos = true;
715
57.7k
        return Status::OK();
716
57.7k
    }
717
718
59.5k
    RETURN_IF_ERROR(create_file_reader(&_data_reader.reader));
719
59.5k
    DORIS_CHECK(_data_reader.reader != nullptr);
720
59.5k
    if (_batch_size > 0) {
721
57.4k
        _data_reader.reader->set_batch_size(_batch_size);
722
57.4k
    }
723
59.5k
    Status st = _data_reader.reader->init(_runtime_state);
724
59.5k
    if (!st.ok()) {
725
15
        if (_io_ctx != nullptr && _io_ctx->should_stop && st.is<ErrorCode::END_OF_FILE>()) {
726
0
            *eos = true;
727
0
            _data_reader.reader.reset();
728
0
            return Status::OK();
729
0
        }
730
15
        return st;
731
15
    }
732
59.5k
    st = open_reader();
733
59.5k
    if (!st.ok()) {
734
0
        if (_io_ctx != nullptr && _io_ctx->should_stop && st.is<ErrorCode::END_OF_FILE>()) {
735
0
            *eos = true;
736
0
            _data_reader.reader.reset();
737
0
            return Status::OK();
738
0
        }
739
0
        return st;
740
0
    }
741
59.5k
    if (_data_reader.reader == nullptr) {
742
1.25k
        *eos = _current_task == nullptr;
743
1.25k
        return Status::OK();
744
1.25k
    }
745
58.2k
    *eos = false;
746
58.2k
    return Status::OK();
747
59.5k
}
748
749
59.3k
Status TableReader::create_file_reader(std::unique_ptr<FileReader>* reader) {
750
59.3k
    DORIS_CHECK(reader != nullptr);
751
59.3k
    const bool enable_mapping_timestamp_tz = _scan_params != nullptr &&
752
59.3k
                                             _scan_params->__isset.enable_mapping_timestamp_tz &&
753
59.4k
                                             _scan_params->enable_mapping_timestamp_tz;
754
59.3k
    if (_format == FileFormat::PARQUET) {
755
31.3k
        *reader = std::make_unique<format::parquet::ParquetReader>(
756
31.3k
                _system_properties, _current_task->data_file, _io_ctx, _scanner_profile,
757
31.3k
                _global_rowid_context, enable_mapping_timestamp_tz);
758
31.3k
        return Status::OK();
759
31.3k
    }
760
27.9k
    if (_format == FileFormat::ORC) {
761
22.3k
        *reader = std::make_unique<format::orc::OrcReader>(
762
22.3k
                _system_properties, _current_task->data_file, _io_ctx, _scanner_profile,
763
22.3k
                _global_rowid_context, enable_mapping_timestamp_tz);
764
22.3k
        return Status::OK();
765
22.3k
    }
766
5.64k
    if (_format == FileFormat::CSV) {
767
616
        if (_file_slot_descs == nullptr) {
768
0
            return Status::InvalidArgument("CSV reader requires file slot descriptors");
769
0
        }
770
        // CSV has no embedded schema. TableReader owns table-level mapping, while CsvReader needs
771
        // only the physical file slots plus scan text parameters to build a file-local schema.
772
        // Non-file columns such as partitions/defaults/virtual row ids are intentionally excluded
773
        // from `_file_slot_descs` and are materialized during finalize_chunk().
774
616
        *reader = std::make_unique<format::csv::CsvReader>(
775
616
                _system_properties, _current_task->data_file, _io_ctx, _scanner_profile,
776
616
                _scan_params, *_file_slot_descs, _current_range_compress_type,
777
616
                _current_range_load_id);
778
616
        return Status::OK();
779
616
    }
780
5.03k
    if (_format == FileFormat::TEXT) {
781
4.56k
        if (_file_slot_descs == nullptr) {
782
0
            return Status::InvalidArgument("Text reader requires file slot descriptors");
783
0
        }
784
        // Text files have no embedded schema. As with CSV, TableReader handles table-level mapping
785
        // and only passes physical file slots to the v2 TextReader.
786
4.56k
        *reader = std::make_unique<format::text::TextReader>(
787
4.56k
                _system_properties, _current_task->data_file, _io_ctx, _scanner_profile,
788
4.56k
                _scan_params, *_file_slot_descs, _current_range_compress_type,
789
4.56k
                _current_range_load_id);
790
4.56k
        return Status::OK();
791
4.56k
    }
792
566
    if (_format == FileFormat::JSON) {
793
566
        if (_file_slot_descs == nullptr) {
794
0
            return Status::InvalidArgument("JSON reader requires file slot descriptors");
795
0
        }
796
566
        *reader = std::make_unique<format::json::JsonReader>(
797
566
                _system_properties, _current_task->data_file, _io_ctx, _scanner_profile,
798
566
                _scan_params, _current_file_range_desc, *_file_slot_descs,
799
566
                _current_range_compress_type, _current_range_load_id);
800
566
        return Status::OK();
801
566
    }
802
18.4E
    if (_format == FileFormat::NATIVE) {
803
0
        *reader = std::make_unique<format::native::NativeReader>(
804
0
                _system_properties, _current_task->data_file, _io_ctx, _scanner_profile);
805
0
        return Status::OK();
806
0
    }
807
18.4E
    return Status::NotSupported("TableReader does not support file format {}",
808
18.4E
                                file_format_to_string(_format));
809
18.4E
}
810
811
59.8k
std::unique_ptr<io::FileDescription> create_file_description(const TFileRangeDesc& range) {
812
59.8k
    auto file_description = std::make_unique<io::FileDescription>();
813
59.8k
    file_description->path = range.path;
814
59.8k
    file_description->file_size = range.__isset.file_size ? range.file_size : -1;
815
59.8k
    file_description->mtime = range.__isset.modification_time ? range.modification_time : 0;
816
59.8k
    file_description->range_start_offset = range.__isset.start_offset ? range.start_offset : 0;
817
59.8k
    file_description->range_size = range.__isset.size ? range.size : -1;
818
59.8k
    if (range.__isset.fs_name) {
819
33.0k
        file_description->fs_name = range.fs_name;
820
33.0k
    }
821
59.8k
    if (range.__isset.file_cache_admission) {
822
59.7k
        file_description->file_cache_admission = range.file_cache_admission;
823
59.7k
    }
824
59.8k
    return file_description;
825
59.8k
}
826
827
60.0k
Status TableReader::prepare_split(const SplitReadOptions& options) {
828
60.0k
    SCOPED_TIMER(_profile.prepare_split_timer);
829
60.0k
    _current_split_pruned = false;
830
60.0k
    _all_runtime_filters_applied_for_split = options.all_runtime_filters_applied;
831
60.0k
    if (options.conjuncts.has_value()) {
832
60.0k
        _conjuncts = *options.conjuncts;
833
60.0k
    }
834
    // Update to current split format to handle ORC/PARQUET files in one table.
835
60.0k
    _format = options.current_split_format;
836
60.0k
    _partition_values = std::move(options.partition_values);
837
60.0k
    _current_task.reset();
838
60.0k
    _current_file_description.reset();
839
60.0k
    _current_file_range_desc = options.current_range;
840
60.0k
    _current_range_compress_type = options.current_range.__isset.compress_type
841
60.0k
                                           ? options.current_range.compress_type
842
60.0k
                                           : TFileCompressType::UNKNOWN;
843
60.0k
    _current_range_load_id = options.current_range.__isset.load_id
844
60.0k
                                     ? std::make_optional(options.current_range.load_id)
845
60.0k
                                     : std::nullopt;
846
60.0k
    _global_rowid_context = options.global_rowid_context;
847
60.0k
    _delete_rows = nullptr;
848
60.0k
    _deletion_vector = nullptr;
849
60.0k
    _aggregate_pushdown_tried = false;
850
60.0k
    _remaining_table_level_count = -1;
851
60.0k
    _current_reader_reached_eof = false;
852
60.0k
    RETURN_IF_ERROR(_evaluate_partition_prune_conjuncts(options.partition_prune_conjuncts,
853
60.0k
                                                        &_current_split_pruned));
854
60.0k
    if (_current_split_pruned) {
855
277
        COUNTER_UPDATE(_profile.runtime_filter_partition_pruned_range_counter, 1);
856
277
        return Status::OK();
857
277
    }
858
59.8k
    _current_task = std::make_unique<ScanTask>();
859
59.8k
    _current_task->data_file = create_file_description(options.current_range);
860
59.8k
    _current_file_description = *_current_task->data_file;
861
    // A table-level row count is only equivalent to scanning the split when no row predicate is
862
    // active and no predicate can arrive later. The metadata path can return several batches for
863
    // one split; after its first synthetic batch there is no way to recover the real rows if a
864
    // runtime filter arrives before the next scheduler turn.
865
59.8k
    if (_push_down_agg_type == TPushAggOp::type::COUNT && options.all_runtime_filters_applied &&
866
59.8k
        _conjuncts.empty() && options.current_range.__isset.table_format_params &&
867
59.8k
        options.current_range.table_format_params.__isset.table_level_row_count) {
868
2.09k
        DORIS_CHECK(options.current_range.table_format_params.table_level_row_count >= -1);
869
2.09k
        _remaining_table_level_count =
870
2.09k
                options.current_range.table_format_params.table_level_row_count;
871
2.09k
    }
872
59.8k
    if (_is_table_level_count_active()) {
873
148
        return Status::OK();
874
148
    }
875
59.6k
    return _parse_delete_predicates(options);
876
59.8k
}
877
878
Status TableReader::_evaluate_partition_prune_conjuncts(const VExprContextSPtrs& conjuncts,
879
60.0k
                                                        bool* can_filter_all) {
880
60.0k
    DORIS_CHECK(can_filter_all != nullptr);
881
60.0k
    SCOPED_TIMER(_profile.runtime_filter_partition_prune_timer);
882
60.0k
    *can_filter_all = false;
883
60.0k
    if (conjuncts.empty() || _partition_values.empty()) {
884
56.9k
        return Status::OK();
885
56.9k
    }
886
887
3.12k
    VExprContextSPtrs partition_conjuncts;
888
4.37k
    for (const auto& conjunct : conjuncts) {
889
4.37k
        DORIS_CHECK(conjunct != nullptr);
890
4.37k
        DORIS_CHECK(conjunct->root() != nullptr);
891
        // Keep only the safe prefix of the original conjunct order. If an unsafe conjunct is
892
        // skipped, a later predicate could prune the split before the unsafe one reaches its
893
        // normal row-level evaluation point.
894
4.37k
        if (!_is_safe_to_pre_execute(conjunct)) {
895
1
            break;
896
1
        }
897
4.37k
        std::set<GlobalIndex> global_indices;
898
4.37k
        collect_global_indices(conjunct->root(), &global_indices);
899
4.37k
        if (global_indices.empty()) {
900
0
            continue;
901
0
        }
902
4.43k
        const bool partition_only = std::ranges::all_of(global_indices, [&](GlobalIndex index) {
903
4.43k
            if (index.value() >= _projected_columns.size()) {
904
0
                return false;
905
0
            }
906
4.43k
            const auto& column = _projected_columns[index.value()];
907
4.43k
            return column.is_partition_key &&
908
4.43k
                   find_partition_value(column, _partition_values) != nullptr;
909
4.43k
        });
910
4.37k
        if (partition_only) {
911
2.75k
            partition_conjuncts.push_back(conjunct);
912
2.75k
        }
913
4.37k
    }
914
3.12k
    if (partition_conjuncts.empty()) {
915
1.17k
        return Status::OK();
916
1.17k
    }
917
918
1.94k
    Block block;
919
1.94k
    RETURN_IF_ERROR(_build_partition_prune_block(&block));
920
1.94k
    RowDescriptor row_desc;
921
2.75k
    for (const auto& conjunct : partition_conjuncts) {
922
2.75k
        RETURN_IF_ERROR(conjunct->prepare(_runtime_state, row_desc));
923
2.75k
        RETURN_IF_ERROR(conjunct->open(_runtime_state));
924
2.75k
    }
925
1.94k
    IColumn::Filter result_filter(block.rows(), 1);
926
1.94k
    return VExprContext::execute_conjuncts(partition_conjuncts, nullptr, &block, &result_filter,
927
1.94k
                                           can_filter_all);
928
1.94k
}
929
930
84.0k
bool TableReader::_is_safe_to_pre_execute(const VExprContextSPtr& conjunct) {
931
84.0k
    DORIS_CHECK(conjunct != nullptr);
932
84.0k
    DORIS_CHECK(conjunct->root() != nullptr);
933
84.0k
    const auto root = conjunct->root();
934
84.0k
    const auto impl = root->get_impl();
935
84.0k
    const auto predicate = impl != nullptr ? impl : root;
936
    // Split pruning evaluates a predicate once before any file rows are read. Reordering
937
    // non-deterministic or error-preserving expressions can change their row-level semantics,
938
    // even when every referenced slot is a partition column or maps to a constant entry.
939
84.0k
    return predicate->is_safe_to_execute_on_selected_rows();
940
84.0k
}
941
942
1.96k
Status TableReader::_build_partition_prune_block(Block* block) const {
943
1.96k
    DORIS_CHECK(block != nullptr);
944
1.96k
    DORIS_CHECK(!_projected_columns.empty());
945
1.96k
    block->clear();
946
5.63k
    for (const auto& column : _projected_columns) {
947
5.63k
        DORIS_CHECK(column.type != nullptr);
948
5.63k
        ColumnPtr value_column = column.type->create_column_const_with_default_value(1);
949
5.63k
        if (column.is_partition_key) {
950
2.78k
            const auto* partition_value = find_partition_value(column, _partition_values);
951
2.78k
            if (partition_value != nullptr) {
952
2.78k
                value_column = column.type->create_column_const(1, *partition_value);
953
2.78k
            }
954
2.78k
        }
955
5.63k
        block->insert({std::move(value_column), column.type, column.name});
956
5.63k
    }
957
1.96k
    return Status::OK();
958
1.96k
}
959
960
59.5k
Status TableReader::_parse_delete_predicates(const SplitReadOptions& options) {
961
59.5k
    DeleteFileDesc desc {.fs_name = options.current_range.fs_name};
962
59.5k
    bool has_delete_file = false;
963
59.5k
    RETURN_IF_ERROR(_parse_deletion_vector_file(options.current_range.table_format_params, &desc,
964
59.5k
                                                &has_delete_file));
965
59.5k
    if (has_delete_file) {
966
3.63k
        DORIS_CHECK(options.cache != nullptr);
967
3.63k
        Status create_status = Status::OK();
968
969
3.63k
        bool decoded_cache_hit = false;
970
3.63k
        _deletion_vector = options.cache->get<DeletionVector>(
971
3.63k
                desc.key,
972
3.63k
                [&]() -> DeletionVector* {
973
1.44k
                    auto deletion_vector = std::make_unique<DeletionVector>();
974
975
1.44k
                    DeletionVectorReader dv_reader(_runtime_state, _scanner_profile, *_scan_params,
976
1.44k
                                                   desc, _io_ctx.get());
977
1.44k
                    create_status = dv_reader.open();
978
1.44k
                    if (!create_status.ok()) [[unlikely]] {
979
0
                        return nullptr;
980
0
                    }
981
982
1.44k
                    size_t bytes_read = desc.size;
983
1.44k
                    std::vector<char> buffer(bytes_read);
984
1.44k
                    DBUG_EXECUTE_IF("TableReader.parse_deletion_vector.io_error", {
985
1.44k
                        create_status =
986
1.44k
                                Status::IOError("injected format v2 deletion vector read failure");
987
1.44k
                        return nullptr;
988
1.44k
                    });
989
1.43k
                    DBUG_EXECUTE_IF("TableReader.parse_deletion_vector.should_stop", {
990
1.43k
                        create_status = Status::EndOfFile("stop read.");
991
1.43k
                        return nullptr;
992
1.43k
                    });
993
1.43k
                    create_status =
994
1.43k
                            dv_reader.read_at(desc.start_offset, {buffer.data(), bytes_read});
995
1.43k
                    const auto& file_cache_stats = dv_reader.file_cache_statistics();
996
1.43k
                    COUNTER_UPDATE(_profile.dv_file_cache_hit_count,
997
1.43k
                                   file_cache_stats.num_local_io_total);
998
1.43k
                    COUNTER_UPDATE(_profile.dv_file_cache_miss_count,
999
1.43k
                                   file_cache_stats.num_remote_io_total);
1000
1.43k
                    COUNTER_UPDATE(_profile.dv_file_cache_peer_read_count,
1001
1.43k
                                   file_cache_stats.num_peer_io_total);
1002
1.43k
                    if (!create_status.ok()) [[unlikely]] {
1003
0
                        return nullptr;
1004
0
                    }
1005
1006
1.43k
                    const char* buf = buffer.data();
1007
1.43k
                    SCOPED_TIMER(_profile.parse_delete_file_time);
1008
1.43k
                    create_status = parse_deletion_vector(buf, bytes_read, desc.format,
1009
1.43k
                                                          deletion_vector.get());
1010
1.43k
                    if (!create_status.ok()) [[unlikely]] {
1011
1
                        return nullptr;
1012
1
                    }
1013
1.43k
                    COUNTER_UPDATE(_profile.num_delete_rows, deletion_vector->cardinality());
1014
1.43k
                    return deletion_vector.release();
1015
1.43k
                },
1016
3.63k
                &decoded_cache_hit);
1017
3.63k
        RETURN_IF_ERROR(create_status);
1018
3.62k
        COUNTER_UPDATE(decoded_cache_hit ? _profile.decoded_dv_cache_hit_count
1019
3.62k
                                         : _profile.decoded_dv_cache_miss_count,
1020
3.62k
                       1);
1021
3.62k
    }
1022
1023
59.5k
    return Status::OK();
1024
59.5k
}
1025
} // namespace doris::format