Coverage Report

Created: 2026-08-14 23:31

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