Coverage Report

Created: 2026-07-24 17:30

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