Coverage Report

Created: 2026-08-10 15:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/storage/partial_update_info.cpp
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 "storage/partial_update_info.h"
19
20
#include <gen_cpp/olap_file.pb.h>
21
22
#include <cstdint>
23
#include <optional>
24
25
#include "common/consts.h"
26
#include "common/logging.h"
27
#include "core/assert_cast.h"
28
#include "core/block/block.h"
29
#include "core/data_type/data_type_number.h" // IWYU pragma: keep
30
#include "core/value/bitmap_value.h"
31
#include "storage/iterator/olap_data_convertor.h"
32
#include "storage/key/row_key_encoder.h"
33
#include "storage/olap_common.h"
34
#include "storage/rowset/rowset.h"
35
#include "storage/rowset/rowset_writer_context.h"
36
#include "storage/segment/historical_row_retriever.h"
37
#include "storage/segment/vertical_segment_writer.h"
38
#include "storage/tablet/base_tablet.h"
39
#include "storage/tablet/tablet_meta.h"
40
#include "storage/tablet/tablet_schema.h"
41
#include "storage/utils.h"
42
43
namespace doris {
44
namespace {
45
46
12
ColumnBitmap* get_mutable_skip_bitmap_column(Block* block, size_t skip_bitmap_col_idx) {
47
12
    auto skip_bitmap_column =
48
12
            IColumn::mutate(std::move(block->get_by_position(skip_bitmap_col_idx).column));
49
12
    auto* skip_bitmap_column_ptr = assert_cast<ColumnBitmap*>(skip_bitmap_column.get());
50
12
    block->replace_by_position(skip_bitmap_col_idx, std::move(skip_bitmap_column));
51
12
    return skip_bitmap_column_ptr;
52
12
}
53
54
} // namespace
55
56
Status PartialUpdateInfo::init(int64_t tablet_id, int64_t txn_id, const TabletSchema& tablet_schema,
57
                               UniqueKeyUpdateModePB unique_key_update_mode,
58
                               PartialUpdateNewRowPolicyPB policy,
59
                               const std::set<std::string>& partial_update_cols,
60
                               bool is_strict_mode_, int64_t timestamp_ms_, int32_t nano_seconds_,
61
                               const std::string& timezone_,
62
                               const std::string& auto_increment_column,
63
55
                               int32_t sequence_map_col_uid, int64_t cur_max_version) {
64
55
    partial_update_mode = unique_key_update_mode;
65
55
    partial_update_new_key_policy = policy;
66
55
    partial_update_input_columns = partial_update_cols;
67
55
    max_version_in_flush_phase = cur_max_version;
68
55
    sequence_map_col_unqiue_id = sequence_map_col_uid;
69
55
    timestamp_ms = timestamp_ms_;
70
55
    nano_seconds = nano_seconds_;
71
55
    timezone = timezone_;
72
55
    missing_cids.clear();
73
55
    update_cids.clear();
74
75
55
    if (partial_update_mode == UniqueKeyUpdateModePB::UPDATE_FIXED_COLUMNS) {
76
        // partial_update_cols should include all key columns
77
96
        for (std::size_t i {0}; i < tablet_schema.num_key_columns(); i++) {
78
77
            const auto key_col = tablet_schema.column(i);
79
77
            if (!partial_update_cols.contains(key_col.name())) {
80
0
                auto msg = fmt::format(
81
0
                        "Unable to do partial update on shadow index's tablet, tablet_id={}, "
82
0
                        "txn_id={}. Missing key column {}.",
83
0
                        tablet_id, txn_id, key_col.name());
84
0
                LOG_WARNING(msg);
85
0
                return Status::Aborted<false>(msg);
86
0
            }
87
77
        }
88
19
    }
89
55
    if (is_partial_update()) {
90
219
        for (auto i = 0; i < tablet_schema.num_columns(); ++i) {
91
196
            if (partial_update_mode == UniqueKeyUpdateModePB::UPDATE_FIXED_COLUMNS) {
92
136
                auto tablet_column = tablet_schema.column(i);
93
136
                if (!partial_update_input_columns.contains(tablet_column.name())) {
94
49
                    missing_cids.emplace_back(i);
95
49
                    if (!tablet_column.has_default_value() && !tablet_column.is_nullable() &&
96
49
                        tablet_schema.auto_increment_column() != tablet_column.name()) {
97
1
                        can_insert_new_rows_in_partial_update = false;
98
1
                    }
99
87
                } else {
100
87
                    update_cids.emplace_back(i);
101
87
                }
102
136
                if (auto_increment_column == tablet_column.name()) {
103
0
                    is_schema_contains_auto_inc_column = true;
104
0
                }
105
136
            } else {
106
                // in flexible partial update, missing cids is all non sort keys' cid
107
60
                if (i >= tablet_schema.num_key_columns()) {
108
18
                    missing_cids.emplace_back(i);
109
18
                }
110
60
            }
111
196
        }
112
23
        _generate_default_values_for_missing_cids(tablet_schema);
113
23
    }
114
55
    is_strict_mode = is_strict_mode_;
115
55
    is_input_columns_contains_auto_inc_column =
116
55
            is_fixed_partial_update() &&
117
55
            partial_update_input_columns.contains(auto_increment_column);
118
55
    return Status::OK();
119
55
}
120
121
0
void PartialUpdateInfo::to_pb(PartialUpdateInfoPB* partial_update_info_pb) const {
122
0
    partial_update_info_pb->set_partial_update_mode(partial_update_mode);
123
0
    partial_update_info_pb->set_partial_update_new_key_policy(partial_update_new_key_policy);
124
0
    partial_update_info_pb->set_max_version_in_flush_phase(max_version_in_flush_phase);
125
0
    for (const auto& col : partial_update_input_columns) {
126
0
        partial_update_info_pb->add_partial_update_input_columns(col);
127
0
    }
128
0
    for (auto cid : missing_cids) {
129
0
        partial_update_info_pb->add_missing_cids(cid);
130
0
    }
131
0
    for (auto cid : update_cids) {
132
0
        partial_update_info_pb->add_update_cids(cid);
133
0
    }
134
0
    partial_update_info_pb->set_can_insert_new_rows_in_partial_update(
135
0
            can_insert_new_rows_in_partial_update);
136
0
    partial_update_info_pb->set_is_strict_mode(is_strict_mode);
137
0
    partial_update_info_pb->set_timestamp_ms(timestamp_ms);
138
0
    partial_update_info_pb->set_nano_seconds(nano_seconds);
139
0
    partial_update_info_pb->set_timezone(timezone);
140
0
    partial_update_info_pb->set_is_input_columns_contains_auto_inc_column(
141
0
            is_input_columns_contains_auto_inc_column);
142
0
    partial_update_info_pb->set_is_schema_contains_auto_inc_column(
143
0
            is_schema_contains_auto_inc_column);
144
0
    for (const auto& value : default_values) {
145
0
        partial_update_info_pb->add_default_values(value);
146
0
    }
147
0
}
148
149
0
void PartialUpdateInfo::from_pb(PartialUpdateInfoPB* partial_update_info_pb) {
150
0
    if (!partial_update_info_pb->has_partial_update_mode()) {
151
        // for backward compatibility
152
0
        if (partial_update_info_pb->is_partial_update()) {
153
0
            partial_update_mode = UniqueKeyUpdateModePB::UPDATE_FIXED_COLUMNS;
154
0
        } else {
155
0
            partial_update_mode = UniqueKeyUpdateModePB::UPSERT;
156
0
        }
157
0
    } else {
158
0
        partial_update_mode = partial_update_info_pb->partial_update_mode();
159
0
    }
160
0
    if (partial_update_info_pb->has_partial_update_new_key_policy()) {
161
0
        partial_update_new_key_policy = partial_update_info_pb->partial_update_new_key_policy();
162
0
    }
163
0
    max_version_in_flush_phase = partial_update_info_pb->has_max_version_in_flush_phase()
164
0
                                         ? partial_update_info_pb->max_version_in_flush_phase()
165
0
                                         : -1;
166
0
    partial_update_input_columns.clear();
167
0
    for (const auto& col : partial_update_info_pb->partial_update_input_columns()) {
168
0
        partial_update_input_columns.insert(col);
169
0
    }
170
0
    missing_cids.clear();
171
0
    for (auto cid : partial_update_info_pb->missing_cids()) {
172
0
        missing_cids.push_back(cid);
173
0
    }
174
0
    update_cids.clear();
175
0
    for (auto cid : partial_update_info_pb->update_cids()) {
176
0
        update_cids.push_back(cid);
177
0
    }
178
0
    can_insert_new_rows_in_partial_update =
179
0
            partial_update_info_pb->can_insert_new_rows_in_partial_update();
180
0
    is_strict_mode = partial_update_info_pb->is_strict_mode();
181
0
    timestamp_ms = partial_update_info_pb->timestamp_ms();
182
0
    timezone = partial_update_info_pb->timezone();
183
0
    is_input_columns_contains_auto_inc_column =
184
0
            partial_update_info_pb->is_input_columns_contains_auto_inc_column();
185
0
    is_schema_contains_auto_inc_column =
186
0
            partial_update_info_pb->is_schema_contains_auto_inc_column();
187
0
    if (partial_update_info_pb->has_nano_seconds()) {
188
0
        nano_seconds = partial_update_info_pb->nano_seconds();
189
0
    }
190
0
    default_values.clear();
191
0
    for (const auto& value : partial_update_info_pb->default_values()) {
192
0
        default_values.push_back(value);
193
0
    }
194
0
}
195
196
0
std::string PartialUpdateInfo::summary() const {
197
0
    std::string mode;
198
0
    switch (partial_update_mode) {
199
0
    case UniqueKeyUpdateModePB::UPSERT:
200
0
        mode = "upsert";
201
0
        break;
202
0
    case UniqueKeyUpdateModePB::UPDATE_FIXED_COLUMNS:
203
0
        mode = "fixed partial update";
204
0
        break;
205
0
    case UniqueKeyUpdateModePB::UPDATE_FLEXIBLE_COLUMNS:
206
0
        mode = "flexible partial update";
207
0
        break;
208
0
    }
209
0
    return fmt::format(
210
0
            "mode={}, update_cids={}, missing_cids={}, is_strict_mode={}, "
211
0
            "max_version_in_flush_phase={}",
212
0
            mode, update_cids.size(), missing_cids.size(), is_strict_mode,
213
0
            max_version_in_flush_phase);
214
0
}
215
216
Status PartialUpdateInfo::handle_new_key(const TabletSchema& tablet_schema,
217
                                         const std::function<std::string()>& line,
218
32
                                         BitmapValue* skip_bitmap) {
219
32
    switch (partial_update_new_key_policy) {
220
32
    case doris::PartialUpdateNewRowPolicyPB::APPEND: {
221
32
        if (partial_update_mode == UniqueKeyUpdateModePB::UPDATE_FIXED_COLUMNS) {
222
24
            if (!can_insert_new_rows_in_partial_update) {
223
0
                std::string error_column;
224
0
                for (auto cid : missing_cids) {
225
0
                    const TabletColumn& col = tablet_schema.column(cid);
226
0
                    if (!col.has_default_value() && !col.is_nullable() &&
227
0
                        !(tablet_schema.auto_increment_column() == col.name())) {
228
0
                        error_column = col.name();
229
0
                        break;
230
0
                    }
231
0
                }
232
0
                return Status::Error<ErrorCode::INVALID_SCHEMA, false>(
233
0
                        "the unmentioned column `{}` should have default value or be nullable "
234
0
                        "for newly inserted rows in non-strict mode partial update",
235
0
                        error_column);
236
0
            }
237
24
        } else if (partial_update_mode == UniqueKeyUpdateModePB::UPDATE_FLEXIBLE_COLUMNS) {
238
8
            DCHECK(skip_bitmap != nullptr);
239
8
            bool can_insert_new_row {true};
240
8
            std::string error_column;
241
48
            for (auto cid : missing_cids) {
242
48
                const TabletColumn& col = tablet_schema.column(cid);
243
48
                if (skip_bitmap->contains(col.unique_id()) && !col.has_default_value() &&
244
48
                    !col.is_nullable() && !col.is_auto_increment()) {
245
0
                    error_column = col.name();
246
0
                    can_insert_new_row = false;
247
0
                    break;
248
0
                }
249
48
            }
250
8
            if (!can_insert_new_row) {
251
0
                return Status::Error<ErrorCode::INVALID_SCHEMA, false>(
252
0
                        "the unmentioned column `{}` should have default value or be "
253
0
                        "nullable for newly inserted rows in non-strict mode flexible partial "
254
0
                        "update",
255
0
                        error_column);
256
0
            }
257
8
        }
258
32
    } break;
259
32
    case doris::PartialUpdateNewRowPolicyPB::ERROR: {
260
0
        return Status::Error<ErrorCode::NEW_ROWS_IN_PARTIAL_UPDATE, false>(
261
0
                "Can't append new rows in partial update when partial_update_new_key_behavior is "
262
0
                "ERROR. Row with key=[{}] is not in table.",
263
0
                line());
264
32
    } break;
265
32
    }
266
32
    return Status::OK();
267
32
}
268
269
void PartialUpdateInfo::_generate_default_values_for_missing_cids(
270
23
        const TabletSchema& tablet_schema) {
271
67
    for (unsigned int cur_cid : missing_cids) {
272
67
        const auto& column = tablet_schema.column(cur_cid);
273
67
        if (column.has_default_value()) {
274
58
            std::string default_value;
275
58
            if (UNLIKELY((column.type() == FieldType::OLAP_FIELD_TYPE_DATETIMEV2 ||
276
58
                          column.type() == FieldType::OLAP_FIELD_TYPE_TIMESTAMPTZ) &&
277
58
                         to_lower(column.default_value()).find(to_lower("CURRENT_TIMESTAMP")) !=
278
58
                                 std::string::npos)) {
279
0
                auto pos = to_lower(column.default_value()).find('(');
280
0
                if (pos == std::string::npos) {
281
0
                    DateV2Value<DateTimeV2ValueType> dtv;
282
0
                    dtv.from_unixtime(timestamp_ms / 1000, timezone);
283
0
                    default_value = dtv.to_string();
284
0
                    if (column.type() == FieldType::OLAP_FIELD_TYPE_TIMESTAMPTZ) {
285
0
                        default_value += timezone;
286
0
                    }
287
0
                } else {
288
0
                    int precision = std::stoi(column.default_value().substr(pos + 1));
289
0
                    DateV2Value<DateTimeV2ValueType> dtv;
290
0
                    dtv.from_unixtime(timestamp_ms / 1000, nano_seconds, timezone, precision);
291
0
                    default_value = dtv.to_string();
292
0
                    if (column.type() == FieldType::OLAP_FIELD_TYPE_TIMESTAMPTZ) {
293
0
                        default_value += timezone;
294
0
                    }
295
0
                }
296
58
            } else if (UNLIKELY(column.type() == FieldType::OLAP_FIELD_TYPE_DATEV2 &&
297
58
                                to_lower(column.default_value()).find(to_lower("CURRENT_DATE")) !=
298
58
                                        std::string::npos)) {
299
0
                DateV2Value<DateV2ValueType> dv;
300
0
                dv.from_unixtime(timestamp_ms / 1000, timezone);
301
0
                default_value = dv.to_string();
302
58
            } else if (UNLIKELY(column.type() == FieldType::OLAP_FIELD_TYPE_BITMAP &&
303
58
                                to_lower(column.default_value()).find(to_lower("BITMAP_EMPTY")) !=
304
58
                                        std::string::npos)) {
305
0
                BitmapValue v = BitmapValue {};
306
0
                default_value = v.to_string();
307
58
            } else {
308
58
                default_value = column.default_value();
309
58
            }
310
58
            default_values.emplace_back(default_value);
311
58
        } else {
312
            // place an empty string here
313
9
            default_values.emplace_back();
314
9
        }
315
67
    }
316
23
    CHECK_EQ(missing_cids.size(), default_values.size());
317
23
}
318
319
22
bool FixedReadPlan::empty() const {
320
22
    return plan.empty();
321
22
}
322
323
87
void FixedReadPlan::prepare_to_read(const RowLocation& row_location, size_t pos) {
324
87
    plan[row_location.rowset_id][row_location.segment_id].emplace_back(row_location.row_id, pos);
325
87
}
326
327
// read columns by read plan
328
// read_index: ori_pos-> block_idx
329
Status FixedReadPlan::read_columns_by_plan(
330
        const TabletSchema& tablet_schema, std::vector<uint32_t> cids_to_read,
331
        const std::map<RowsetId, RowsetSharedPtr>& rsid_to_rowset, Block& block,
332
        std::map<uint32_t, uint32_t>* read_index, bool force_read_old_delete_signs,
333
52
        const signed char* __restrict cur_delete_signs) const {
334
52
    if (force_read_old_delete_signs) {
335
        // always read delete sign column from historical data
336
47
        if (block.get_position_by_name(DELETE_SIGN) == -1) {
337
9
            auto del_col_cid = tablet_schema.field_index(DELETE_SIGN);
338
9
            cids_to_read.emplace_back(del_col_cid);
339
9
            block.swap(tablet_schema.create_block_by_cids(cids_to_read));
340
9
        }
341
47
    }
342
52
    bool has_row_column = tablet_schema.has_row_store_for_all_columns();
343
52
    std::optional<Block::ScopedMutableColumns> mutable_columns_guard;
344
52
    MutableColumns* mutable_columns = nullptr;
345
52
    if (!has_row_column) {
346
40
        mutable_columns_guard.emplace(block);
347
40
        mutable_columns = &mutable_columns_guard->mutable_columns();
348
40
    }
349
52
    uint32_t read_idx = 0;
350
52
    for (const auto& [rowset_id, segment_row_mappings] : plan) {
351
48
        for (const auto& [segment_id, mappings] : segment_row_mappings) {
352
48
            auto rowset_iter = rsid_to_rowset.find(rowset_id);
353
48
            CHECK(rowset_iter != rsid_to_rowset.end());
354
48
            std::vector<uint32_t> rids;
355
87
            for (auto [rid, pos] : mappings) {
356
87
                if (cur_delete_signs && cur_delete_signs[pos]) {
357
0
                    continue;
358
0
                }
359
87
                rids.emplace_back(rid);
360
87
                (*read_index)[static_cast<uint32_t>(pos)] = read_idx++;
361
87
            }
362
48
            if (has_row_column) {
363
12
                auto st = BaseTablet::fetch_value_through_row_column(
364
12
                        rowset_iter->second, tablet_schema, segment_id, rids, cids_to_read, block);
365
12
                if (!st.ok()) {
366
0
                    LOG(WARNING) << "failed to fetch value through row column";
367
0
                    return st;
368
0
                }
369
12
                continue;
370
12
            }
371
151
            for (size_t cid = 0; cid < mutable_columns->size(); ++cid) {
372
115
                TabletColumn tablet_column = tablet_schema.column(cids_to_read[cid]);
373
115
                auto st = doris::BaseTablet::fetch_value_by_rowids(rowset_iter->second, segment_id,
374
115
                                                                   rids, tablet_column,
375
115
                                                                   (*mutable_columns)[cid]);
376
                // set read value to output block
377
115
                if (!st.ok()) {
378
0
                    LOG(WARNING) << "failed to fetch value";
379
0
                    return st;
380
0
                }
381
115
            }
382
36
        }
383
48
    }
384
52
    return Status::OK();
385
52
}
386
387
Status FixedReadPlan::fill_old_delete_signs(const Block& old_value_block,
388
                                            const std::map<uint32_t, uint32_t>& read_index,
389
                                            size_t num_rows,
390
48
                                            std::vector<signed char>* old_delete_signs) const {
391
48
    if (old_delete_signs == nullptr) {
392
26
        return Status::OK();
393
26
    }
394
22
    const auto* old_delete_sign_column_data =
395
22
            BaseTablet::get_delete_sign_column_data(old_value_block);
396
22
    if (old_delete_sign_column_data == nullptr) {
397
0
        return Status::InternalError("old delete signs column not found, block: {}",
398
0
                                     old_value_block.dump_structure());
399
0
    }
400
22
    old_delete_signs->assign(num_rows, 0);
401
72
    for (size_t idx = 0; idx < num_rows; ++idx) {
402
50
        auto it = read_index.find(cast_set<uint32_t>(idx));
403
50
        if (it != read_index.end()) {
404
32
            (*old_delete_signs)[idx] = old_delete_sign_column_data[it->second];
405
32
        }
406
50
    }
407
22
    return Status::OK();
408
22
}
409
410
Status FixedReadPlan::fill_missing_columns(
411
        const segment_v2::HistoricalRowRetrieverContext& historical_context,
412
        const std::map<RowsetId, RowsetSharedPtr>& rsid_to_rowset,
413
        const TabletSchema& tablet_schema, Block& full_block,
414
        const std::vector<bool>& use_default_or_null_flag, bool has_default_or_nullable,
415
        uint32_t segment_start_pos, const Block* block,
416
38
        std::vector<signed char>* old_delete_signs) const {
417
38
    auto mutable_full_columns_guard = full_block.mutate_columns_scoped();
418
38
    auto& mutable_full_columns = mutable_full_columns_guard.mutable_columns();
419
    // create old value columns
420
38
    DCHECK(historical_context.partial_update_info != nullptr);
421
38
    DCHECK(historical_context.tablet_schema != nullptr);
422
38
    const auto& partial_update_info = *historical_context.partial_update_info;
423
38
    const auto& missing_cids = partial_update_info.missing_cids;
424
38
    bool have_input_seq_column = false;
425
38
    if (tablet_schema.has_sequence_col()) {
426
20
        const std::vector<uint32_t>& including_cids = partial_update_info.update_cids;
427
20
        have_input_seq_column =
428
20
                (std::find(including_cids.cbegin(), including_cids.cend(),
429
20
                           tablet_schema.sequence_col_idx()) != including_cids.cend());
430
20
    }
431
432
38
    auto old_value_block = tablet_schema.create_block_by_cids(missing_cids);
433
38
    CHECK_EQ(missing_cids.size(), old_value_block.columns());
434
435
    // segment pos to write -> rowid to read in old_value_block
436
38
    std::map<uint32_t, uint32_t> read_index;
437
38
    RETURN_IF_ERROR(read_columns_by_plan(tablet_schema, missing_cids, rsid_to_rowset,
438
38
                                         old_value_block, &read_index, true, nullptr));
439
440
38
    const auto* old_delete_sign_column_data =
441
38
            BaseTablet::get_delete_sign_column_data(old_value_block);
442
38
    if (old_delete_sign_column_data == nullptr) {
443
0
        return Status::InternalError("old delete signs column not found, block: {}",
444
0
                                     old_value_block.dump_structure());
445
0
    }
446
38
    RETURN_IF_ERROR(fill_old_delete_signs(old_value_block, read_index,
447
38
                                          use_default_or_null_flag.size(), old_delete_signs));
448
    // build default value columns
449
38
    auto default_value_block = old_value_block.clone_empty();
450
38
    RETURN_IF_ERROR(BaseTablet::generate_default_value_block(tablet_schema, missing_cids,
451
38
                                                             partial_update_info.default_values,
452
38
                                                             old_value_block, default_value_block));
453
38
    auto mutable_default_value_columns_guard = default_value_block.mutate_columns_scoped();
454
38
    auto& mutable_default_value_columns = mutable_default_value_columns_guard.mutable_columns();
455
456
    // fill all missing value from mutable_old_columns, need to consider default value and null value
457
144
    for (auto idx = 0; idx < use_default_or_null_flag.size(); idx++) {
458
106
        auto segment_pos = idx + segment_start_pos;
459
106
        auto pos_in_old_block = read_index[segment_pos];
460
461
510
        for (auto i = 0; i < missing_cids.size(); ++i) {
462
            // if the column has default value, fill it with default value
463
            // otherwise, if the column is nullable, fill it with null value
464
404
            const auto& tablet_column = tablet_schema.column(missing_cids[i]);
465
404
            auto& missing_col = mutable_full_columns[missing_cids[i]];
466
467
404
            bool should_use_default = use_default_or_null_flag[idx];
468
404
            if (!should_use_default) {
469
268
                bool old_row_delete_sign = old_delete_sign_column_data[pos_in_old_block] != 0;
470
268
                if (old_row_delete_sign) {
471
2
                    if (!tablet_schema.has_sequence_col()) {
472
2
                        should_use_default = true;
473
2
                    } else if (have_input_seq_column || (!tablet_column.is_seqeunce_col())) {
474
                        // to keep the sequence column value not decreasing, we should read values of seq column
475
                        // from old rows even if the old row is deleted when the input don't specify the sequence column, otherwise
476
                        // it may cause the merge-on-read based compaction to produce incorrect results
477
0
                        should_use_default = true;
478
0
                    }
479
2
                }
480
268
            }
481
482
404
            if (should_use_default) {
483
138
                if (tablet_column.has_default_value()) {
484
112
                    missing_col->insert_from(*mutable_default_value_columns[i], 0);
485
112
                } else if (tablet_column.is_nullable()) {
486
25
                    auto* nullable_column = assert_cast<ColumnNullable*>(missing_col.get());
487
25
                    nullable_column->insert_many_defaults(1);
488
25
                } else if (tablet_schema.auto_increment_column() == tablet_column.name()) {
489
0
                    const auto& column = *DORIS_TRY(
490
0
                            historical_context.tablet_schema->column(tablet_column.name()));
491
0
                    DCHECK(column.type() == FieldType::OLAP_FIELD_TYPE_BIGINT);
492
0
                    auto* auto_inc_column = assert_cast<ColumnInt64*>(missing_col.get());
493
0
                    int pos = block->get_position_by_name(BeConsts::PARTIAL_UPDATE_AUTO_INC_COL);
494
0
                    if (pos == -1) {
495
0
                        return Status::InternalError("auto increment column not found in block {}",
496
0
                                                     block->dump_structure());
497
0
                    }
498
0
                    auto_inc_column->insert_from(*block->get_by_position(pos).column.get(), idx);
499
1
                } else {
500
                    // If the control flow reaches this branch, the column neither has default value
501
                    // nor is nullable. It means that the row's delete sign is marked, and the value
502
                    // columns are useless and won't be read. So we can just put arbitary values in the cells
503
1
                    missing_col->insert_default();
504
1
                }
505
266
            } else {
506
266
                missing_col->insert_from(*old_value_block.get_by_position(i).column,
507
266
                                         pos_in_old_block);
508
266
            }
509
404
        }
510
106
    }
511
38
    return Status::OK();
512
38
}
513
514
void FlexibleReadPlan::prepare_to_read(const RowLocation& row_location, size_t pos,
515
8
                                       const BitmapValue& skip_bitmap) {
516
8
    if (!use_row_store) {
517
8
        for (uint64_t col_uid : skip_bitmap) {
518
8
            plan[row_location.rowset_id][row_location.segment_id][static_cast<uint32_t>(col_uid)]
519
8
                    .emplace_back(row_location.row_id, pos);
520
8
        }
521
4
    } else {
522
4
        row_store_plan[row_location.rowset_id][row_location.segment_id].emplace_back(
523
4
                row_location.row_id, pos);
524
4
    }
525
8
}
526
527
Status FlexibleReadPlan::read_columns_by_plan(
528
        const TabletSchema& tablet_schema,
529
        const std::map<RowsetId, RowsetSharedPtr>& rsid_to_rowset, Block& old_value_block,
530
4
        std::map<uint32_t, std::map<uint32_t, uint32_t>>* read_index) const {
531
4
    auto mutable_columns_guard = old_value_block.mutate_columns_scoped();
532
4
    auto& mutable_columns = mutable_columns_guard.mutable_columns();
533
534
    // cid -> next rid to fill in block
535
4
    std::map<uint32_t, uint32_t> next_read_idx;
536
104
    for (uint32_t cid {0}; cid < tablet_schema.num_columns(); cid++) {
537
100
        next_read_idx[cid] = 0;
538
100
    }
539
540
4
    for (const auto& [rowset_id, segment_mappings] : plan) {
541
4
        for (const auto& [segment_id, uid_mappings] : segment_mappings) {
542
8
            for (const auto& [col_uid, mappings] : uid_mappings) {
543
8
                auto rowset_iter = rsid_to_rowset.find(rowset_id);
544
8
                CHECK(rowset_iter != rsid_to_rowset.end());
545
8
                auto cid = tablet_schema.field_index(col_uid);
546
8
                DCHECK_NE(cid, -1);
547
8
                DCHECK_GE(cid, tablet_schema.num_key_columns());
548
8
                std::vector<uint32_t> rids;
549
8
                for (auto [rid, pos] : mappings) {
550
8
                    rids.emplace_back(rid);
551
8
                    (*read_index)[cid][static_cast<uint32_t>(pos)] = next_read_idx[cid]++;
552
8
                }
553
554
8
                TabletColumn tablet_column = tablet_schema.column(cid);
555
8
                auto idx = cid - tablet_schema.num_key_columns();
556
8
                RETURN_IF_ERROR(doris::BaseTablet::fetch_value_by_rowids(
557
8
                        rowset_iter->second, segment_id, rids, tablet_column,
558
8
                        mutable_columns[idx]));
559
8
            }
560
4
        }
561
4
    }
562
    // !!!ATTENTION!!!: columns in block may have different size because every row has different columns to update
563
4
    return Status::OK();
564
4
}
565
566
Status FlexibleReadPlan::read_columns_by_plan(
567
        const TabletSchema& tablet_schema, const std::vector<uint32_t>& cids_to_read,
568
        const std::map<RowsetId, RowsetSharedPtr>& rsid_to_rowset, Block& old_value_block,
569
4
        std::map<uint32_t, uint32_t>* read_index) const {
570
4
    DCHECK(use_row_store);
571
4
    uint32_t read_idx = 0;
572
4
    for (const auto& [rowset_id, segment_row_mappings] : row_store_plan) {
573
4
        for (const auto& [segment_id, mappings] : segment_row_mappings) {
574
4
            auto rowset_iter = rsid_to_rowset.find(rowset_id);
575
4
            CHECK(rowset_iter != rsid_to_rowset.end());
576
4
            std::vector<uint32_t> rids;
577
4
            for (auto [rid, pos] : mappings) {
578
4
                rids.emplace_back(rid);
579
4
                (*read_index)[static_cast<uint32_t>(pos)] = read_idx++;
580
4
            }
581
4
            auto st = BaseTablet::fetch_value_through_row_column(rowset_iter->second, tablet_schema,
582
4
                                                                 segment_id, rids, cids_to_read,
583
4
                                                                 old_value_block);
584
4
            if (!st.ok()) {
585
0
                LOG(WARNING) << "failed to fetch value through row column";
586
0
                return st;
587
0
            }
588
4
        }
589
4
    }
590
4
    return Status::OK();
591
4
}
592
593
Status FlexibleReadPlan::fill_non_primary_key_columns(
594
        const segment_v2::HistoricalRowRetrieverContext& historical_context,
595
        const std::map<RowsetId, RowsetSharedPtr>& rsid_to_rowset,
596
        const TabletSchema& tablet_schema, Block& full_block,
597
        const std::vector<bool>& use_default_or_null_flag, bool has_default_or_nullable,
598
        uint32_t segment_start_pos, uint32_t block_start_pos, const Block* block,
599
8
        std::vector<BitmapValue>* skip_bitmaps) const {
600
8
    auto mutable_full_columns_guard = full_block.mutate_columns_scoped();
601
8
    auto& mutable_full_columns = mutable_full_columns_guard.mutable_columns();
602
8
    DCHECK(historical_context.partial_update_info != nullptr);
603
604
    // missing_cids are all non sort key columns' cids
605
8
    const auto& non_sort_key_cids = historical_context.partial_update_info->missing_cids;
606
8
    auto old_value_block = tablet_schema.create_block_by_cids(non_sort_key_cids);
607
8
    CHECK_EQ(non_sort_key_cids.size(), old_value_block.columns());
608
609
8
    if (!use_row_store) {
610
4
        RETURN_IF_ERROR(fill_non_primary_key_columns_for_column_store(
611
4
                historical_context, rsid_to_rowset, tablet_schema, non_sort_key_cids,
612
4
                old_value_block, mutable_full_columns, use_default_or_null_flag,
613
4
                has_default_or_nullable, segment_start_pos, block_start_pos, block, skip_bitmaps));
614
4
    } else {
615
4
        RETURN_IF_ERROR(fill_non_primary_key_columns_for_row_store(
616
4
                historical_context, rsid_to_rowset, tablet_schema, non_sort_key_cids,
617
4
                old_value_block, mutable_full_columns, use_default_or_null_flag,
618
4
                has_default_or_nullable, segment_start_pos, block_start_pos, block, skip_bitmaps));
619
4
    }
620
8
    return Status::OK();
621
8
}
622
623
static void fill_non_primary_key_cell_for_column_store(
624
        const TabletColumn& tablet_column, uint32_t cid, MutableColumnPtr& new_col,
625
        const IColumn& default_value_col, const IColumn& old_value_col, const IColumn& cur_col,
626
        std::size_t block_pos, uint32_t segment_pos, bool skipped, bool row_has_sequence_col,
627
        bool use_default, const signed char* delete_sign_column_data,
628
        const TabletSchema& tablet_schema,
629
        std::map<uint32_t, std::map<uint32_t, uint32_t>>& read_index,
630
60
        const PartialUpdateInfo* info) {
631
60
    if (skipped) {
632
20
        DCHECK(cid != tablet_schema.skip_bitmap_col_idx());
633
20
        DCHECK(cid != tablet_schema.version_col_idx());
634
20
        DCHECK(!tablet_column.is_row_store_column());
635
636
20
        if (!use_default) {
637
8
            if (delete_sign_column_data != nullptr) {
638
8
                bool old_row_delete_sign = false;
639
8
                if (auto it = read_index[tablet_schema.delete_sign_idx()].find(segment_pos);
640
8
                    it != read_index[tablet_schema.delete_sign_idx()].end()) {
641
8
                    old_row_delete_sign = (delete_sign_column_data[it->second] != 0);
642
8
                }
643
644
8
                if (old_row_delete_sign) {
645
0
                    if (!tablet_schema.has_sequence_col()) {
646
0
                        use_default = true;
647
0
                    } else if (row_has_sequence_col ||
648
0
                               (!tablet_column.is_seqeunce_col() &&
649
0
                                (tablet_column.unique_id() != info->sequence_map_col_uid()))) {
650
                        // to keep the sequence column value not decreasing, we should read values of seq column(and seq map column)
651
                        // from old rows even if the old row is deleted when the input don't specify the sequence column, otherwise
652
                        // it may cause the merge-on-read based compaction to produce incorrect results
653
0
                        use_default = true;
654
0
                    }
655
0
                }
656
8
            }
657
8
        }
658
20
        if (!use_default && tablet_column.is_on_update_current_timestamp()) {
659
0
            use_default = true;
660
0
        }
661
20
        if (use_default) {
662
12
            if (tablet_column.has_default_value()) {
663
12
                new_col->insert_from(default_value_col, 0);
664
12
            } else if (tablet_column.is_nullable()) {
665
0
                assert_cast<ColumnNullable*, TypeCheckOnRelease::DISABLE>(new_col.get())
666
0
                        ->insert_many_defaults(1);
667
0
            } else if (tablet_column.is_auto_increment()) {
668
                // In flexible partial update, the skip bitmap indicates whether a cell
669
                // is specified in the original load, so the generated auto-increment value is filled
670
                // in current block in place if needed rather than using a seperate column to
671
                // store the generated auto-increment value in fixed partial update
672
0
                new_col->insert_from(cur_col, block_pos);
673
0
            } else {
674
0
                new_col->insert_default();
675
0
            }
676
12
        } else {
677
8
            auto pos_in_old_block = read_index.at(cid).at(segment_pos);
678
8
            new_col->insert_from(old_value_col, pos_in_old_block);
679
8
        }
680
40
    } else {
681
40
        new_col->insert_from(cur_col, block_pos);
682
40
    }
683
60
}
684
685
Status FlexibleReadPlan::fill_non_primary_key_columns_for_column_store(
686
        const segment_v2::HistoricalRowRetrieverContext& historical_context,
687
        const std::map<RowsetId, RowsetSharedPtr>& rsid_to_rowset,
688
        const TabletSchema& tablet_schema, const std::vector<uint32_t>& non_sort_key_cids,
689
        Block& old_value_block, MutableColumns& mutable_full_columns,
690
        const std::vector<bool>& use_default_or_null_flag, bool has_default_or_nullable,
691
        uint32_t segment_start_pos, uint32_t block_start_pos, const Block* block,
692
4
        std::vector<BitmapValue>* skip_bitmaps) const {
693
4
    auto* info = historical_context.partial_update_info.get();
694
4
    int32_t seq_col_unique_id = -1;
695
4
    if (tablet_schema.has_sequence_col()) {
696
0
        seq_col_unique_id = tablet_schema.column(tablet_schema.sequence_col_idx()).unique_id();
697
0
    }
698
    // cid -> segment pos to write -> rowid to read in old_value_block
699
4
    std::map<uint32_t, std::map<uint32_t, uint32_t>> read_index;
700
4
    RETURN_IF_ERROR(
701
4
            read_columns_by_plan(tablet_schema, rsid_to_rowset, old_value_block, &read_index));
702
    // !!!ATTENTION!!!: columns in old_value_block may have different size because every row has different columns to update
703
704
4
    const auto* delete_sign_column_data = BaseTablet::get_delete_sign_column_data(old_value_block);
705
    // build default value columns
706
4
    auto default_value_block = old_value_block.clone_empty();
707
4
    if (has_default_or_nullable || delete_sign_column_data != nullptr) {
708
4
        RETURN_IF_ERROR(BaseTablet::generate_default_value_block(
709
4
                tablet_schema, non_sort_key_cids, info->default_values, old_value_block,
710
4
                default_value_block));
711
4
    }
712
713
    // fill all non sort key columns from mutable_old_columns, need to consider default value and null value
714
24
    for (std::size_t i {0}; i < non_sort_key_cids.size(); i++) {
715
20
        auto cid = non_sort_key_cids[i];
716
20
        const auto& tablet_column = tablet_schema.column(cid);
717
20
        auto col_uid = tablet_column.unique_id();
718
80
        for (auto idx = 0; idx < use_default_or_null_flag.size(); idx++) {
719
60
            auto segment_pos = segment_start_pos + idx;
720
60
            auto block_pos = block_start_pos + idx;
721
722
60
            fill_non_primary_key_cell_for_column_store(
723
60
                    tablet_column, cid, mutable_full_columns[cid],
724
60
                    *default_value_block.get_by_position(i).column,
725
60
                    *old_value_block.get_by_position(i).column, *block->get_by_position(cid).column,
726
60
                    block_pos, segment_pos, skip_bitmaps->at(block_pos).contains(col_uid),
727
60
                    tablet_schema.has_sequence_col()
728
60
                            ? !skip_bitmaps->at(block_pos).contains(seq_col_unique_id)
729
60
                            : false,
730
60
                    use_default_or_null_flag[idx], delete_sign_column_data, tablet_schema,
731
60
                    read_index, info);
732
60
        }
733
20
    }
734
4
    return Status::OK();
735
4
}
736
737
static void fill_non_primary_key_cell_for_row_store(
738
        const TabletColumn& tablet_column, uint32_t cid, MutableColumnPtr& new_col,
739
        const IColumn& default_value_col, const IColumn& old_value_col, const IColumn& cur_col,
740
        std::size_t block_pos, bool skipped, bool row_has_sequence_col, bool use_default,
741
        const signed char* delete_sign_column_data, uint32_t pos_in_old_block,
742
84
        const TabletSchema& tablet_schema, const PartialUpdateInfo* info) {
743
84
    if (skipped) {
744
24
        DCHECK(cid != tablet_schema.skip_bitmap_col_idx());
745
24
        DCHECK(cid != tablet_schema.version_col_idx());
746
24
        DCHECK(!tablet_column.is_row_store_column());
747
24
        if (!use_default) {
748
8
            if (delete_sign_column_data != nullptr) {
749
8
                bool old_row_delete_sign = (delete_sign_column_data[pos_in_old_block] != 0);
750
8
                if (old_row_delete_sign) {
751
0
                    if (!tablet_schema.has_sequence_col()) {
752
0
                        use_default = true;
753
0
                    } else if (row_has_sequence_col ||
754
0
                               (!tablet_column.is_seqeunce_col() &&
755
0
                                (tablet_column.unique_id() != info->sequence_map_col_uid()))) {
756
                        // to keep the sequence column value not decreasing, we should read values of seq column(and seq map column)
757
                        // from old rows even if the old row is deleted when the input don't specify the sequence column, otherwise
758
                        // it may cause the merge-on-read based compaction to produce incorrect results
759
0
                        use_default = true;
760
0
                    }
761
0
                }
762
8
            }
763
8
        }
764
765
24
        if (!use_default && tablet_column.is_on_update_current_timestamp()) {
766
0
            use_default = true;
767
0
        }
768
24
        if (use_default) {
769
16
            if (tablet_column.has_default_value()) {
770
12
                new_col->insert_from(default_value_col, 0);
771
12
            } else if (tablet_column.is_nullable()) {
772
4
                assert_cast<ColumnNullable*, TypeCheckOnRelease::DISABLE>(new_col.get())
773
4
                        ->insert_many_defaults(1);
774
4
            } else if (tablet_column.is_auto_increment()) {
775
                // In flexible partial update, the skip bitmap indicates whether a cell
776
                // is specified in the original load, so the generated auto-increment value is filled
777
                // in current block in place if needed rather than using a seperate column to
778
                // store the generated auto-increment value in fixed partial update
779
0
                new_col->insert_from(cur_col, block_pos);
780
0
            } else {
781
0
                new_col->insert_default();
782
0
            }
783
16
        } else {
784
8
            new_col->insert_from(old_value_col, pos_in_old_block);
785
8
        }
786
60
    } else {
787
60
        new_col->insert_from(cur_col, block_pos);
788
60
    }
789
84
}
790
791
Status FlexibleReadPlan::fill_non_primary_key_columns_for_row_store(
792
        const segment_v2::HistoricalRowRetrieverContext& historical_context,
793
        const std::map<RowsetId, RowsetSharedPtr>& rsid_to_rowset,
794
        const TabletSchema& tablet_schema, const std::vector<uint32_t>& non_sort_key_cids,
795
        Block& old_value_block, MutableColumns& mutable_full_columns,
796
        const std::vector<bool>& use_default_or_null_flag, bool has_default_or_nullable,
797
        uint32_t segment_start_pos, uint32_t block_start_pos, const Block* block,
798
4
        std::vector<BitmapValue>* skip_bitmaps) const {
799
4
    auto* info = historical_context.partial_update_info.get();
800
4
    int32_t seq_col_unique_id = -1;
801
4
    if (tablet_schema.has_sequence_col()) {
802
4
        seq_col_unique_id = tablet_schema.column(tablet_schema.sequence_col_idx()).unique_id();
803
4
    }
804
    // segment pos to write -> rowid to read in old_value_block
805
4
    std::map<uint32_t, uint32_t> read_index;
806
4
    RETURN_IF_ERROR(read_columns_by_plan(tablet_schema, non_sort_key_cids, rsid_to_rowset,
807
4
                                         old_value_block, &read_index));
808
809
4
    const auto* delete_sign_column_data = BaseTablet::get_delete_sign_column_data(old_value_block);
810
    // build default value columns
811
4
    auto default_value_block = old_value_block.clone_empty();
812
4
    if (has_default_or_nullable || delete_sign_column_data != nullptr) {
813
4
        RETURN_IF_ERROR(BaseTablet::generate_default_value_block(
814
4
                tablet_schema, non_sort_key_cids, info->default_values, old_value_block,
815
4
                default_value_block));
816
4
    }
817
818
    // fill all non sort key columns from mutable_old_columns, need to consider default value and null value
819
32
    for (std::size_t i {0}; i < non_sort_key_cids.size(); i++) {
820
28
        auto cid = non_sort_key_cids[i];
821
28
        const auto& tablet_column = tablet_schema.column(cid);
822
28
        auto col_uid = tablet_column.unique_id();
823
112
        for (auto idx = 0; idx < use_default_or_null_flag.size(); idx++) {
824
84
            auto segment_pos = segment_start_pos + idx;
825
84
            auto block_pos = block_start_pos + idx;
826
84
            auto pos_in_old_block = read_index[segment_pos];
827
828
84
            fill_non_primary_key_cell_for_row_store(
829
84
                    tablet_column, cid, mutable_full_columns[cid],
830
84
                    *default_value_block.get_by_position(i).column,
831
84
                    *old_value_block.get_by_position(i).column, *block->get_by_position(cid).column,
832
84
                    block_pos, skip_bitmaps->at(block_pos).contains(col_uid),
833
84
                    tablet_schema.has_sequence_col()
834
84
                            ? !skip_bitmaps->at(block_pos).contains(seq_col_unique_id)
835
84
                            : false,
836
84
                    use_default_or_null_flag[idx], delete_sign_column_data, pos_in_old_block,
837
84
                    tablet_schema, info);
838
84
        }
839
28
    }
840
4
    return Status::OK();
841
4
}
842
843
BlockAggregator::BlockAggregator(segment_v2::VerticalSegmentWriter& vertical_segment_writer)
844
140
        : _writer(vertical_segment_writer), _tablet_schema(*_writer._tablet_schema) {}
845
846
void BlockAggregator::merge_one_row(MutableBlock& dst_block, Block* src_block, int rid,
847
0
                                    BitmapValue& skip_bitmap) {
848
0
    for (size_t cid {_tablet_schema.num_key_columns()}; cid < _tablet_schema.num_columns(); cid++) {
849
0
        if (cid == _tablet_schema.skip_bitmap_col_idx()) {
850
0
            auto& cur_skip_bitmap =
851
0
                    assert_cast<ColumnBitmap*>(dst_block.mutable_columns()[cid].get())
852
0
                            ->get_data()
853
0
                            .back();
854
0
            const auto& new_row_skip_bitmap =
855
0
                    assert_cast<const ColumnBitmap*>(src_block->get_by_position(cid).column.get())
856
0
                            ->get_data()[rid];
857
0
            cur_skip_bitmap &= new_row_skip_bitmap;
858
0
            continue;
859
0
        }
860
0
        if (!skip_bitmap.contains(_tablet_schema.column(cid).unique_id())) {
861
0
            dst_block.mutable_columns()[cid]->pop_back(1);
862
0
            dst_block.mutable_columns()[cid]->insert_from(*src_block->get_by_position(cid).column,
863
0
                                                          rid);
864
0
        }
865
0
    }
866
0
    VLOG_DEBUG << fmt::format("merge a row, after merge, output_block.rows()={}, state: {}",
867
0
                              dst_block.rows(), _state.to_string());
868
0
}
869
870
8
void BlockAggregator::append_one_row(MutableBlock& dst_block, Block* src_block, int rid) {
871
8
    dst_block.add_row(src_block, rid);
872
8
    _state.rows++;
873
8
    VLOG_DEBUG << fmt::format("append a new row, after append, output_block.rows()={}, state: {}",
874
0
                              dst_block.rows(), _state.to_string());
875
8
}
876
877
4
void BlockAggregator::remove_last_n_rows(MutableBlock& dst_block, int n) {
878
4
    if (n > 0) {
879
0
        for (size_t cid {0}; cid < _tablet_schema.num_columns(); cid++) {
880
0
            DCHECK_GE(dst_block.mutable_columns()[cid]->size(), n);
881
0
            dst_block.mutable_columns()[cid]->pop_back(n);
882
0
        }
883
0
    }
884
4
}
885
886
void BlockAggregator::append_or_merge_row(MutableBlock& dst_block, Block* src_block, int rid,
887
8
                                          BitmapValue& skip_bitmap, bool have_delete_sign) {
888
8
    if (have_delete_sign) {
889
        // remove all the previous batched rows
890
4
        remove_last_n_rows(dst_block, _state.rows);
891
4
        _state.rows = 0;
892
4
        _state.has_row_with_delete_sign = true;
893
894
4
        append_one_row(dst_block, src_block, rid);
895
4
    } else {
896
4
        if (_state.should_merge()) {
897
0
            merge_one_row(dst_block, src_block, rid, skip_bitmap);
898
4
        } else {
899
4
            append_one_row(dst_block, src_block, rid);
900
4
        }
901
4
    }
902
8
};
903
904
Status BlockAggregator::aggregate_rows(
905
        MutableBlock& output_block, Block* block, int start, int end, std::string key,
906
        std::vector<BitmapValue>* skip_bitmaps, const signed char* delete_signs,
907
        IOlapColumnDataAccessor* seq_column, const std::vector<RowsetSharedPtr>& specified_rowsets,
908
12
        std::vector<std::unique_ptr<SegmentCacheHandle>>& segment_caches) {
909
12
    VLOG_DEBUG << fmt::format("merge rows in range=[{}-{})", start, end);
910
12
    if (end - start == 1) {
911
8
        output_block.add_row(block, start);
912
8
        VLOG_DEBUG << fmt::format("append a row directly, rid={}", start);
913
8
        return Status::OK();
914
8
    }
915
916
4
    auto seq_col_unique_id = _tablet_schema.column(_tablet_schema.sequence_col_idx()).unique_id();
917
4
    auto delete_sign_col_unique_id =
918
4
            _tablet_schema.column(_tablet_schema.delete_sign_idx()).unique_id();
919
920
4
    _state.reset();
921
922
4
    RowLocation loc;
923
4
    RowsetSharedPtr rowset;
924
4
    std::string previous_encoded_seq_value {};
925
4
    Status st = _writer._tablet->lookup_row_key(
926
4
            key, &_tablet_schema, false, specified_rowsets, &loc, _writer._mow_context->max_version,
927
4
            segment_caches, &rowset, true, &previous_encoded_seq_value);
928
4
    int pos = start;
929
4
    bool is_expected_st = (st.is<ErrorCode::KEY_NOT_FOUND>() || st.ok());
930
4
    DCHECK(is_expected_st || st.is<ErrorCode::MEM_LIMIT_EXCEEDED>())
931
0
            << "[BlockAggregator::aggregate_rows] unexpected error status while lookup_row_key:"
932
0
            << st;
933
4
    if (!is_expected_st) {
934
0
        return st;
935
0
    }
936
937
4
    std::string cur_seq_val;
938
4
    if (st.ok()) {
939
4
        for (pos = start; pos < end; pos++) {
940
4
            auto& skip_bitmap = skip_bitmaps->at(pos);
941
4
            bool row_has_sequence_col = (!skip_bitmap.contains(seq_col_unique_id));
942
            // Discard all the rows whose seq value is smaller than previous_encoded_seq_value.
943
4
            if (row_has_sequence_col) {
944
4
                std::string seq_val {};
945
4
                _writer._key_encoder.append_seq_suffix(&seq_val, seq_column, pos);
946
4
                if (Slice {seq_val}.compare(Slice {previous_encoded_seq_value}) < 0) {
947
0
                    continue;
948
0
                }
949
4
                cur_seq_val = std::move(seq_val);
950
4
                break;
951
4
            }
952
0
            cur_seq_val = std::move(previous_encoded_seq_value);
953
0
            break;
954
4
        }
955
4
    } else {
956
0
        pos = start;
957
0
        auto& skip_bitmap = skip_bitmaps->at(pos);
958
0
        bool row_has_sequence_col = (!skip_bitmap.contains(seq_col_unique_id));
959
0
        if (row_has_sequence_col) {
960
0
            std::string seq_val {};
961
            // for rows that don't specify seqeunce col, seq_val will be encoded to minial value
962
0
            _writer._key_encoder.append_seq_suffix(&seq_val, seq_column, pos);
963
0
            cur_seq_val = std::move(seq_val);
964
0
        } else {
965
0
            cur_seq_val.clear();
966
0
            RETURN_IF_ERROR(_writer._generate_encoded_default_seq_value(
967
0
                    _tablet_schema, *_writer._opts.rowset_ctx->partial_update_info, &cur_seq_val));
968
0
        }
969
0
    }
970
971
12
    for (int rid {pos}; rid < end; rid++) {
972
8
        auto& skip_bitmap = skip_bitmaps->at(rid);
973
8
        bool row_has_sequence_col = (!skip_bitmap.contains(seq_col_unique_id));
974
8
        bool have_delete_sign =
975
8
                (!skip_bitmap.contains(delete_sign_col_unique_id) && delete_signs[rid] != 0);
976
8
        if (!row_has_sequence_col) {
977
4
            append_or_merge_row(output_block, block, rid, skip_bitmap, have_delete_sign);
978
4
        } else {
979
4
            std::string seq_val {};
980
4
            _writer._key_encoder.append_seq_suffix(&seq_val, seq_column, rid);
981
4
            if (Slice {seq_val}.compare(Slice {cur_seq_val}) >= 0) {
982
4
                append_or_merge_row(output_block, block, rid, skip_bitmap, have_delete_sign);
983
4
                cur_seq_val = std::move(seq_val);
984
4
            } else {
985
0
                VLOG_DEBUG << fmt::format(
986
0
                        "skip rid={} becasue its seq value is lower than the previous", rid);
987
0
            }
988
4
        }
989
8
    }
990
4
    return Status::OK();
991
4
};
992
993
Status BlockAggregator::aggregate_for_sequence_column(
994
        Block* block, int num_rows, const std::vector<IOlapColumnDataAccessor*>& key_columns,
995
        IOlapColumnDataAccessor* seq_column, const std::vector<RowsetSharedPtr>& specified_rowsets,
996
4
        std::vector<std::unique_ptr<SegmentCacheHandle>>& segment_caches) {
997
4
    DCHECK_EQ(block->columns(), _tablet_schema.num_columns());
998
    // the process logic here is the same as MemTable::_aggregate_for_flexible_partial_update_without_seq_col()
999
    // after this function, there will be at most 2 rows for a specified key
1000
4
    std::vector<BitmapValue>* skip_bitmaps =
1001
4
            &get_mutable_skip_bitmap_column(block, _tablet_schema.skip_bitmap_col_idx())
1002
4
                     ->get_data();
1003
4
    const auto* delete_signs = BaseTablet::get_delete_sign_column_data(*block, num_rows);
1004
1005
4
    auto filtered_block = _tablet_schema.create_block();
1006
4
    MutableBlock output_block = MutableBlock::build_mutable_block(std::move(filtered_block));
1007
1008
4
    int same_key_rows {0};
1009
4
    std::string previous_key {};
1010
20
    for (int block_pos {0}; block_pos < num_rows; block_pos++) {
1011
16
        std::string key = _writer._key_encoder.full_encode(key_columns, block_pos);
1012
16
        if (block_pos > 0 && previous_key == key) {
1013
4
            same_key_rows++;
1014
12
        } else {
1015
12
            if (same_key_rows > 0) {
1016
8
                RETURN_IF_ERROR(aggregate_rows(output_block, block, block_pos - same_key_rows,
1017
8
                                               block_pos, std::move(previous_key), skip_bitmaps,
1018
8
                                               delete_signs, seq_column, specified_rowsets,
1019
8
                                               segment_caches));
1020
8
            }
1021
12
            same_key_rows = 1;
1022
12
        }
1023
16
        previous_key = std::move(key);
1024
16
    }
1025
4
    if (same_key_rows > 0) {
1026
4
        RETURN_IF_ERROR(aggregate_rows(output_block, block, num_rows - same_key_rows, num_rows,
1027
4
                                       std::move(previous_key), skip_bitmaps, delete_signs,
1028
4
                                       seq_column, specified_rowsets, segment_caches));
1029
4
    }
1030
1031
4
    block->swap(output_block.to_block());
1032
4
    return Status::OK();
1033
4
}
1034
1035
Status BlockAggregator::fill_sequence_column(Block* block, size_t num_rows,
1036
                                             const FixedReadPlan& read_plan,
1037
4
                                             std::vector<BitmapValue>& skip_bitmaps) {
1038
4
    DCHECK(_tablet_schema.has_sequence_col());
1039
4
    std::vector<uint32_t> cids {static_cast<uint32_t>(_tablet_schema.sequence_col_idx())};
1040
4
    auto seq_col_unique_id = _tablet_schema.column(_tablet_schema.sequence_col_idx()).unique_id();
1041
1042
4
    auto seq_col_block = _tablet_schema.create_block_by_cids(cids);
1043
4
    auto tmp_block = _tablet_schema.create_block_by_cids(cids);
1044
4
    std::map<uint32_t, uint32_t> read_index;
1045
4
    RETURN_IF_ERROR(read_plan.read_columns_by_plan(_tablet_schema, cids, _writer._rsid_to_rowset,
1046
4
                                                   seq_col_block, &read_index, false));
1047
1048
4
    auto new_seq_col_ptr = tmp_block.get_by_position(0).column->assert_mutable();
1049
4
    const auto& old_seq_col_ptr = *seq_col_block.get_by_position(0).column;
1050
4
    const auto& cur_seq_col_ptr = *block->get_by_position(_tablet_schema.sequence_col_idx()).column;
1051
20
    for (uint32_t block_pos {0}; block_pos < num_rows; block_pos++) {
1052
16
        if (read_index.contains(block_pos)) {
1053
4
            new_seq_col_ptr->insert_from(old_seq_col_ptr, read_index[block_pos]);
1054
4
            skip_bitmaps[block_pos].remove(seq_col_unique_id);
1055
12
        } else {
1056
12
            new_seq_col_ptr->insert_from(cur_seq_col_ptr, block_pos);
1057
12
        }
1058
16
    }
1059
4
    block->replace_by_position(_tablet_schema.sequence_col_idx(), std::move(new_seq_col_ptr));
1060
4
    return Status::OK();
1061
4
}
1062
1063
Status BlockAggregator::aggregate_for_insert_after_delete(
1064
        Block* block, size_t num_rows, const std::vector<IOlapColumnDataAccessor*>& key_columns,
1065
        const std::vector<RowsetSharedPtr>& specified_rowsets,
1066
8
        std::vector<std::unique_ptr<SegmentCacheHandle>>& segment_caches) {
1067
8
    DCHECK_EQ(block->columns(), _tablet_schema.num_columns());
1068
    // there will be at most 2 rows for a specified key in block when control flow reaches here
1069
    // after this function, there will not be duplicate rows in block
1070
1071
8
    std::vector<BitmapValue>* skip_bitmaps =
1072
8
            &get_mutable_skip_bitmap_column(block, _tablet_schema.skip_bitmap_col_idx())
1073
8
                     ->get_data();
1074
8
    const auto* delete_signs = BaseTablet::get_delete_sign_column_data(*block, num_rows);
1075
1076
8
    auto filter_column = ColumnUInt8::create(num_rows, 1);
1077
8
    auto* __restrict filter_map = filter_column->get_data().data();
1078
8
    std::string previous_key {};
1079
8
    bool previous_has_delete_sign {false};
1080
8
    int duplicate_rows {0};
1081
8
    int32_t delete_sign_col_unique_id =
1082
8
            _tablet_schema.column(_tablet_schema.delete_sign_idx()).unique_id();
1083
8
    auto seq_col_unique_id =
1084
8
            (_tablet_schema.sequence_col_idx() != -1)
1085
8
                    ? _tablet_schema.column(_tablet_schema.sequence_col_idx()).unique_id()
1086
8
                    : -1;
1087
8
    FixedReadPlan read_plan;
1088
40
    for (size_t block_pos {0}; block_pos < num_rows; block_pos++) {
1089
32
        size_t delta_pos = block_pos;
1090
32
        auto& skip_bitmap = skip_bitmaps->at(block_pos);
1091
32
        std::string key = _writer._key_encoder.full_encode(key_columns, delta_pos);
1092
32
        bool have_delete_sign =
1093
32
                (!skip_bitmap.contains(delete_sign_col_unique_id) && delete_signs[block_pos] != 0);
1094
32
        if (delta_pos > 0 && previous_key == key) {
1095
            // !!ATTENTION!!: We can only remove the row with delete sign if there is a insert with the same key after this row.
1096
            // If there is only a row with delete sign, we should keep it and can't remove it from block, because
1097
            // compaction will not use the delete bitmap when reading data. So there may still be rows with delete sign
1098
            // in later process
1099
8
            DCHECK(previous_has_delete_sign);
1100
8
            DCHECK(!have_delete_sign);
1101
8
            ++duplicate_rows;
1102
8
            RowLocation loc;
1103
8
            RowsetSharedPtr rowset;
1104
8
            Status st = _writer._tablet->lookup_row_key(
1105
8
                    key, &_tablet_schema, false, specified_rowsets, &loc,
1106
8
                    _writer._mow_context->max_version, segment_caches, &rowset, true);
1107
8
            bool is_expected_st = (st.is<ErrorCode::KEY_NOT_FOUND>() || st.ok());
1108
8
            DCHECK(is_expected_st || st.is<ErrorCode::MEM_LIMIT_EXCEEDED>())
1109
0
                    << "[BlockAggregator::aggregate_for_insert_after_delete] unexpected error "
1110
0
                       "status while lookup_row_key:"
1111
0
                    << st;
1112
8
            if (!is_expected_st) {
1113
0
                return st;
1114
0
            }
1115
1116
8
            Slice previous_seq_slice {};
1117
8
            if (st.ok()) {
1118
8
                if (_tablet_schema.has_sequence_col()) {
1119
                    // if the insert row doesn't specify the sequence column, we need to
1120
                    // read the historical's sequence column value so that we don't need
1121
                    // to handle seqeunce column in append_block_with_flexible_content()
1122
                    // for this row
1123
4
                    bool row_has_sequence_col = (!skip_bitmap.contains(seq_col_unique_id));
1124
4
                    if (!row_has_sequence_col) {
1125
4
                        read_plan.prepare_to_read(loc, block_pos);
1126
4
                        _writer._rsid_to_rowset.emplace(rowset->rowset_id(), rowset);
1127
4
                    }
1128
4
                }
1129
                // delete the existing row
1130
8
                _writer._mow_context->delete_bitmap->add(
1131
8
                        {loc.rowset_id, loc.segment_id, DeleteBitmap::TEMP_VERSION_COMMON},
1132
8
                        loc.row_id);
1133
8
            }
1134
            // and remove the row with delete sign from the current block
1135
8
            filter_map[block_pos - 1] = 0;
1136
8
        }
1137
32
        previous_has_delete_sign = have_delete_sign;
1138
32
        previous_key = std::move(key);
1139
32
    }
1140
8
    if (duplicate_rows > 0) {
1141
8
        if (!read_plan.empty()) {
1142
            // fill sequence column value for some rows
1143
4
            RETURN_IF_ERROR(fill_sequence_column(block, num_rows, read_plan, *skip_bitmaps));
1144
4
        }
1145
8
        RETURN_IF_ERROR(filter_block(block, num_rows, std::move(filter_column), duplicate_rows,
1146
8
                                     "__filter_insert_after_delete_col__"));
1147
8
    }
1148
8
    return Status::OK();
1149
8
}
1150
1151
Status BlockAggregator::filter_block(Block* block, size_t num_rows, MutableColumnPtr filter_column,
1152
8
                                     int duplicate_rows, std::string col_name) {
1153
8
    auto num_cols = block->columns();
1154
8
    block->insert({std::move(filter_column), std::make_shared<DataTypeUInt8>(), col_name});
1155
8
    RETURN_IF_ERROR(Block::filter_block(block, num_cols, num_cols));
1156
8
    DCHECK_EQ(num_cols, block->columns());
1157
8
    size_t merged_rows = num_rows - block->rows();
1158
8
    if (duplicate_rows != merged_rows) {
1159
0
        auto msg = fmt::format(
1160
0
                "filter_block_for_flexible_partial_update {}: duplicate_rows != merged_rows, "
1161
0
                "duplicate_keys={}, merged_rows={}, num_rows={}, mutable_block->rows()={}",
1162
0
                col_name, duplicate_rows, merged_rows, num_rows, block->rows());
1163
0
        DCHECK(false) << msg;
1164
0
        return Status::InternalError<false>(msg);
1165
0
    }
1166
8
    return Status::OK();
1167
8
}
1168
1169
Status BlockAggregator::convert_pk_columns(Block* block, size_t row_pos, size_t num_rows,
1170
16
                                           std::vector<IOlapColumnDataAccessor*>& key_columns) {
1171
16
    key_columns.clear();
1172
336
    for (uint32_t cid {0}; cid < _tablet_schema.num_key_columns(); cid++) {
1173
320
        RETURN_IF_ERROR(_writer._olap_data_convertor->set_source_content_with_specifid_column(
1174
320
                block->get_by_position(cid), row_pos, num_rows, cid));
1175
320
        auto [status, column] = _writer._olap_data_convertor->convert_column_data(cid);
1176
320
        if (!status.ok()) {
1177
0
            return status;
1178
0
        }
1179
320
        key_columns.push_back(column);
1180
320
    }
1181
16
    return Status::OK();
1182
16
}
1183
1184
Status BlockAggregator::convert_seq_column(Block* block, size_t row_pos, size_t num_rows,
1185
16
                                           IOlapColumnDataAccessor*& seq_column) {
1186
16
    seq_column = nullptr;
1187
16
    if (_tablet_schema.has_sequence_col()) {
1188
8
        auto seq_col_idx = _tablet_schema.sequence_col_idx();
1189
8
        RETURN_IF_ERROR(_writer._olap_data_convertor->set_source_content_with_specifid_column(
1190
8
                block->get_by_position(seq_col_idx), row_pos, num_rows, seq_col_idx));
1191
8
        auto [status, column] = _writer._olap_data_convertor->convert_column_data(seq_col_idx);
1192
8
        if (!status.ok()) {
1193
0
            return status;
1194
0
        }
1195
8
        seq_column = column;
1196
8
    }
1197
16
    return Status::OK();
1198
16
};
1199
1200
Status BlockAggregator::aggregate_for_flexible_partial_update(
1201
        Block* block, size_t num_rows, const std::vector<RowsetSharedPtr>& specified_rowsets,
1202
8
        std::vector<std::unique_ptr<SegmentCacheHandle>>& segment_caches) {
1203
8
    std::vector<IOlapColumnDataAccessor*> key_columns {};
1204
8
    IOlapColumnDataAccessor* seq_column {nullptr};
1205
1206
8
    RETURN_IF_ERROR(convert_pk_columns(block, 0, num_rows, key_columns));
1207
8
    RETURN_IF_ERROR(convert_seq_column(block, 0, num_rows, seq_column));
1208
1209
    // 1. merge duplicate rows when table has sequence column
1210
    // When there are multiple rows with the same keys in memtable, some of them specify specify the sequence column,
1211
    // some of them don't. We can't do the de-duplication in memtable because we don't know the historical data. We must
1212
    // de-duplicate them here.
1213
8
    if (_tablet_schema.has_sequence_col()) {
1214
4
        RETURN_IF_ERROR(aggregate_for_sequence_column(block, static_cast<int>(num_rows),
1215
4
                                                      key_columns, seq_column, specified_rowsets,
1216
4
                                                      segment_caches));
1217
4
    }
1218
1219
    // 2. merge duplicate rows and handle insert after delete
1220
8
    if (block->rows() != num_rows) {
1221
0
        num_rows = block->rows();
1222
        // data in block has changed, should re-encode key columns, sequence column
1223
0
        _writer._olap_data_convertor->clear_source_content();
1224
0
        RETURN_IF_ERROR(convert_pk_columns(block, 0, num_rows, key_columns));
1225
0
        RETURN_IF_ERROR(convert_seq_column(block, 0, num_rows, seq_column));
1226
0
    }
1227
8
    RETURN_IF_ERROR(aggregate_for_insert_after_delete(block, num_rows, key_columns,
1228
8
                                                      specified_rowsets, segment_caches));
1229
8
    return Status::OK();
1230
8
}
1231
1232
} // namespace doris