Coverage Report

Created: 2026-09-17 13:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/storage/partial_update_info.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
#pragma once
19
#include <gen_cpp/olap_file.pb.h>
20
21
#include <cstdint>
22
#include <functional>
23
#include <map>
24
#include <memory>
25
#include <set>
26
#include <string>
27
#include <vector>
28
29
#include "common/status.h"
30
#include "core/column/column.h"
31
#include "core/data_type/primitive_type.h"
32
#include "storage/rowset/rowset_fwd.h"
33
#include "storage/tablet/tablet_fwd.h"
34
35
namespace doris {
36
class TabletSchema;
37
class PartialUpdateInfoPB;
38
class BitmapValue;
39
struct RowLocation;
40
class Block;
41
class MutableBlock;
42
class IOlapColumnDataAccessor;
43
namespace segment_v2 {
44
struct HistoricalRowRetrieverContext;
45
}
46
47
struct RowsetWriterContext;
48
struct RowsetId;
49
class BitmapValue;
50
class HistoricalRowFetcher;
51
class OlapBlockDataConvertor;
52
class RowKeyEncoder;
53
struct MowContext;
54
namespace segment_v2 {
55
class MowKeyProbe;
56
}
57
58
class SegmentCacheHandle;
59
60
struct PartialUpdateInfo {
61
    Status init(int64_t tablet_id, int64_t txn_id, const TabletSchema& tablet_schema,
62
                UniqueKeyUpdateModePB unique_key_update_mode, PartialUpdateNewRowPolicyPB policy,
63
                const std::set<std::string>& partial_update_cols, bool is_strict_mode,
64
                int64_t timestamp_ms, int32_t nano_seconds, const std::string& timezone,
65
                const std::string& auto_increment_column, int32_t sequence_map_col_uid = -1,
66
                int64_t cur_max_version = -1);
67
    void to_pb(PartialUpdateInfoPB* partial_update_info) const;
68
    void from_pb(PartialUpdateInfoPB* partial_update_info);
69
    Status handle_new_key(const TabletSchema& tablet_schema,
70
                          const std::function<std::string()>& line,
71
                          BitmapValue* skip_bitmap = nullptr);
72
    std::string summary() const;
73
74
7.06k
    std::string partial_update_mode_str() const {
75
7.06k
        switch (partial_update_mode) {
76
1.76k
        case UniqueKeyUpdateModePB::UPSERT:
77
1.76k
            return "upsert";
78
4.83k
        case UniqueKeyUpdateModePB::UPDATE_FIXED_COLUMNS:
79
4.83k
            return "partial update";
80
533
        case UniqueKeyUpdateModePB::UPDATE_FLEXIBLE_COLUMNS:
81
533
            return "flexible partial update";
82
7.06k
        }
83
0
        return "";
84
7.06k
    }
85
638k
    bool is_partial_update() const { return partial_update_mode != UniqueKeyUpdateModePB::UPSERT; }
86
182k
    bool is_fixed_partial_update() const {
87
182k
        return partial_update_mode == UniqueKeyUpdateModePB::UPDATE_FIXED_COLUMNS;
88
182k
    }
89
26.3k
    bool is_flexible_partial_update() const {
90
26.3k
        return partial_update_mode == UniqueKeyUpdateModePB::UPDATE_FLEXIBLE_COLUMNS;
91
26.3k
    }
92
176k
    UniqueKeyUpdateModePB update_mode() const { return partial_update_mode; }
93
8
    int32_t sequence_map_col_uid() const { return sequence_map_col_unqiue_id; }
94
95
private:
96
    void _generate_default_values_for_missing_cids(const TabletSchema& tablet_schema);
97
98
public:
99
    UniqueKeyUpdateModePB partial_update_mode {UniqueKeyUpdateModePB::UPSERT};
100
    PartialUpdateNewRowPolicyPB partial_update_new_key_policy {PartialUpdateNewRowPolicyPB::APPEND};
101
    int64_t max_version_in_flush_phase {-1};
102
    std::set<std::string> partial_update_input_columns;
103
    std::vector<uint32_t> missing_cids;
104
    std::vector<uint32_t> update_cids;
105
    // if key not exist in old rowset, use default value or null value for the unmentioned cols
106
    // to generate a new row, only available in non-strict mode
107
    bool can_insert_new_rows_in_partial_update {true};
108
    bool is_strict_mode {false};
109
    int64_t timestamp_ms {0};
110
    int32_t nano_seconds {0};
111
    std::string timezone;
112
    bool is_input_columns_contains_auto_inc_column = false;
113
    bool is_schema_contains_auto_inc_column = false;
114
115
    // default values for missing cids
116
    std::vector<std::string> default_values;
117
118
    int32_t sequence_map_col_unqiue_id {-1};
119
};
120
121
// used in mow partial update
122
struct RidAndPos {
123
    uint32_t rid;
124
    // pos in block
125
    size_t pos;
126
};
127
128
class FixedReadPlan {
129
public:
130
    enum class ReadStrategy {
131
        // Use the full row-store column when available; otherwise read physical columns.
132
        PREFER_ROW_STORE,
133
        // Read only the requested physical columns, even when a full row-store column exists.
134
        COLUMN_STORE,
135
    };
136
137
    bool empty() const;
138
0
    void clear() { plan.clear(); }
139
    void prepare_to_read(const RowLocation& row_location, size_t pos);
140
    Status read_columns_by_plan(const TabletSchema& tablet_schema,
141
                                std::vector<uint32_t> cids_to_read,
142
                                const std::map<RowsetId, RowsetSharedPtr>& rsid_to_rowset,
143
                                Block& block, std::map<uint32_t, uint32_t>* read_index,
144
                                ReadStrategy read_strategy, bool force_read_old_delete_signs,
145
                                const signed char* __restrict cur_delete_signs = nullptr) const;
146
    Status fill_missing_columns(const segment_v2::HistoricalRowRetrieverContext& historical_context,
147
                                const std::map<RowsetId, RowsetSharedPtr>& rsid_to_rowset,
148
                                const TabletSchema& tablet_schema, Block& full_block,
149
                                const std::vector<bool>& use_default_or_null_flag,
150
                                bool has_default_or_nullable, uint32_t segment_start_pos,
151
                                const Block* block,
152
                                std::vector<signed char>* old_delete_signs = nullptr) const;
153
    Status fill_old_delete_signs(const Block& old_value_block,
154
                                 const std::map<uint32_t, uint32_t>& read_index, size_t num_rows,
155
                                 std::vector<signed char>* old_delete_signs) const;
156
157
private:
158
    std::map<RowsetId, std::map<uint32_t /* segment_id */, std::vector<RidAndPos>>> plan;
159
};
160
161
class FlexibleReadPlan {
162
public:
163
2.37k
    FlexibleReadPlan(bool has_row_store_for_column) : use_row_store(has_row_store_for_column) {}
164
    void prepare_to_read(const RowLocation& row_location, size_t pos,
165
                         const BitmapValue& skip_bitmap);
166
    // for column store
167
    Status read_columns_by_plan(const TabletSchema& tablet_schema,
168
                                const std::map<RowsetId, RowsetSharedPtr>& rsid_to_rowset,
169
                                Block& old_value_block,
170
                                std::map<uint32_t, std::map<uint32_t, uint32_t>>* read_index) const;
171
172
    // for row_store
173
    Status read_columns_by_plan(const TabletSchema& tablet_schema,
174
                                const std::vector<uint32_t>& cids_to_read,
175
                                const std::map<RowsetId, RowsetSharedPtr>& rsid_to_rowset,
176
                                Block& old_value_block,
177
                                std::map<uint32_t, uint32_t>* read_index) const;
178
    Status fill_non_primary_key_columns(
179
            const segment_v2::HistoricalRowRetrieverContext& historical_context,
180
            const std::map<RowsetId, RowsetSharedPtr>& rsid_to_rowset,
181
            const TabletSchema& tablet_schema, Block& full_block,
182
            const std::vector<bool>& use_default_or_null_flag, bool has_default_or_nullable,
183
            uint32_t segment_start_pos, uint32_t block_start_pos, const Block* block,
184
            std::vector<BitmapValue>* skip_bitmaps) const;
185
186
    Status fill_non_primary_key_columns_for_column_store(
187
            const segment_v2::HistoricalRowRetrieverContext& historical_context,
188
            const std::map<RowsetId, RowsetSharedPtr>& rsid_to_rowset,
189
            const TabletSchema& tablet_schema, const std::vector<uint32_t>& non_sort_key_cids,
190
            Block& old_value_block, MutableColumns& mutable_full_columns,
191
            const std::vector<bool>& use_default_or_null_flag, bool has_default_or_nullable,
192
            uint32_t segment_start_pos, uint32_t block_start_pos, const Block* block,
193
            std::vector<BitmapValue>* skip_bitmaps) const;
194
    Status fill_non_primary_key_columns_for_row_store(
195
            const segment_v2::HistoricalRowRetrieverContext& historical_context,
196
            const std::map<RowsetId, RowsetSharedPtr>& rsid_to_rowset,
197
            const TabletSchema& tablet_schema, const std::vector<uint32_t>& non_sort_key_cids,
198
            Block& old_value_block, MutableColumns& mutable_full_columns,
199
            const std::vector<bool>& use_default_or_null_flag, bool has_default_or_nullable,
200
            uint32_t segment_start_pos, uint32_t block_start_pos, const Block* block,
201
            std::vector<BitmapValue>* skip_bitmaps) const;
202
203
private:
204
    bool use_row_store {false};
205
    // rowset_id -> segment_id -> column unique id -> mappings
206
    std::map<RowsetId, std::map<uint32_t, std::map<uint32_t, std::vector<RidAndPos>>>> plan;
207
    std::map<RowsetId, std::map<uint32_t /* segment_id */, std::vector<RidAndPos>>> row_store_plan;
208
};
209
210
ColumnBitmap* get_mutable_skip_bitmap_column(Block* block, size_t skip_bitmap_col_idx);
211
212
class BlockAggregator {
213
public:
214
    ~BlockAggregator();
215
    // All references must live longer than the aggregator; the flexible fill
216
    // stage builds everything as locals in one apply() scope. The aggregator
217
    // owns its block convertor (key + sequence column slots).
218
    BlockAggregator(TabletSchema& tablet_schema, BaseTabletSPtr tablet,
219
                    std::shared_ptr<MowContext> mow_context,
220
                    const PartialUpdateInfo& partial_update_info, const RowKeyEncoder& key_encoder,
221
                    const segment_v2::MowKeyProbe& probe, HistoricalRowFetcher& fetcher);
222
223
    Status convert_pk_columns(Block* block, size_t row_pos, size_t num_rows,
224
                              std::vector<IOlapColumnDataAccessor*>& key_columns);
225
    Status convert_seq_column(Block* block, size_t row_pos, size_t num_rows,
226
                              IOlapColumnDataAccessor*& seq_column);
227
    // Optional sidecars are aggregated and filtered with `block`; on return each element is
228
    // aligned with the corresponding row in the final block. A non-zero
229
    // `insert_after_delete_flags` entry marks the surviving INSERT of a same-batch
230
    // DELETE-then-INSERT pair.
231
    Status aggregate_for_flexible_partial_update(
232
            Block* block, size_t num_rows, const std::vector<RowsetSharedPtr>& specified_rowsets,
233
            std::vector<std::unique_ptr<SegmentCacheHandle>>& segment_caches,
234
            std::vector<int64_t>* row_lsns = nullptr,
235
            std::vector<uint8_t>* insert_after_delete_flags = nullptr);
236
237
private:
238
    Status aggregate_for_sequence_column(
239
            Block* block, int num_rows, const std::vector<IOlapColumnDataAccessor*>& key_columns,
240
            IOlapColumnDataAccessor* seq_column,
241
            const std::vector<RowsetSharedPtr>& specified_rowsets,
242
            std::vector<std::unique_ptr<SegmentCacheHandle>>& segment_caches,
243
            std::vector<int64_t>* row_lsns);
244
    Status aggregate_for_insert_after_delete(
245
            Block* block, size_t num_rows, const std::vector<IOlapColumnDataAccessor*>& key_columns,
246
            const std::vector<RowsetSharedPtr>& specified_rowsets,
247
            std::vector<std::unique_ptr<SegmentCacheHandle>>& segment_caches,
248
            std::vector<int64_t>* row_lsns, std::vector<uint8_t>* insert_after_delete_flags);
249
    Status filter_block(Block* block, size_t num_rows, MutableColumnPtr filter_column,
250
                        int duplicate_rows, std::string col_name);
251
252
    Status fill_sequence_column(Block* block, size_t num_rows, const FixedReadPlan& read_plan,
253
                                std::vector<BitmapValue>& skip_bitmaps);
254
255
    void append_or_merge_row(MutableBlock& dst_block, Block* src_block, int rid,
256
                             BitmapValue& skip_bitmap, bool have_delete_sign);
257
    void merge_one_row(MutableBlock& dst_block, Block* src_block, int rid,
258
                       BitmapValue& skip_bitmap);
259
    void append_one_row(MutableBlock& dst_block, Block* src_block, int rid);
260
    void remove_last_n_rows(MutableBlock& dst_block, int n);
261
262
    void append_row_lsn(int rid);
263
    void merge_row_lsn(int rid);
264
    void remove_last_row_lsns(int n);
265
266
    // aggregate rows with same keys in range [start, end) from block to output_block
267
    Status aggregate_rows(MutableBlock& output_block, Block* block, int start, int end,
268
                          std::string key, std::vector<BitmapValue>* skip_bitmaps,
269
                          const signed char* delete_signs, IOlapColumnDataAccessor* seq_column,
270
                          const std::vector<RowsetSharedPtr>& specified_rowsets,
271
                          std::vector<std::unique_ptr<SegmentCacheHandle>>& segment_caches);
272
273
    Status _generate_encoded_default_seq_value(std::string* encoded_value);
274
275
    TabletSchema& _tablet_schema;
276
    BaseTabletSPtr _tablet;
277
    std::shared_ptr<MowContext> _mow_context;
278
    const PartialUpdateInfo& _partial_update_info;
279
    const RowKeyEncoder& _key_encoder;
280
    std::unique_ptr<OlapBlockDataConvertor> _convertor;
281
    const segment_v2::MowKeyProbe& _probe;
282
    HistoricalRowFetcher& _fetcher;
283
284
    // Optional sidecar used by Row Binlog. Flexible aggregation can remove or merge input rows;
285
    // keep the persisted LSN aligned with the surviving row and retain the largest LSN of all
286
    // merged changes.
287
    const std::vector<int64_t>* _input_row_lsns = nullptr;
288
    std::vector<int64_t>* _output_row_lsns = nullptr;
289
290
    // used to store state when aggregating rows in block
291
    struct AggregateState {
292
        int rows {0};
293
        bool has_row_with_delete_sign {false};
294
295
315
        bool should_merge() const {
296
315
            return ((rows == 1 && !has_row_with_delete_sign) || rows == 2);
297
315
        }
298
299
123
        void reset() {
300
123
            rows = 0;
301
123
            has_row_with_delete_sign = false;
302
123
        }
303
304
0
        std::string to_string() const {
305
0
            return fmt::format("rows={}, have_delete_row={}", rows, has_row_with_delete_sign);
306
0
        }
307
    } _state {};
308
};
309
310
struct PartialUpdateStats {
311
    int64_t num_rows_updated {0};
312
    int64_t num_rows_new_added {0};
313
    int64_t num_rows_deleted {0};
314
    int64_t num_rows_filtered {0};
315
};
316
} // namespace doris