Coverage Report

Created: 2026-10-30 17:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/format/table/equality_delete.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
#include "core/block/block.h"
19
#include "exprs/hybrid_set.h"
20
#include "runtime/runtime_profile.h"
21
22
namespace doris::schema::external {
23
class TField;
24
} // namespace doris::schema::external
25
26
namespace doris {
27
28
// The type an equality-delete key is compared in: the field's type in the scan schema (`field`, which
29
// is null when the plan carries no schema), as in the V2 reader. Data files are read in that type, so
30
// an equality delete written before a type promotion is widened to it rather than the data narrowed
31
// to the delete file's type. Example: `id` promoted from INT to BIGINT, a data file written after the
32
// promotion holds 4294967296, and a delete file written before it holds the NULL key. Narrowed to
33
// INT, 4294967296 became NULL and was deleted by that NULL key; widened, the delete key is compared
34
// as BIGINT and deletes only rows whose id is NULL. STRING / VARBINARY keys keep `delete_key_type`:
35
// they match by their bytes whatever the two sides were read as.
36
DataTypePtr equality_delete_key_type(const schema::external::TField* field,
37
                                     const DataTypePtr& delete_key_type);
38
39
// Iceberg BINARY / FIXED keys are read as VARBINARY and string keys as STRING, and the delete file and
40
// the data file may carry the same key in different ones of these classes (a delete block holding
41
// STRING bytes for a VARBINARY data column, say). Keys of these types therefore match by their bytes.
42
bool is_equality_delete_byte_type(const DataTypePtr& type);
43
44
// Widens an equality-delete key read in `column_type` to `key_type`, the type the key is compared in.
45
// A file written before an Iceberg type promotion holds the key in the old type: the INT key of a
46
// column since promoted to BIGINT becomes BIGINT here. Iceberg's promotions (INT to BIGINT, FLOAT to
47
// DOUBLE, a wider DECIMAL precision) only widen, so every value is kept. Returns `column` itself when
48
// the types are equal.
49
Status widen_equality_delete_key(ColumnPtr column, const DataTypePtr& column_type,
50
                                 const DataTypePtr& key_type, ColumnPtr* result);
51
52
/**
53
 * Support Iceberg equality delete.
54
 * If there's only one delete column in delete file, use `SimpleEqualityDelete`,
55
 * which uses optimized `HybridSetBase` to build the hash set.
56
 * If there are more delete columns in delete file, use `MultiEqualityDelete`,
57
 * which generates a hash column from all delete columns, and only compare the values
58
 * when the hash values are the same.
59
 */
60
class EqualityDeleteBase {
61
protected:
62
    RuntimeProfile::Counter* num_delete_rows;
63
    RuntimeProfile::Counter* build_set_time;
64
    RuntimeProfile::Counter* equality_delete_time;
65
66
    const Block* _delete_block;
67
    std::vector<int> _delete_col_ids;
68
69
    virtual Status _build_set() = 0;
70
71
public:
72
    EqualityDeleteBase(const Block* delete_block, const std::vector<int> delete_col_ids)
73
63
            : _delete_block(delete_block), _delete_col_ids(delete_col_ids) {}
74
63
    virtual ~EqualityDeleteBase() = default;
75
76
63
    Status init(RuntimeProfile* profile) {
77
63
        static const char* delete_profile = "EqualityDelete";
78
63
        ADD_TIMER_WITH_LEVEL(profile, delete_profile, 1);
79
63
        num_delete_rows = ADD_CHILD_COUNTER_WITH_LEVEL(profile, "NumRowsInDeleteFile", TUnit::UNIT,
80
63
                                                       delete_profile, 1);
81
63
        build_set_time = ADD_CHILD_TIMER_WITH_LEVEL(profile, "BuildHashSetTime", delete_profile, 1);
82
63
        equality_delete_time =
83
63
                ADD_CHILD_TIMER_WITH_LEVEL(profile, "EqualityDeleteFilterTime", delete_profile, 1);
84
63
        SCOPED_TIMER(build_set_time);
85
63
        return _build_set();
86
63
    }
87
88
    virtual Status filter_data_block(
89
            Block* data_block,
90
            const std::unordered_map<std::string, uint32_t>* col_name_to_block_idx,
91
            const std::unordered_map<int, std::string>& id_to_block_column_name,
92
            IColumn::Filter& filter) = 0;
93
94
    static std::unique_ptr<EqualityDeleteBase> get_delete_impl(
95
            const Block* delete_block, const std::vector<int>& delete_col_ids);
96
};
97
98
class SimpleEqualityDelete : public EqualityDeleteBase {
99
protected:
100
    std::shared_ptr<HybridSetBase> _hybrid_set;
101
    std::unique_ptr<IColumn::Filter> _single_filter;
102
103
    Status _build_set() override;
104
105
public:
106
    SimpleEqualityDelete(const Block* delete_block, const std::vector<int>& delete_col_ids)
107
50
            : EqualityDeleteBase(delete_block, delete_col_ids) {}
108
109
    Status filter_data_block(Block* data_block,
110
                             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
                             IColumn::Filter& filter) override;
113
};
114
115
/**
116
 * `MultiEqualityDelete` will generate the hash column for delete block and data block.
117
 */
118
class MultiEqualityDelete : public EqualityDeleteBase {
119
protected:
120
    // hash column for delete block
121
    std::vector<uint64_t> _delete_hashes;
122
    // hash column for data block
123
    std::vector<uint64_t> _data_hashes;
124
    // hash code => row index
125
    // if hash values are equal, then compare the real values
126
    // the row index records the row number of the delete row in delete block
127
    std::multimap<uint64_t, size_t> _delete_hash_map;
128
    // the delete column indexes in data block
129
    std::vector<size_t> _data_column_index;
130
131
    Status _build_set() override;
132
133
    bool _equal(Block* data_block, size_t data_row_index, size_t delete_row_index);
134
135
public:
136
    MultiEqualityDelete(const Block* delete_block, const std::vector<int>& delete_col_ids)
137
13
            : EqualityDeleteBase(delete_block, delete_col_ids) {}
138
139
    Status filter_data_block(Block* data_block,
140
                             const std::unordered_map<std::string, uint32_t>* col_name_to_block_idx,
141
                             const std::unordered_map<int, std::string>& id_to_block_column_name,
142
                             IColumn::Filter& filter) override;
143
};
144
145
} // namespace doris