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 |