github-actions[bot] commented on code in PR #38216:
URL: https://github.com/apache/doris/pull/38216#discussion_r1686393869
##########
be/src/olap/base_tablet.cpp:
##########
@@ -606,17 +560,19 @@ Status BaseTablet::calc_delete_bitmap(const
BaseTabletSPtr& tablet, RowsetShared
return Status::OK();
}
-Status BaseTablet::calc_segment_delete_bitmap(RowsetSharedPtr rowset,
- const
segment_v2::SegmentSharedPtr& seg,
- const
std::vector<RowsetSharedPtr>& specified_rowsets,
- DeleteBitmapPtr delete_bitmap,
int64_t end_version,
- RowsetWriter* rowset_writer) {
+Status BaseTablet::calc_segment_delete_bitmap(
Review Comment:
warning: function 'calc_segment_delete_bitmap' has cognitive complexity of
86 (threshold 50) [readability-function-cognitive-complexity]
```cpp
Status BaseTablet::calc_segment_delete_bitmap(
^
```
<details>
<summary>Additional context</summary>
**be/src/olap/base_tablet.cpp:572:** +1
```cpp
bool is_partial_update = rowset_writer &&
rowset_writer->is_partial_update();
^
```
**be/src/olap/base_tablet.cpp:575:** +1, including nesting penalty of 0,
nesting level increased to 1
```cpp
if (is_partial_update && rowset_schema->has_sequence_col()) {
^
```
**be/src/olap/base_tablet.cpp:575:** +1
```cpp
if (is_partial_update && rowset_schema->has_sequence_col()) {
^
```
**be/src/olap/base_tablet.cpp:579:** +1
```cpp
rowset_schema->has_sequence_col() &&
^
```
**be/src/olap/base_tablet.cpp:583:** +1, including nesting penalty of 0,
nesting level increased to 1
```cpp
if (rowset_schema->num_variant_columns() > 0) {
^
```
**be/src/olap/base_tablet.cpp:593:** +1, including nesting penalty of 0,
nesting level increased to 1
```cpp
if (has_row_column) {
^
```
**be/src/olap/base_tablet.cpp:606:** +1, including nesting penalty of 0,
nesting level increased to 1
```cpp
RETURN_IF_ERROR(seg->load_pk_index_and_bf()); // We need index blocks to
iterate
^
```
**be/src/common/status.h:618:** expanded from macro 'RETURN_IF_ERROR'
```cpp
do { \
^
```
**be/src/olap/base_tablet.cpp:606:** +2, including nesting penalty of 1,
nesting level increased to 2
```cpp
RETURN_IF_ERROR(seg->load_pk_index_and_bf()); // We need index blocks to
iterate
^
```
**be/src/common/status.h:620:** expanded from macro 'RETURN_IF_ERROR'
```cpp
if (UNLIKELY(!_status_.ok())) { \
^
```
**be/src/olap/base_tablet.cpp:618:** +1, including nesting penalty of 0,
nesting level increased to 1
```cpp
while (remaining > 0) {
^
```
**be/src/olap/base_tablet.cpp:620:** +2, including nesting penalty of 1,
nesting level increased to 2
```cpp
RETURN_IF_ERROR(pk_idx->new_iterator(&iter));
^
```
**be/src/common/status.h:618:** expanded from macro 'RETURN_IF_ERROR'
```cpp
do { \
^
```
**be/src/olap/base_tablet.cpp:620:** +3, including nesting penalty of 2,
nesting level increased to 3
```cpp
RETURN_IF_ERROR(pk_idx->new_iterator(&iter));
^
```
**be/src/common/status.h:620:** expanded from macro 'RETURN_IF_ERROR'
```cpp
if (UNLIKELY(!_status_.ok())) { \
^
```
**be/src/olap/base_tablet.cpp:627:** +2, including nesting penalty of 1,
nesting level increased to 2
```cpp
RETURN_IF_ERROR(iter->seek_at_or_after(&last_key_slice,
&exact_match));
^
```
**be/src/common/status.h:618:** expanded from macro 'RETURN_IF_ERROR'
```cpp
do { \
^
```
**be/src/olap/base_tablet.cpp:627:** +3, including nesting penalty of 2,
nesting level increased to 3
```cpp
RETURN_IF_ERROR(iter->seek_at_or_after(&last_key_slice,
&exact_match));
^
```
**be/src/common/status.h:620:** expanded from macro 'RETURN_IF_ERROR'
```cpp
if (UNLIKELY(!_status_.ok())) { \
^
```
**be/src/olap/base_tablet.cpp:634:** +2, including nesting penalty of 1,
nesting level increased to 2
```cpp
RETURN_IF_ERROR(iter->next_batch(&num_read, index_column));
^
```
**be/src/common/status.h:618:** expanded from macro 'RETURN_IF_ERROR'
```cpp
do { \
^
```
**be/src/olap/base_tablet.cpp:634:** +3, including nesting penalty of 2,
nesting level increased to 3
```cpp
RETURN_IF_ERROR(iter->next_batch(&num_read, index_column));
^
```
**be/src/common/status.h:620:** expanded from macro 'RETURN_IF_ERROR'
```cpp
if (UNLIKELY(!_status_.ok())) { \
^
```
**be/src/olap/base_tablet.cpp:640:** +2, including nesting penalty of 1,
nesting level increased to 2
```cpp
if (num_read == batch_size && num_read != remaining) {
^
```
**be/src/olap/base_tablet.cpp:640:** +1
```cpp
if (num_read == batch_size && num_read != remaining) {
^
```
**be/src/olap/base_tablet.cpp:643:** +2, including nesting penalty of 1,
nesting level increased to 2
```cpp
for (size_t i = 0; i < num_read; i++, row_id++) {
^
```
**be/src/olap/base_tablet.cpp:647:** +3, including nesting penalty of 2,
nesting level increased to 3
```cpp
if (!_tablet_meta->tablet_schema()->cluster_key_idxes().empty())
{
^
```
**be/src/olap/base_tablet.cpp:649:** +4, including nesting penalty of 3,
nesting level increased to 4
```cpp
if (_tablet_meta->tablet_schema()->has_sequence_col()) {
^
```
**be/src/olap/base_tablet.cpp:665:** +4, including nesting penalty of 3,
nesting level increased to 4
```cpp
RETURN_IF_ERROR(rowid_coder->decode_ascending(&rowid_slice,
rowid_length,
^
```
**be/src/common/status.h:618:** expanded from macro 'RETURN_IF_ERROR'
```cpp
do { \
^
```
**be/src/olap/base_tablet.cpp:665:** +5, including nesting penalty of 4,
nesting level increased to 5
```cpp
RETURN_IF_ERROR(rowid_coder->decode_ascending(&rowid_slice,
rowid_length,
^
```
**be/src/common/status.h:620:** expanded from macro 'RETURN_IF_ERROR'
```cpp
if (UNLIKELY(!_status_.ok())) { \
^
```
**be/src/olap/base_tablet.cpp:669:** +3, including nesting penalty of 2,
nesting level increased to 3
```cpp
if (delete_bitmap->contains({rowset_id, seg->id(),
DeleteBitmap::TEMP_VERSION_COMMON},
^
```
**be/src/olap/base_tablet.cpp:682:** +3, including nesting penalty of 2,
nesting level increased to 3
```cpp
if (!expected_st) {
^
```
**be/src/olap/base_tablet.cpp:685:** +3, including nesting penalty of 2,
nesting level increased to 3
```cpp
if (st.is<KEY_NOT_FOUND>()) {
^
```
**be/src/olap/base_tablet.cpp:689:** +3, including nesting penalty of 2,
nesting level increased to 3
```cpp
if (st.is<KEY_ALREADY_EXISTS>() && (!is_partial_update ||
have_input_seq_column)) {
^
```
**be/src/olap/base_tablet.cpp:707:** +3, including nesting penalty of 2,
nesting level increased to 3
```cpp
if (is_partial_update && rowset_writer != nullptr) {
^
```
**be/src/olap/base_tablet.cpp:707:** +1
```cpp
if (is_partial_update && rowset_writer != nullptr) {
^
```
**be/src/olap/base_tablet.cpp:715:** +4, including nesting penalty of 3,
nesting level increased to 4
```cpp
if (indicator_maps) {
^
```
**be/src/olap/base_tablet.cpp:719:** +1, nesting level increased to 4
```cpp
} else {
^
```
**be/src/olap/base_tablet.cpp:753:** +1, including nesting penalty of 0,
nesting level increased to 1
```cpp
if (config::enable_merge_on_write_correctness_check) {
^
```
**be/src/olap/base_tablet.cpp:764:** +1, including nesting penalty of 0,
nesting level increased to 1
```cpp
if (pos > 0) {
^
```
**be/src/olap/base_tablet.cpp:767:** +2, including nesting penalty of 1,
nesting level increased to 2
```cpp
RETURN_IF_ERROR(generate_new_block_for_partial_update(
^
```
**be/src/common/status.h:618:** expanded from macro 'RETURN_IF_ERROR'
```cpp
do { \
^
```
**be/src/olap/base_tablet.cpp:767:** +3, including nesting penalty of 2,
nesting level increased to 3
```cpp
RETURN_IF_ERROR(generate_new_block_for_partial_update(
^
```
**be/src/common/status.h:620:** expanded from macro 'RETURN_IF_ERROR'
```cpp
if (UNLIKELY(!_status_.ok())) { \
^
```
**be/src/olap/base_tablet.cpp:770:** +2, including nesting penalty of 1,
nesting level increased to 2
```cpp
RETURN_IF_ERROR(sort_block(block, ordered_block));
^
```
**be/src/common/status.h:618:** expanded from macro 'RETURN_IF_ERROR'
```cpp
do { \
^
```
**be/src/olap/base_tablet.cpp:770:** +3, including nesting penalty of 2,
nesting level increased to 3
```cpp
RETURN_IF_ERROR(sort_block(block, ordered_block));
^
```
**be/src/common/status.h:620:** expanded from macro 'RETURN_IF_ERROR'
```cpp
if (UNLIKELY(!_status_.ok())) { \
^
```
**be/src/olap/base_tablet.cpp:771:** +2, including nesting penalty of 1,
nesting level increased to 2
```cpp
RETURN_IF_ERROR(rowset_writer->flush_single_block(&ordered_block));
^
```
**be/src/common/status.h:618:** expanded from macro 'RETURN_IF_ERROR'
```cpp
do { \
^
```
**be/src/olap/base_tablet.cpp:771:** +3, including nesting penalty of 2,
nesting level increased to 3
```cpp
RETURN_IF_ERROR(rowset_writer->flush_single_block(&ordered_block));
^
```
**be/src/common/status.h:620:** expanded from macro 'RETURN_IF_ERROR'
```cpp
if (UNLIKELY(!_status_.ok())) { \
^
```
**be/src/olap/base_tablet.cpp:772:** +2, including nesting penalty of 1,
nesting level increased to 2
```cpp
if (new_generated_rows != rowset_writer->num_rows()) {
^
```
</details>
##########
be/src/olap/base_tablet.cpp:
##########
@@ -925,42 +924,197 @@
return Status::OK();
}
+Status BaseTablet::read_columns_by_plan(TabletSchemaSPtr tablet_schema,
+ const std::map<RowsetId,
RowsetSharedPtr>& rsid_to_rowset,
+ const PartialUpdateReadPlan& read_plan,
+ const std::vector<uint32_t>*
cids_full_read,
+ vectorized::Block* block_full_read,
+ std::map<uint32_t, uint32_t>*
full_read_index) {
+ auto full_read_columns = block_full_read->mutate_columns();
+ uint32_t read_idx1 = 0;
+ return std::visit(
+ vectorized::Overload {
+ [&](const RowStoreReadPlan& row_store_read_plan) -> Status
{
+ for (const auto& [rowset_id, segment_read_infos] :
row_store_read_plan) {
+ auto rowset = rsid_to_rowset.at(rowset_id);
+ CHECK(rowset);
+ for (const auto& [segment_id, rows_info] :
segment_read_infos) {
+ std::vector<uint32_t> rids;
+ for (const auto& [id_and_pos, cids] :
rows_info) {
+ // set read index for missing columns
+ rids.emplace_back(id_and_pos.rid);
+ (*full_read_index)[id_and_pos.pos] =
read_idx1++;
+ }
+
+ auto st = fetch_value_through_row_column(
+ rowset, *tablet_schema, segment_id,
rids, rows_info,
+ cids_full_read, nullptr,
block_full_read, nullptr, false);
+ if (!st.ok()) {
+ LOG(WARNING) << "failed to fetch value
through row column";
+ return st;
+ }
+ }
+ }
+ return Status::OK();
+ },
+ [&](const ColumnStoreReadPlan& column_store_read_plan) ->
Status {
+ for (const auto& [rowset_id, segment_read_infos] :
column_store_read_plan) {
+ auto rowset = rsid_to_rowset.at(rowset_id);
+ CHECK(rowset);
+
+ for (const auto& [segment_id, columns_info] :
segment_read_infos) {
+ std::vector<uint32_t> rids;
+ for (auto [rid, pos] :
columns_info.missing_column_rows) {
+ rids.emplace_back(rid);
+ // set read index for missing columns
+ (*full_read_index)[pos] = read_idx1++;
+ }
+
+ // read values for missing columns
+ for (size_t i = 0; i < cids_full_read->size();
++i) {
+ TabletColumn tablet_column =
+
tablet_schema->column(cids_full_read->at(i));
+ auto st = fetch_value_by_rowids(rowset,
segment_id, rids,
+
tablet_column,
+
full_read_columns[i]);
+ if (!st.ok()) {
+ LOG(WARNING) << "failed to fetch value
by rowids";
+ return st;
+ }
+ }
+ }
+ }
+ return Status::OK();
+ }},
+ read_plan);
+}
+
+Status BaseTablet::read_columns_by_plan(
+ TabletSchemaSPtr tablet_schema, const std::map<RowsetId,
RowsetSharedPtr>& rsid_to_rowset,
+ const PartialUpdateReadPlan& read_plan, const std::vector<uint32_t>*
cids_full_read,
+ const std::vector<uint32_t>* cids_point_read, vectorized::Block*
block_full_read,
+ vectorized::Block* block_point_read, std::map<uint32_t, uint32_t>*
full_read_index,
+ std::map<uint32_t, std::map<uint32_t, uint32_t>>* point_read_index) {
+ auto full_read_columns = block_full_read->mutate_columns();
+ auto point_read_columns = block_point_read->mutate_columns();
+
+ uint32_t read_idx1 = 0;
+ std::map<uint32_t, uint32_t> read_idx2;
+ for (uint32_t cid : *cids_point_read) {
+ read_idx2[cid] = 0;
+ }
+
+ return std::visit(
+ vectorized::Overload {
+ [&](const RowStoreReadPlan& row_store_read_plan) -> Status
{
+ for (const auto& [rowset_id, segment_read_infos] :
row_store_read_plan) {
+ auto rowset = rsid_to_rowset.at(rowset_id);
+ CHECK(rowset);
+ for (const auto& [segment_id, rows_info] :
segment_read_infos) {
+ std::vector<uint32_t> rids;
+ for (const auto& [id_and_pos, cids] :
rows_info) {
+ // set read index for missing columns
+ rids.emplace_back(id_and_pos.rid);
+ (*full_read_index)[id_and_pos.pos] =
read_idx1++;
+ for (const auto cid : cids) {
+ // set read index for partial update
columns
+
(*point_read_index)[cid][id_and_pos.pos] = read_idx2[cid]++;
+ }
+ }
+
+ auto st = fetch_value_through_row_column(
+ rowset, *tablet_schema, segment_id,
rids, rows_info,
+ cids_full_read, cids_point_read,
block_full_read,
+ block_point_read, true);
+ if (!st.ok()) {
+ LOG(WARNING) << "failed to fetch value
through row column";
+ return st;
+ }
+ }
+ }
+ return Status::OK();
+ },
+ [&](const ColumnStoreReadPlan& column_store_read_plan) ->
Status {
+ for (const auto& [rowset_id, segment_read_infos] :
column_store_read_plan) {
+ auto rowset = rsid_to_rowset.at(rowset_id);
+ CHECK(rowset);
+
+ for (const auto& [segment_id, columns_info] :
segment_read_infos) {
+ std::vector<uint32_t> rids;
+ for (auto [rid, pos] :
columns_info.missing_column_rows) {
+ rids.emplace_back(rid);
+ // set read index for missing columns
+ (*full_read_index)[pos] = read_idx1++;
+ }
+
+ // read values for missing columns
+ for (size_t i = 0; i < cids_full_read->size();
++i) {
+ TabletColumn tablet_column =
+
tablet_schema->column(cids_full_read->at(i));
+ auto st = fetch_value_by_rowids(rowset,
segment_id, rids,
+
tablet_column,
+
full_read_columns[i]);
+ if (!st.ok()) {
+ LOG(WARNING) << "failed to fetch value
by rowids";
+ return st;
+ }
+ }
+ // read values for cells with indicator values
in including columns
+ for (size_t i = 0; i <
cids_point_read->size(); i++) {
+ const auto& rows_info =
columns_info.partial_update_rows;
+ uint32_t cid = cids_point_read->at(i);
+ if (!rows_info.empty() &&
rows_info.contains(cid)) {
+ std::vector<uint32_t> rids;
+ for (auto [rid, pos] :
rows_info.at(cid)) {
+ rids.emplace_back(rid);
+ // set read index for partial
update columns
+ (*point_read_index)[cid][pos] =
read_idx2[cid]++;
+ }
+
+ TabletColumn tablet_column =
tablet_schema->column(cid);
+ auto st =
fetch_value_by_rowids(rowset, segment_id, rids,
+
tablet_column,
+
point_read_columns[i]);
+ if (!st.ok()) {
+ LOG(WARNING) << "failed to fetch
value by rowids";
+ return st;
+ }
+ }
+ }
+ }
+ }
+ return Status::OK();
+ }},
+ read_plan);
+}
+
Status BaseTablet::generate_new_block_for_partial_update(
Review Comment:
warning: function 'generate_new_block_for_partial_update' has cognitive
complexity of 96 (threshold 50) [readability-function-cognitive-complexity]
```cpp
Status BaseTablet::generate_new_block_for_partial_update(
^
```
<details>
<summary>Additional context</summary>
**be/src/olap/base_tablet.cpp:1107:** +1, including nesting penalty of 0,
nesting level increased to 1
```cpp
if (const vectorized::ColumnWithTypeAndName* delete_sign_column =
^
```
**be/src/olap/base_tablet.cpp:1118:** +1, including nesting penalty of 0,
nesting level increased to 1
```cpp
if (delete_sign_column_data != nullptr) {
^
```
**be/src/olap/base_tablet.cpp:1119:** +2, including nesting penalty of 1,
nesting level increased to 2
```cpp
for (auto i = 0; i < missing_cids.size(); ++i) {
^
```
**be/src/olap/base_tablet.cpp:1121:** +3, including nesting penalty of 2,
nesting level increased to 3
```cpp
if (column.has_default_value()) {
^
```
**be/src/olap/base_tablet.cpp:1125:** +4, including nesting penalty of 3,
nesting level increased to 4
```cpp
RETURN_IF_ERROR(old_full_read_block.get_by_position(i).type->from_string(
^
```
**be/src/common/status.h:618:** expanded from macro 'RETURN_IF_ERROR'
```cpp
do { \
^
```
**be/src/olap/base_tablet.cpp:1125:** +5, including nesting penalty of 4,
nesting level increased to 5
```cpp
RETURN_IF_ERROR(old_full_read_block.get_by_position(i).type->from_string(
^
```
**be/src/common/status.h:620:** expanded from macro 'RETURN_IF_ERROR'
```cpp
if (UNLIKELY(!_status_.ok())) { \
^
```
**be/src/olap/base_tablet.cpp:1131:** +1, including nesting penalty of 0,
nesting level increased to 1
```cpp
if (!indicator_maps) {
^
```
**be/src/olap/base_tablet.cpp:1136:** +2, including nesting penalty of 1,
nesting level increased to 2
```cpp
RETURN_IF_ERROR(read_columns_by_plan(rowset_schema, rsid_to_rowset,
read_plan_ori,
^
```
**be/src/common/status.h:618:** expanded from macro 'RETURN_IF_ERROR'
```cpp
do { \
^
```
**be/src/olap/base_tablet.cpp:1136:** +3, including nesting penalty of 2,
nesting level increased to 3
```cpp
RETURN_IF_ERROR(read_columns_by_plan(rowset_schema, rsid_to_rowset,
read_plan_ori,
^
```
**be/src/common/status.h:620:** expanded from macro 'RETURN_IF_ERROR'
```cpp
if (UNLIKELY(!_status_.ok())) { \
^
```
**be/src/olap/base_tablet.cpp:1140:** +2, including nesting penalty of 1,
nesting level increased to 2
```cpp
RETURN_IF_ERROR(read_columns_by_plan(rowset_schema, rsid_to_rowset,
read_plan_update,
^
```
**be/src/common/status.h:618:** expanded from macro 'RETURN_IF_ERROR'
```cpp
do { \
^
```
**be/src/olap/base_tablet.cpp:1140:** +3, including nesting penalty of 2,
nesting level increased to 3
```cpp
RETURN_IF_ERROR(read_columns_by_plan(rowset_schema, rsid_to_rowset,
read_plan_update,
^
```
**be/src/common/status.h:620:** expanded from macro 'RETURN_IF_ERROR'
```cpp
if (UNLIKELY(!_status_.ok())) { \
^
```
**be/src/olap/base_tablet.cpp:1146:** +2, including nesting penalty of 1,
nesting level increased to 2
```cpp
for (auto i = 0; i < missing_cids.size(); ++i) {
^
```
**be/src/olap/base_tablet.cpp:1148:** +3, including nesting penalty of 2,
nesting level increased to 3
```cpp
for (auto idx = 0; idx < read_index_old.size(); ++idx) {
^
```
**be/src/olap/base_tablet.cpp:1157:** +4, including nesting penalty of 3,
nesting level increased to 4
```cpp
if (delete_sign_column_data != nullptr &&
^
```
**be/src/olap/base_tablet.cpp:1157:** +1
```cpp
if (delete_sign_column_data != nullptr &&
^
```
**be/src/olap/base_tablet.cpp:1159:** +5, including nesting penalty of 4,
nesting level increased to 5
```cpp
if (rs_column.has_default_value()) {
^
```
**be/src/olap/base_tablet.cpp:1161:** +1, nesting level increased to 5
```cpp
} else if (rs_column.is_nullable()) {
^
```
**be/src/olap/base_tablet.cpp:1164:** +1, nesting level increased to 5
```cpp
} else {
^
```
**be/src/olap/base_tablet.cpp:1174:** +2, including nesting penalty of 1,
nesting level increased to 2
```cpp
for (auto i = 0; i < update_cids.size(); ++i) {
^
```
**be/src/olap/base_tablet.cpp:1175:** +3, including nesting penalty of 2,
nesting level increased to 3
```cpp
for (auto idx = 0; idx < read_index_update.size(); ++idx) {
^
```
**be/src/olap/base_tablet.cpp:1183:** +1, nesting level increased to 1
```cpp
} else {
^
```
**be/src/olap/base_tablet.cpp:1201:** +2, including nesting penalty of 1,
nesting level increased to 2
```cpp
RETURN_IF_ERROR(read_columns_by_plan(rowset_schema, rsid_to_rowset,
read_plan_ori,
^
```
**be/src/common/status.h:618:** expanded from macro 'RETURN_IF_ERROR'
```cpp
do { \
^
```
**be/src/olap/base_tablet.cpp:1201:** +3, including nesting penalty of 2,
nesting level increased to 3
```cpp
RETURN_IF_ERROR(read_columns_by_plan(rowset_schema, rsid_to_rowset,
read_plan_ori,
^
```
**be/src/common/status.h:620:** expanded from macro 'RETURN_IF_ERROR'
```cpp
if (UNLIKELY(!_status_.ok())) { \
^
```
**be/src/olap/base_tablet.cpp:1208:** +2, including nesting penalty of 1,
nesting level increased to 2
```cpp
RETURN_IF_ERROR(read_columns_by_plan(rowset_schema, rsid_to_rowset,
read_plan_update,
^
```
**be/src/common/status.h:618:** expanded from macro 'RETURN_IF_ERROR'
```cpp
do { \
^
```
**be/src/olap/base_tablet.cpp:1208:** +3, including nesting penalty of 2,
nesting level increased to 3
```cpp
RETURN_IF_ERROR(read_columns_by_plan(rowset_schema, rsid_to_rowset,
read_plan_update,
^
```
**be/src/common/status.h:620:** expanded from macro 'RETURN_IF_ERROR'
```cpp
if (UNLIKELY(!_status_.ok())) { \
^
```
**be/src/olap/base_tablet.cpp:1213:** +2, including nesting penalty of 1,
nesting level increased to 2
```cpp
for (size_t i = 0; i < missing_cids.size(); ++i) {
^
```
**be/src/olap/base_tablet.cpp:1215:** +3, including nesting penalty of 2,
nesting level increased to 3
```cpp
for (auto idx = 0; idx < full_read_index_old.size(); ++idx) {
^
```
**be/src/olap/base_tablet.cpp:1224:** +4, including nesting penalty of 3,
nesting level increased to 4
```cpp
if (delete_sign_column_data != nullptr &&
^
```
**be/src/olap/base_tablet.cpp:1224:** +1
```cpp
if (delete_sign_column_data != nullptr &&
^
```
**be/src/olap/base_tablet.cpp:1226:** +5, including nesting penalty of 4,
nesting level increased to 5
```cpp
if (rs_column.has_default_value()) {
^
```
**be/src/olap/base_tablet.cpp:1228:** +1, nesting level increased to 5
```cpp
} else if (rs_column.is_nullable()) {
^
```
**be/src/olap/base_tablet.cpp:1231:** +1, nesting level increased to 5
```cpp
} else {
^
```
**be/src/olap/base_tablet.cpp:1242:** +2, including nesting penalty of 1,
nesting level increased to 2
```cpp
for (size_t i = 0; i < update_cids.size(); i++) {
^
```
**be/src/olap/base_tablet.cpp:1244:** +3, including nesting penalty of 2,
nesting level increased to 3
```cpp
if (!cids_point_read.contains(cid)) {
^
```
**be/src/olap/base_tablet.cpp:1245:** +4, including nesting penalty of 3,
nesting level increased to 4
```cpp
for (auto idx = 0; idx < full_read_index_old.size(); ++idx) {
^
```
**be/src/olap/base_tablet.cpp:1252:** +2, including nesting penalty of 1,
nesting level increased to 2
```cpp
for (size_t i = 0; i < point_read_cids.size(); i++) {
^
```
**be/src/olap/base_tablet.cpp:1254:** +3, including nesting penalty of 2,
nesting level increased to 3
```cpp
for (uint32_t idx = 0; i < full_read_index_old.size(); i++) {
^
```
**be/src/olap/base_tablet.cpp:1255:** +4, including nesting penalty of 3,
nesting level increased to 4
```cpp
if (point_read_index_old[cid].contains(idx)) {
^
```
**be/src/olap/base_tablet.cpp:1259:** +1, nesting level increased to 4
```cpp
} else {
^
```
</details>
##########
be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:
##########
@@ -552,104 +617,131 @@
return Status::OK();
}
-Status VerticalSegmentWriter::_fill_missing_columns(
- vectorized::MutableColumns& mutable_full_columns,
- const std::vector<bool>& use_default_or_null_flag, bool
has_default_or_nullable,
- const size_t& segment_start_pos, const vectorized::Block* block) {
- // create old value columns
- const auto& missing_cids =
_opts.rowset_ctx->partial_update_info->missing_cids;
- auto old_value_block = _tablet_schema->create_block_by_cids(missing_cids);
- CHECK_EQ(missing_cids.size(), old_value_block.columns());
- auto mutable_old_columns = old_value_block.mutate_columns();
- bool has_row_column = _tablet_schema->has_row_store_for_all_columns();
- // record real pos, key is input line num, value is old_block line num
- std::map<uint32_t, uint32_t> read_index;
- size_t read_idx = 0;
- for (auto rs_it : _rssid_to_rid) {
- for (auto seg_it : rs_it.second) {
- auto rowset = _rsid_to_rowset[rs_it.first];
- CHECK(rowset);
- std::vector<uint32_t> rids;
- for (auto id_and_pos : seg_it.second) {
- rids.emplace_back(id_and_pos.rid);
- read_index[id_and_pos.pos] = read_idx++;
- }
- if (has_row_column) {
- auto st = _tablet->fetch_value_through_row_column(
- rowset, *_tablet_schema, seg_it.first, rids,
missing_cids, old_value_block);
- if (!st.ok()) {
- LOG(WARNING) << "failed to fetch value through row column";
- return st;
- }
- continue;
- }
- for (size_t cid = 0; cid < mutable_old_columns.size(); ++cid) {
- TabletColumn tablet_column =
_tablet_schema->column(missing_cids[cid]);
- auto st = _tablet->fetch_value_by_rowids(rowset, seg_it.first,
rids, tablet_column,
-
mutable_old_columns[cid]);
- // set read value to output block
- if (!st.ok()) {
- LOG(WARNING) << "failed to fetch value by rowids";
- return st;
- }
+void VerticalSegmentWriter::_calc_indicator_maps(
+ uint32_t row_pos, uint32_t num_rows, const IndicatorMapsVertical&
indicator_maps_vertical) {
+ _indicator_maps = std::make_shared<std::map<uint32_t,
std::vector<uint32_t>>>();
+ for (auto [cid, indicator_map] : indicator_maps_vertical) {
+ for (uint32_t pos = row_pos; pos < row_pos + num_rows; pos++) {
+ if (indicator_map != nullptr && indicator_map[pos] != 0) {
+ (*_indicator_maps)[pos].emplace_back(cid);
}
}
}
+}
+
+// Consider a merge-on-write unique table with colums [k1, k2, v1, v1, v2, v3,
v4, v5] where k1, k2 are key columns
+// and v1, v2, v3, v4, v5 are value columns. The table has the following data:
+// k1|k2|v1|v2|v3|v4|v5
+// 1 |1 |1 |1 |1 |1 |1
+// 2 |2 |2 |2 |2 |2 |2
+// 3 |3 |3 |3 |3 |3 |3
+// 4 |4 |4 |4 |4 |4 |4
+// 5 |5 |5 |5 |5 |5 |5
+// The user inserts the following data for partial update. Charactor `?` means
that the cell is filled
+// with indicator value(currently we use null as indicator value).
+// row_num k1|k2|v1|v2|v3
+// 1 1 |1 |10|10|10
+// 2 2 |2 |? |20|20
+// 3 3 |3 |30|30|?
+// 4 4 |4 |40|40|40
+// 5 5 |5 |50|? |50
+// Here, full_columns = [k1, k2, v1, v2, v3, v4, v5]
+// old_full_read_columns = [v4, v5], the old values from the previous
rows will be read into these columns.
+// old_point_read_columns = [v1, v2, v3], the old values from the
previous rows will be read into these columns if the correspoding columns in
the input block has cell with indicator value.
+// Becase the column is immutable, filled_including_value_columns will store
the data merged from
+// the original input block and old_point_read_columns. After the insertion,
the data in the table will be:
+// k1|k2|v1|v2|v3|v4|v5
+// 1 |1 |10|10|10|1 |1
+// 2 |2 |2 |20|20|2 |2
+// 3 |3 |30|30|3 |3 |3
+// 4 |4 |40|40|40|4 |4
+// 5 |5 |50|5 |50|5 |5
+Status VerticalSegmentWriter::_fill_missing_columns(
Review Comment:
warning: function '_fill_missing_columns' exceeds recommended
size/complexity thresholds [readability-function-size]
```cpp
Status VerticalSegmentWriter::_fill_missing_columns(
^
```
<details>
<summary>Additional context</summary>
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:658:** 132 lines
including whitespace and comments (threshold 80)
```cpp
Status VerticalSegmentWriter::_fill_missing_columns(
^
```
</details>
##########
be/src/olap/base_tablet.cpp:
##########
@@ -606,17 +560,19 @@
return Status::OK();
}
-Status BaseTablet::calc_segment_delete_bitmap(RowsetSharedPtr rowset,
- const
segment_v2::SegmentSharedPtr& seg,
- const
std::vector<RowsetSharedPtr>& specified_rowsets,
- DeleteBitmapPtr delete_bitmap,
int64_t end_version,
- RowsetWriter* rowset_writer) {
+Status BaseTablet::calc_segment_delete_bitmap(
Review Comment:
warning: function 'calc_segment_delete_bitmap' exceeds recommended
size/complexity thresholds [readability-function-size]
```cpp
Status BaseTablet::calc_segment_delete_bitmap(
^
```
<details>
<summary>Additional context</summary>
**be/src/olap/base_tablet.cpp:562:** 225 lines including whitespace and
comments (threshold 80)
```cpp
Status BaseTablet::calc_segment_delete_bitmap(
^
```
</details>
##########
be/src/olap/base_tablet.cpp:
##########
@@ -925,42 +924,197 @@
return Status::OK();
}
+Status BaseTablet::read_columns_by_plan(TabletSchemaSPtr tablet_schema,
+ const std::map<RowsetId,
RowsetSharedPtr>& rsid_to_rowset,
+ const PartialUpdateReadPlan& read_plan,
+ const std::vector<uint32_t>*
cids_full_read,
+ vectorized::Block* block_full_read,
+ std::map<uint32_t, uint32_t>*
full_read_index) {
+ auto full_read_columns = block_full_read->mutate_columns();
+ uint32_t read_idx1 = 0;
+ return std::visit(
+ vectorized::Overload {
+ [&](const RowStoreReadPlan& row_store_read_plan) -> Status
{
+ for (const auto& [rowset_id, segment_read_infos] :
row_store_read_plan) {
+ auto rowset = rsid_to_rowset.at(rowset_id);
+ CHECK(rowset);
+ for (const auto& [segment_id, rows_info] :
segment_read_infos) {
+ std::vector<uint32_t> rids;
+ for (const auto& [id_and_pos, cids] :
rows_info) {
+ // set read index for missing columns
+ rids.emplace_back(id_and_pos.rid);
+ (*full_read_index)[id_and_pos.pos] =
read_idx1++;
+ }
+
+ auto st = fetch_value_through_row_column(
+ rowset, *tablet_schema, segment_id,
rids, rows_info,
+ cids_full_read, nullptr,
block_full_read, nullptr, false);
+ if (!st.ok()) {
+ LOG(WARNING) << "failed to fetch value
through row column";
+ return st;
+ }
+ }
+ }
+ return Status::OK();
+ },
+ [&](const ColumnStoreReadPlan& column_store_read_plan) ->
Status {
+ for (const auto& [rowset_id, segment_read_infos] :
column_store_read_plan) {
+ auto rowset = rsid_to_rowset.at(rowset_id);
+ CHECK(rowset);
+
+ for (const auto& [segment_id, columns_info] :
segment_read_infos) {
+ std::vector<uint32_t> rids;
+ for (auto [rid, pos] :
columns_info.missing_column_rows) {
+ rids.emplace_back(rid);
+ // set read index for missing columns
+ (*full_read_index)[pos] = read_idx1++;
+ }
+
+ // read values for missing columns
+ for (size_t i = 0; i < cids_full_read->size();
++i) {
+ TabletColumn tablet_column =
+
tablet_schema->column(cids_full_read->at(i));
+ auto st = fetch_value_by_rowids(rowset,
segment_id, rids,
+
tablet_column,
+
full_read_columns[i]);
+ if (!st.ok()) {
+ LOG(WARNING) << "failed to fetch value
by rowids";
+ return st;
+ }
+ }
+ }
+ }
+ return Status::OK();
+ }},
+ read_plan);
+}
+
+Status BaseTablet::read_columns_by_plan(
Review Comment:
warning: function 'read_columns_by_plan' exceeds recommended size/complexity
thresholds [readability-function-size]
```cpp
Status BaseTablet::read_columns_by_plan(
^
```
<details>
<summary>Additional context</summary>
**be/src/olap/base_tablet.cpp:991:** 92 lines including whitespace and
comments (threshold 80)
```cpp
Status BaseTablet::read_columns_by_plan(
^
```
</details>
##########
be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:
##########
@@ -552,104 +617,131 @@ Status
VerticalSegmentWriter::_append_block_with_partial_content(RowsInBlock& da
return Status::OK();
}
-Status VerticalSegmentWriter::_fill_missing_columns(
- vectorized::MutableColumns& mutable_full_columns,
- const std::vector<bool>& use_default_or_null_flag, bool
has_default_or_nullable,
- const size_t& segment_start_pos, const vectorized::Block* block) {
- // create old value columns
- const auto& missing_cids =
_opts.rowset_ctx->partial_update_info->missing_cids;
- auto old_value_block = _tablet_schema->create_block_by_cids(missing_cids);
- CHECK_EQ(missing_cids.size(), old_value_block.columns());
- auto mutable_old_columns = old_value_block.mutate_columns();
- bool has_row_column = _tablet_schema->has_row_store_for_all_columns();
- // record real pos, key is input line num, value is old_block line num
- std::map<uint32_t, uint32_t> read_index;
- size_t read_idx = 0;
- for (auto rs_it : _rssid_to_rid) {
- for (auto seg_it : rs_it.second) {
- auto rowset = _rsid_to_rowset[rs_it.first];
- CHECK(rowset);
- std::vector<uint32_t> rids;
- for (auto id_and_pos : seg_it.second) {
- rids.emplace_back(id_and_pos.rid);
- read_index[id_and_pos.pos] = read_idx++;
- }
- if (has_row_column) {
- auto st = _tablet->fetch_value_through_row_column(
- rowset, *_tablet_schema, seg_it.first, rids,
missing_cids, old_value_block);
- if (!st.ok()) {
- LOG(WARNING) << "failed to fetch value through row column";
- return st;
- }
- continue;
- }
- for (size_t cid = 0; cid < mutable_old_columns.size(); ++cid) {
- TabletColumn tablet_column =
_tablet_schema->column(missing_cids[cid]);
- auto st = _tablet->fetch_value_by_rowids(rowset, seg_it.first,
rids, tablet_column,
-
mutable_old_columns[cid]);
- // set read value to output block
- if (!st.ok()) {
- LOG(WARNING) << "failed to fetch value by rowids";
- return st;
- }
+void VerticalSegmentWriter::_calc_indicator_maps(
+ uint32_t row_pos, uint32_t num_rows, const IndicatorMapsVertical&
indicator_maps_vertical) {
+ _indicator_maps = std::make_shared<std::map<uint32_t,
std::vector<uint32_t>>>();
+ for (auto [cid, indicator_map] : indicator_maps_vertical) {
+ for (uint32_t pos = row_pos; pos < row_pos + num_rows; pos++) {
+ if (indicator_map != nullptr && indicator_map[pos] != 0) {
+ (*_indicator_maps)[pos].emplace_back(cid);
}
}
}
+}
+
+// Consider a merge-on-write unique table with colums [k1, k2, v1, v1, v2, v3,
v4, v5] where k1, k2 are key columns
+// and v1, v2, v3, v4, v5 are value columns. The table has the following data:
+// k1|k2|v1|v2|v3|v4|v5
+// 1 |1 |1 |1 |1 |1 |1
+// 2 |2 |2 |2 |2 |2 |2
+// 3 |3 |3 |3 |3 |3 |3
+// 4 |4 |4 |4 |4 |4 |4
+// 5 |5 |5 |5 |5 |5 |5
+// The user inserts the following data for partial update. Charactor `?` means
that the cell is filled
+// with indicator value(currently we use null as indicator value).
+// row_num k1|k2|v1|v2|v3
+// 1 1 |1 |10|10|10
+// 2 2 |2 |? |20|20
+// 3 3 |3 |30|30|?
+// 4 4 |4 |40|40|40
+// 5 5 |5 |50|? |50
+// Here, full_columns = [k1, k2, v1, v2, v3, v4, v5]
+// old_full_read_columns = [v4, v5], the old values from the previous
rows will be read into these columns.
+// old_point_read_columns = [v1, v2, v3], the old values from the
previous rows will be read into these columns if the correspoding columns in
the input block has cell with indicator value.
+// Becase the column is immutable, filled_including_value_columns will store
the data merged from
+// the original input block and old_point_read_columns. After the insertion,
the data in the table will be:
+// k1|k2|v1|v2|v3|v4|v5
+// 1 |1 |10|10|10|1 |1
+// 2 |2 |2 |20|20|2 |2
+// 3 |3 |30|30|3 |3 |3
+// 4 |4 |40|40|40|4 |4
+// 5 |5 |50|5 |50|5 |5
+Status VerticalSegmentWriter::_fill_missing_columns(
Review Comment:
warning: function '_fill_missing_columns' has cognitive complexity of 68
(threshold 50) [readability-function-cognitive-complexity]
```cpp
Status VerticalSegmentWriter::_fill_missing_columns(
^
```
<details>
<summary>Additional context</summary>
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:678:** +1,
including nesting penalty of 0, nesting level increased to 1
```cpp
if (is_unique_key_replace_if_not_null) {
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:679:** +2,
including nesting penalty of 1, nesting level increased to 2
```cpp
RETURN_IF_ERROR(_tablet->read_columns_by_plan(
^
```
**be/src/common/status.h:618:** expanded from macro 'RETURN_IF_ERROR'
```cpp
do { \
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:679:** +3,
including nesting penalty of 2, nesting level increased to 3
```cpp
RETURN_IF_ERROR(_tablet->read_columns_by_plan(
^
```
**be/src/common/status.h:620:** expanded from macro 'RETURN_IF_ERROR'
```cpp
if (UNLIKELY(!_status_.ok())) { \
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:683:** +1,
nesting level increased to 1
```cpp
} else {
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:684:** +2,
including nesting penalty of 1, nesting level increased to 2
```cpp
RETURN_IF_ERROR(_tablet->read_columns_by_plan(_tablet_schema,
_rsid_to_rowset, read_plan,
^
```
**be/src/common/status.h:618:** expanded from macro 'RETURN_IF_ERROR'
```cpp
do { \
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:684:** +3,
including nesting penalty of 2, nesting level increased to 3
```cpp
RETURN_IF_ERROR(_tablet->read_columns_by_plan(_tablet_schema,
_rsid_to_rowset, read_plan,
^
```
**be/src/common/status.h:620:** expanded from macro 'RETURN_IF_ERROR'
```cpp
if (UNLIKELY(!_status_.ok())) { \
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:694:** +1,
including nesting penalty of 0, nesting level increased to 1
```cpp
if (const vectorized::ColumnWithTypeAndName* delete_sign_column =
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:702:** +1,
including nesting penalty of 0, nesting level increased to 1
```cpp
if (has_default_or_nullable || delete_sign_column_data != nullptr) {
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:702:** +1
```cpp
if (has_default_or_nullable || delete_sign_column_data != nullptr) {
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:703:** +2,
including nesting penalty of 1, nesting level increased to 2
```cpp
for (auto i = 0; i < cids_full_read.size(); ++i) {
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:705:** +3,
including nesting penalty of 2, nesting level increased to 3
```cpp
if (column.has_default_value()) {
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:710:** +4,
including nesting penalty of 3, nesting level increased to 4
```cpp
RETURN_IF_ERROR(old_full_read_block.get_by_position(i).type->from_string(
^
```
**be/src/common/status.h:618:** expanded from macro 'RETURN_IF_ERROR'
```cpp
do { \
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:710:** +5,
including nesting penalty of 4, nesting level increased to 5
```cpp
RETURN_IF_ERROR(old_full_read_block.get_by_position(i).type->from_string(
^
```
**be/src/common/status.h:620:** expanded from macro 'RETURN_IF_ERROR'
```cpp
if (UNLIKELY(!_status_.ok())) { \
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:717:** +1,
including nesting penalty of 0, nesting level increased to 1
```cpp
for (auto idx = 0; idx < use_default_or_null_flag.size(); idx++) {
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:725:** +2,
including nesting penalty of 1, nesting level increased to 2
```cpp
if (use_default_or_null_flag[idx] || (delete_sign_column_data !=
nullptr &&
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:725:** +1
```cpp
if (use_default_or_null_flag[idx] || (delete_sign_column_data !=
nullptr &&
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:725:** +1
```cpp
if (use_default_or_null_flag[idx] || (delete_sign_column_data !=
nullptr &&
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:727:** +3,
including nesting penalty of 2, nesting level increased to 3
```cpp
for (auto i = 0; i < cids_full_read.size(); ++i) {
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:732:** +4,
including nesting penalty of 3, nesting level increased to 4
```cpp
if (tablet_column.has_default_value()) {
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:734:** +1,
nesting level increased to 4
```cpp
} else if (tablet_column.is_nullable()) {
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:738:** +1,
nesting level increased to 4
```cpp
} else if (_tablet_schema->auto_increment_column() ==
tablet_column.name()) {
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:739:** nesting
level increased to 5
```cpp
const auto& column = *DORIS_TRY(
^
```
**be/src/common/status.h:700:** expanded from macro 'DORIS_TRY'
```cpp
({ \
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:739:** +6,
including nesting penalty of 5, nesting level increased to 6
```cpp
const auto& column = *DORIS_TRY(
^
```
**be/src/common/status.h:703:** expanded from macro 'DORIS_TRY'
```cpp
if (!res.has_value()) [[unlikely]] { \
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:749:** +1,
nesting level increased to 4
```cpp
} else {
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:761:** +2,
including nesting penalty of 1, nesting level increased to 2
```cpp
for (auto i = 0; i < cids_full_read.size(); ++i) {
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:763:** +3,
including nesting penalty of 2, nesting level increased to 3
```cpp
if (full_block->get_by_position(cid).name !=
BeConsts::ROW_STORE_COL) {
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:770:** +2,
including nesting penalty of 1, nesting level increased to 2
```cpp
if (is_unique_key_replace_if_not_null) {
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:771:** +3,
including nesting penalty of 2, nesting level increased to 3
```cpp
for (size_t i = 0; i < cids_point_read.size(); i++) {
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:773:** +4,
including nesting penalty of 3, nesting level increased to 4
```cpp
if (parital_update_cols_read_index[cid].contains(idx +
segment_start_pos)) {
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:779:** +1,
nesting level increased to 4
```cpp
} else {
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:787:** +1,
including nesting penalty of 0, nesting level increased to 1
```cpp
if (is_unique_key_replace_if_not_null) {
^
```
**be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp:788:** +2,
including nesting penalty of 1, nesting level increased to 2
```cpp
for (size_t i = 0; i < cids_point_read.size(); i++) {
^
```
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]