Coverage Report

Created: 2026-08-01 18:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/format/table/equality_delete.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/table/equality_delete.h"
19
20
#include "core/column/column_nullable.h"
21
#include "exprs/create_predicate_function.h"
22
23
namespace doris {
24
25
std::unique_ptr<EqualityDeleteBase> EqualityDeleteBase::get_delete_impl(
26
88
        const Block* delete_block, const std::vector<int>& delete_col_ids) {
27
88
    DCHECK_EQ(delete_block->columns(), delete_col_ids.size());
28
88
    if (delete_block->columns() == 1) {
29
88
        return std::make_unique<SimpleEqualityDelete>(delete_block, delete_col_ids);
30
88
    } else {
31
0
        return std::make_unique<MultiEqualityDelete>(delete_block, delete_col_ids);
32
0
    }
33
88
}
34
35
88
Status SimpleEqualityDelete::_build_set() {
36
88
    COUNTER_UPDATE(num_delete_rows, _delete_block->rows());
37
88
    if (_delete_block->columns() != 1) [[unlikely]] {
38
0
        return Status::InternalError("Simple equality delete can be only applied with one column");
39
0
    }
40
88
    auto& column_and_type = _delete_block->get_by_position(0);
41
88
    auto delete_column_type = remove_nullable(column_and_type.type)->get_primitive_type();
42
88
    size_t non_null_rows = _delete_block->rows();
43
88
    if (const auto* nullable = check_and_get_column<ColumnNullable>(column_and_type.column.get());
44
88
        nullable != nullptr) {
45
88
        non_null_rows = std::ranges::count(nullable->get_null_map_data(), UInt8(0));
46
88
    }
47
88
    _hybrid_set.reset(create_set(delete_column_type, non_null_rows, true));
48
88
    _hybrid_set->insert_fixed_len(column_and_type.column, 0);
49
88
    return Status::OK();
50
88
}
51
52
Status SimpleEqualityDelete::filter_data_block(
53
        Block* data_block, const std::unordered_map<std::string, uint32_t>* col_name_to_block_idx,
54
        const std::unordered_map<int, std::string>& id_to_block_column_name,
55
116
        IColumn::Filter& filter) {
56
116
    SCOPED_TIMER(equality_delete_time);
57
116
    DCHECK(_delete_col_ids.size() == 1);
58
116
    auto column_field_id = _delete_col_ids[0];
59
60
116
    const auto& block_col_name = id_to_block_column_name.at(column_field_id);
61
116
    auto block_idx = col_name_to_block_idx->at(block_col_name);
62
63
116
    auto column_and_type = data_block->get_by_position(block_idx);
64
65
116
    size_t rows = data_block->rows();
66
    //     _filter: 1 => in _hybrid_set; 0 => not in _hybrid_set
67
116
    if (_single_filter == nullptr) {
68
88
        _single_filter = std::make_unique<IColumn::Filter>(rows, 0);
69
88
    } else {
70
        // reset the array capacity and fill all elements using the 0
71
28
        _single_filter->assign(rows, UInt8(0));
72
28
    }
73
116
    auto column = column_and_type.column->convert_to_full_column_if_const();
74
116
    if (const auto* nullable = check_and_get_column<ColumnNullable>(column.get())) {
75
116
        const NullMap& null_map = nullable->get_null_map_data();
76
116
        _hybrid_set->find_batch_nullable(*remove_nullable(column), rows, null_map, *_single_filter);
77
116
        if (_hybrid_set->contain_null()) {
78
4
            auto* filter_data = _single_filter->data();
79
16
            for (size_t i = 0; i < rows; ++i) {
80
12
                filter_data[i] = filter_data[i] || null_map[i];
81
12
            }
82
4
        }
83
116
    } else {
84
0
        _hybrid_set->find_batch(*column, rows, *_single_filter);
85
0
    }
86
    // should reverse _filter
87
116
    auto* filter_data = filter.data();
88
348
    for (size_t i = 0; i < rows; ++i) {
89
232
        filter_data[i] &= !_single_filter->data()[i];
90
232
    }
91
116
    return Status::OK();
92
116
}
93
94
2
Status MultiEqualityDelete::_build_set() {
95
2
    COUNTER_UPDATE(num_delete_rows, _delete_block->rows());
96
2
    size_t rows = _delete_block->rows();
97
2
    _delete_hashes.clear();
98
2
    _delete_hashes.resize(rows, 0);
99
3
    for (ColumnPtr column : _delete_block->get_columns()) {
100
3
        column->update_hashes_with_value(_delete_hashes.data(), nullptr);
101
3
    }
102
4
    for (size_t i = 0; i < rows; ++i) {
103
2
        _delete_hash_map.insert({_delete_hashes[i], i});
104
2
    }
105
2
    _data_column_index.resize(_delete_block->columns());
106
2
    return Status::OK();
107
2
}
108
109
Status MultiEqualityDelete::filter_data_block(
110
        Block* data_block, const std::unordered_map<std::string, uint32_t>* col_name_to_block_idx,
111
        const std::unordered_map<int, std::string>& id_to_block_column_name,
112
2
        IColumn::Filter& filter) {
113
2
    SCOPED_TIMER(equality_delete_time);
114
2
    DCHECK_EQ(_delete_block->get_columns_with_type_and_name().size(), _delete_col_ids.size());
115
2
    size_t column_index = 0;
116
117
5
    for (size_t idx = 0; idx < _delete_block->get_columns_with_type_and_name().size(); ++idx) {
118
3
        auto delete_col = _delete_block->get_columns_with_type_and_name()[idx];
119
3
        auto delete_col_id = _delete_col_ids[idx];
120
121
3
        DCHECK(id_to_block_column_name.contains(delete_col_id));
122
3
        const auto& block_column_name = id_to_block_column_name.at(delete_col_id);
123
3
        if (!col_name_to_block_idx->contains(block_column_name)) [[unlikely]] {
124
0
            return Status::InternalError("Column '{}' not found in data block: {}",
125
0
                                         block_column_name, data_block->dump_structure());
126
0
        }
127
3
        auto column_and_type =
128
3
                data_block->safe_get_by_position(col_name_to_block_idx->at(block_column_name));
129
3
        if (!delete_col.type->equals(*column_and_type.type)) [[unlikely]] {
130
0
            return Status::InternalError(
131
0
                    "Not support type change in column '{}', src type: {}, target type: {}",
132
0
                    block_column_name, delete_col.type->get_name(),
133
0
                    column_and_type.type->get_name());
134
0
        }
135
3
        _data_column_index[column_index++] = col_name_to_block_idx->at(block_column_name);
136
3
    }
137
2
    size_t rows = data_block->rows();
138
2
    _data_hashes.clear();
139
2
    _data_hashes.resize(rows, 0);
140
3
    for (size_t index : _data_column_index) {
141
3
        data_block->get_by_position(index).column->update_hashes_with_value(_data_hashes.data(),
142
3
                                                                            nullptr);
143
3
    }
144
2
    auto* filter_data = filter.data();
145
7
    for (size_t i = 0; i < rows; ++i) {
146
5
        for (auto beg = _delete_hash_map.lower_bound(_data_hashes[i]),
147
5
                  end = _delete_hash_map.upper_bound(_data_hashes[i]);
148
5
             beg != end; ++beg) {
149
3
            if (filter[i] && _equal(data_block, i, beg->second)) {
150
3
                filter_data[i] = 0;
151
3
                break;
152
3
            }
153
3
        }
154
5
    }
155
156
2
    return Status::OK();
157
2
}
158
159
bool MultiEqualityDelete::_equal(Block* data_block, size_t data_row_index,
160
3
                                 size_t delete_row_index) {
161
7
    for (size_t i = 0; i < _delete_block->columns(); ++i) {
162
4
        ColumnPtr data_col = data_block->get_by_position(_data_column_index[i]).column;
163
4
        ColumnPtr delete_col = _delete_block->get_by_position(i).column;
164
4
        if (data_col->compare_at(data_row_index, delete_row_index, *delete_col, -1) != 0) {
165
0
            return false;
166
0
        }
167
4
    }
168
3
    return true;
169
3
}
170
171
} // namespace doris