Coverage Report

Created: 2026-09-13 01:02

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