Coverage Report

Created: 2026-10-23 20:08

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
namespace segment_v2 {
44
struct HistoricalRowRetrieverContext;
45
}
46
47
struct RowsetWriterContext;
48
struct RowsetId;
49
class BitmapValue;
50
class HistoricalRowFetcher;
51
class RowKeyEncoder;
52
struct MowContext;
53
namespace segment_v2 {
54
class MowKeyProbe;
55
}
56
57
class SegmentCacheHandle;
58
59
struct PartialUpdateInfo {
60
    Status init(int64_t tablet_id, int64_t txn_id, const TabletSchema& tablet_schema,
61
                UniqueKeyUpdateModePB unique_key_update_mode, PartialUpdateNewRowPolicyPB policy,
62
                const std::set<std::string>& partial_update_cols, bool is_strict_mode,
63
                int64_t timestamp_ms, int32_t nano_seconds, const std::string& timezone,
64
                const std::string& auto_increment_column, int32_t sequence_map_col_uid = -1,
65
                int64_t cur_max_version = -1, const PUniqueId* load_id = nullptr);
66
    void to_pb(PartialUpdateInfoPB* partial_update_info) const;
67
    void from_pb(PartialUpdateInfoPB* partial_update_info);
68
    Status handle_new_key(const TabletSchema& tablet_schema,
69
                          const std::function<std::string()>& line,
70
                          BitmapValue* skip_bitmap = nullptr);
71
    std::string summary() const;
72
73
0
    std::string partial_update_mode_str() const {
74
0
        switch (partial_update_mode) {
75
0
        case UniqueKeyUpdateModePB::UPSERT:
76
0
            return "upsert";
77
0
        case UniqueKeyUpdateModePB::UPDATE_FIXED_COLUMNS:
78
0
            return "partial update";
79
0
        case UniqueKeyUpdateModePB::UPDATE_FLEXIBLE_COLUMNS:
80
0
            return "flexible partial update";
81
0
        }
82
0
        return "";
83
0
    }
84
392
    bool is_partial_update() const { return partial_update_mode != UniqueKeyUpdateModePB::UPSERT; }
85
234
    bool is_fixed_partial_update() const {
86
234
        return partial_update_mode == UniqueKeyUpdateModePB::UPDATE_FIXED_COLUMNS;
87
234
    }
88
77
    bool is_flexible_partial_update() const {
89
77
        return partial_update_mode == UniqueKeyUpdateModePB::UPDATE_FLEXIBLE_COLUMNS;
90
77
    }
91
32
    UniqueKeyUpdateModePB update_mode() const { return partial_update_mode; }
92
0
    int32_t sequence_map_col_uid() const { return sequence_map_col_unqiue_id; }
93
94
private:
95
    void _generate_default_values_for_missing_cids(const TabletSchema& tablet_schema);
96
97
public:
98
    UniqueKeyUpdateModePB partial_update_mode {UniqueKeyUpdateModePB::UPSERT};
99
    PartialUpdateNewRowPolicyPB partial_update_new_key_policy {PartialUpdateNewRowPolicyPB::APPEND};
100
    int64_t max_version_in_flush_phase {-1};
101
    std::set<std::string> partial_update_input_columns;
102
    std::vector<uint32_t> missing_cids;
103
    std::vector<uint32_t> update_cids;
104
    // if key not exist in old rowset, use default value or null value for the unmentioned cols
105
    // to generate a new row, only available in non-strict mode
106
    bool can_insert_new_rows_in_partial_update {true};
107
    bool is_strict_mode {false};
108
    // Shared by all replicas and persisted for publish/recovery. Never use process-local RNGs
109
    // when filling a missing cell after the input rows have been fanned out.
110
    bool has_load_id {false};
111
    uint64_t load_id_hi {0};
112
    uint64_t load_id_lo {0};
113
    int64_t timestamp_ms {0};
114
    int32_t nano_seconds {0};
115
    std::string timezone;
116
    bool is_input_columns_contains_auto_inc_column = false;
117
    bool is_schema_contains_auto_inc_column = false;
118
119
    // default values for missing cids
120
    std::vector<std::string> default_values;
121
122
    int32_t sequence_map_col_unqiue_id {-1};
123
};
124
125
// used in mow partial update
126
struct RidAndPos {
127
    uint32_t rid;
128
    // pos in block
129
    size_t pos;
130
};
131
132
class FixedReadPlan {
133
public:
134
    enum class ReadStrategy {
135
        // Use the full row-store column when available; otherwise read physical columns.
136
        PREFER_ROW_STORE,
137
        // Read only the requested physical columns, even when a full row-store column exists.
138
        COLUMN_STORE,
139
    };
140
141
    bool empty() const;
142
0
    void clear() { plan.clear(); }
143
    void prepare_to_read(const RowLocation& row_location, size_t pos);
144
    Status read_columns_by_plan(const TabletSchema& tablet_schema,
145
                                std::vector<uint32_t> cids_to_read,
146
                                const std::map<RowsetId, RowsetSharedPtr>& rsid_to_rowset,
147
                                Block& block, std::map<uint32_t, uint32_t>* read_index,
148
                                ReadStrategy read_strategy, bool force_read_old_delete_signs,
149
                                const signed char* __restrict cur_delete_signs = nullptr) const;
150
    Status fill_missing_columns(const segment_v2::HistoricalRowRetrieverContext& historical_context,
151
                                const std::map<RowsetId, RowsetSharedPtr>& rsid_to_rowset,
152
                                const TabletSchema& tablet_schema, Block& full_block,
153
                                const std::vector<bool>& use_default_or_null_flag,
154
                                bool has_default_or_nullable, uint32_t segment_start_pos,
155
                                const Block* block,
156
                                std::vector<signed char>* old_delete_signs = nullptr) const;
157
    Status fill_old_delete_signs(const Block& old_value_block,
158
                                 const std::map<uint32_t, uint32_t>& read_index, size_t num_rows,
159
                                 std::vector<signed char>* old_delete_signs) const;
160
161
private:
162
    std::map<RowsetId, std::map<uint32_t /* segment_id */, std::vector<RidAndPos>>> plan;
163
};
164
165
class FlexibleReadPlan {
166
public:
167
106
    FlexibleReadPlan(bool has_row_store_for_column) : use_row_store(has_row_store_for_column) {}
168
    void prepare_to_read(const RowLocation& row_location, size_t pos,
169
                         const BitmapValue& skip_bitmap);
170
    // for column store
171
    Status read_columns_by_plan(const TabletSchema& tablet_schema,
172
                                const std::map<RowsetId, RowsetSharedPtr>& rsid_to_rowset,
173
                                Block& old_value_block,
174
                                std::map<uint32_t, std::map<uint32_t, uint32_t>>* read_index) const;
175
176
    // for row_store
177
    Status read_columns_by_plan(const TabletSchema& tablet_schema,
178
                                const std::vector<uint32_t>& cids_to_read,
179
                                const std::map<RowsetId, RowsetSharedPtr>& rsid_to_rowset,
180
                                Block& old_value_block,
181
                                std::map<uint32_t, uint32_t>* read_index) const;
182
    Status fill_non_primary_key_columns(
183
            const segment_v2::HistoricalRowRetrieverContext& historical_context,
184
            const std::map<RowsetId, RowsetSharedPtr>& rsid_to_rowset,
185
            const TabletSchema& tablet_schema, Block& full_block,
186
            const std::vector<bool>& use_default_or_null_flag, bool has_default_or_nullable,
187
            uint32_t segment_start_pos, uint32_t block_start_pos, const Block* block,
188
            std::vector<BitmapValue>* skip_bitmaps) const;
189
190
    Status fill_non_primary_key_columns_for_column_store(
191
            const segment_v2::HistoricalRowRetrieverContext& historical_context,
192
            const std::map<RowsetId, RowsetSharedPtr>& rsid_to_rowset,
193
            const TabletSchema& tablet_schema, const std::vector<uint32_t>& non_sort_key_cids,
194
            Block& old_value_block, MutableColumns& mutable_full_columns,
195
            const std::vector<bool>& use_default_or_null_flag, bool has_default_or_nullable,
196
            uint32_t segment_start_pos, uint32_t block_start_pos, const Block* block,
197
            std::vector<BitmapValue>* skip_bitmaps) const;
198
    Status fill_non_primary_key_columns_for_row_store(
199
            const segment_v2::HistoricalRowRetrieverContext& historical_context,
200
            const std::map<RowsetId, RowsetSharedPtr>& rsid_to_rowset,
201
            const TabletSchema& tablet_schema, const std::vector<uint32_t>& non_sort_key_cids,
202
            Block& old_value_block, MutableColumns& mutable_full_columns,
203
            const std::vector<bool>& use_default_or_null_flag, bool has_default_or_nullable,
204
            uint32_t segment_start_pos, uint32_t block_start_pos, const Block* block,
205
            std::vector<BitmapValue>* skip_bitmaps) const;
206
207
private:
208
    bool use_row_store {false};
209
    // rowset_id -> segment_id -> column unique id -> mappings
210
    std::map<RowsetId, std::map<uint32_t, std::map<uint32_t, std::vector<RidAndPos>>>> plan;
211
    std::map<RowsetId, std::map<uint32_t /* segment_id */, std::vector<RidAndPos>>> row_store_plan;
212
};
213
214
ColumnBitmap* get_mutable_skip_bitmap_column(Block* block, size_t skip_bitmap_col_idx);
215
216
class BlockAggregator {
217
public:
218
    ~BlockAggregator();
219
    // All references must live longer than the aggregator; the flexible fill
220
    // stage builds everything as locals in one apply() scope.
221
    BlockAggregator(TabletSchema& tablet_schema, BaseTabletSPtr tablet,
222
                    std::shared_ptr<MowContext> mow_context,
223
                    const PartialUpdateInfo& partial_update_info, const RowKeyEncoder& key_encoder,
224
                    const segment_v2::MowKeyProbe& probe, HistoricalRowFetcher& fetcher);
225
226
    void collect_pk_columns(const Block& block, std::vector<const IColumn*>& key_columns) const;
227
    void collect_seq_column(const Block& block, const IColumn*& seq_column) const;
228
    // Optional sidecars are aggregated and filtered with `block`; on return each element is
229
    // aligned with the corresponding row in the final block. A non-zero
230
    // `insert_after_delete_flags` entry marks the surviving INSERT of a same-batch
231
    // DELETE-then-INSERT pair.
232
    Status aggregate_for_flexible_partial_update(
233
            Block* block, size_t num_rows, const std::vector<RowsetSharedPtr>& specified_rowsets,
234
            std::vector<std::unique_ptr<SegmentCacheHandle>>& segment_caches,
235
            std::vector<int64_t>* row_lsns = nullptr,
236
            std::vector<uint8_t>* insert_after_delete_flags = nullptr);
237
238
private:
239
    Status aggregate_for_sequence_column(
240
            Block* block, int num_rows, const std::vector<const IColumn*>& key_columns,
241
            const IColumn* seq_column, 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<const IColumn*>& 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, const IColumn* 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
    const segment_v2::MowKeyProbe& _probe;
281
    HistoricalRowFetcher& _fetcher;
282
283
    // Optional sidecar used by changelog. 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