Coverage Report

Created: 2026-07-29 20:27

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