/** * Copyright (c) 2021 OceanBase * OceanBase CE is licensed under Mulan PubL v2. * You can use this software according to the terms and conditions of the Mulan PubL v2. * You may obtain a copy of Mulan PubL v2 at: * http://license.coscl.org.cn/MulanPubL-2.0 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. * See the Mulan PubL v2 for more details. */ #define USING_LOG_PREFIX STORAGE_COMPACTION #include "ob_partition_merge_fuser.h" #include "ob_tablet_merge_ctx.h" #include "sql/ob_sql_utils.h" #include "storage/column_store/ob_column_oriented_merge_fuser.h" namespace oceanbase { using namespace share::schema; using namespace common; using namespace memtable; using namespace storage; using namespace blocksstable; namespace compaction { /* *ObMergeFuser */ int ObMergeFuser::base_init(const bool is_fuse_row_flag) { int ret = OB_SUCCESS; is_fuse_row_flag_ = is_fuse_row_flag; if (OB_FAIL(result_row_.init(allocator_, column_cnt_))) { STORAGE_LOG(WARN, "Failed to init datum row", K(ret)); } else if (OB_FAIL(nop_pos_.init(allocator_, column_cnt_))) { STORAGE_LOG(WARN, "Failed to init nop pos", K(ret), K(column_cnt_)); } return ret; } void ObMergeFuser::clean_nop_pos_and_result_row() { nop_pos_.reset(); result_row_.row_flag_.reset(); result_row_.row_flag_.set_flag(ObDmlFlag::DF_NOT_EXIST); result_row_.trans_id_.reset(); result_row_.mvcc_row_flag_.reset(); for (int64_t i = 0; i < result_row_.count_; i++) { result_row_.storage_datums_[i].set_nop(); } } int ObMergeFuser::add_fuse_row(const blocksstable::ObDatumRow &row, bool &final_result) { int ret = OB_SUCCESS; if (OB_UNLIKELY(!is_inited_)) { ret = OB_NOT_INIT; STORAGE_LOG(WARN, "ObMergeFuser is not inited", K(ret), K(is_inited_)); } else if (OB_FAIL(storage::ObRowFuse::fuse_row(row, result_row_, nop_pos_, final_result))) { STORAGE_LOG(WARN, "Failed to fuse row", K(ret)); } else if (result_row_.count_ != column_cnt_) { ret = OB_ERR_UNEXPECTED; STORAGE_LOG(WARN, "row count is not valid", K(ret), K(row), K(result_row_), K(column_cnt_)); } else if (is_fuse_row_flag_) { result_row_.row_flag_.fuse_flag(row.row_flag_); } return ret; } int ObMergeFuser::preprocess_fuse_row(const blocksstable::ObDatumRow &row, bool &is_need_fuse) { UNUSED(row); int ret = OB_SUCCESS; clean_nop_pos_and_result_row(); is_need_fuse = true; return ret; } int ObMergeFuser::end_fuse_row(const storage::ObNopPos &nop_pos, blocksstable::ObDatumRow &result_row) { if (result_row.row_flag_.is_exist_without_delete()) { result_row.row_flag_.reset(); result_row.row_flag_.set_flag(ObDmlFlag::DF_INSERT); } return OB_SUCCESS; } int ObMergeFuser::set_multi_version_flag(const ObMultiVersionRowFlag &row_flag) { int ret = OB_SUCCESS; if (IS_NOT_INIT) { ret = OB_NOT_INIT; STORAGE_LOG(WARN, "ObMergeFuser is not inited", K(ret)); } else { result_row_.set_multi_version_flag(row_flag); } return ret; } int ObMergeFuser::fuse_row(MERGE_ITER_ARRAY ¯o_row_iters) { int ret = OB_SUCCESS; bool is_need_fuse = false; const int64_t macro_row_iters_cnt = macro_row_iters.count(); if (OB_UNLIKELY(!is_inited())) { ret = OB_NOT_INIT; STORAGE_LOG(WARN, "ObMergeFuser not init", K(ret)); } else if (OB_UNLIKELY(macro_row_iters_cnt <= 0)) { ret = OB_INVALID_ARGUMENT; STORAGE_LOG(WARN, "Invalid macro row iters to fuse row", K(ret), K(macro_row_iters)); } else if (OB_ISNULL(macro_row_iters.at(0)) || OB_ISNULL(macro_row_iters.at(0)->get_curr_row())) { ret = OB_INVALID_ARGUMENT; STORAGE_LOG(WARN, "Invalid macro row iters to fuse row", K(ret), K(macro_row_iters)); } else if (OB_FAIL(preprocess_fuse_row(*macro_row_iters.at(0)->get_curr_row(), is_need_fuse))) { STORAGE_LOG(WARN, "failed to preprocess_fuse_row", K(ret)); } else if (!is_need_fuse && macro_row_iters.at(0)->get_curr_row()->row_flag_.is_delete()) { result_row_.row_flag_.reset(); result_row_.row_flag_ = macro_row_iters.at(0)->get_curr_row()->row_flag_; for (int64_t i = 1; i < macro_row_iters_cnt; ++i) { result_row_.row_flag_.fuse_flag(macro_row_iters.at(i)->get_curr_row()->row_flag_); } } else { bool final_result = false; for (int64_t i = 0; OB_SUCC(ret) && !final_result && i < macro_row_iters_cnt; ++i) { if (OB_FAIL(add_fuse_row(*macro_row_iters.at(i)->get_curr_row(), final_result))) { STORAGE_LOG(WARN, "Failed to fuse row", K(ret)); } } if (OB_FAIL(ret)) { } else if (OB_FAIL(end_fuse_row(nop_pos_, result_row_))) { STORAGE_LOG(WARN, "failed to end fuse row", K(ret), KPC(this)); } } return ret; } // fuse delete row int ObMergeFuser::fuse_delete_row( const blocksstable::ObDatumRow &del_row, const int64_t rowkey_column_cnt) { int ret = OB_SUCCESS; if (!del_row.row_flag_.is_delete()) { ret = OB_ERR_UNEXPECTED; STORAGE_LOG(WARN, "Unexpected row flag", K(ret)); } else { int64_t copy_cnt = rowkey_column_cnt; if (del_row.is_uncommitted_row()){ copy_cnt = result_row_.count_; } for (int64_t i = 0; i < copy_cnt; ++i) { result_row_.storage_datums_[i] = del_row.storage_datums_[i]; } for (int64_t i = copy_cnt; i < result_row_.count_; ++i) { result_row_.storage_datums_[i].set_nop(); } if (OB_SUCC(ret)) { result_row_.row_flag_.set_flag(ObDmlFlag::DF_DELETE); result_row_.mvcc_row_flag_ = del_row.mvcc_row_flag_; result_row_.set_compacted_multi_version_row(); STORAGE_LOG(DEBUG, "fuse delete row", K(ret), K(del_row), K(result_row_)); } } return ret; } bool ObMergeFuser::is_valid() const { return (is_inited_ && column_cnt_ > 0); } /* *ObIPartitionMergeFuser */ bool ObIPartitionMergeFuser::is_valid() const { return ObMergeFuser::is_valid(); } int ObIPartitionMergeFuser::init(const ObMergeParameter &merge_param, const bool is_fuse_row_flag) { int ret = OB_SUCCESS; if (OB_UNLIKELY(is_inited())) { ret = OB_INIT_TWICE; STORAGE_LOG(WARN, "ObIPartitionMergeFuser init twice", K(ret)); } else if (OB_UNLIKELY(!merge_param.is_valid())) { ret = OB_INVALID_ARGUMENT; STORAGE_LOG(WARN, "Invalid argument to init ObIPartitionMergeFuser", K(merge_param), K(ret)); } else if (OB_FAIL(inner_init(merge_param))) { STORAGE_LOG(WARN, "Failed to inner init", K(ret), K(*this)); } else if (OB_FAIL(base_init(is_fuse_row_flag))){ STORAGE_LOG(WARN, "failed to init ObMergeFuser", K(ret), K(merge_param)); } else { is_inited_ = true; STORAGE_LOG(INFO, "Succ to init partition fuser", K(ret), K(*this)); } return ret; } /* *ObDefaultMergeFuser */ int ObDefaultMergeFuser::init(const int64_t column_count) { int ret = OB_SUCCESS; if (OB_UNLIKELY(is_inited_)) { ret = OB_INIT_TWICE; STORAGE_LOG(WARN, "ObMergeFuser init twice", K(ret)); } else if (FALSE_IT(column_cnt_ = column_count)) { } else if (OB_FAIL(base_init())) { STORAGE_LOG(WARN, "Failed to init datum row", K(ret)); } else { is_inited_ = true; } return ret; } /* *ObMajorPartitionMergeFuser */ ObMajorPartitionMergeFuser::~ObMajorPartitionMergeFuser() { default_row_.reset(); generated_cols_.reset(); } int ObMajorPartitionMergeFuser::inner_init(const ObMergeParameter &merge_param) { int ret = OB_SUCCESS; const common::ObIArray &multi_version_column_ids = merge_param.static_param_.multi_version_column_descs_; const ObStorageSchema *schema = merge_param.get_schema(); column_cnt_ = multi_version_column_ids.count(); const bool need_trim_default_row = cluster_version_ >= DATA_VERSION_4_3_1_0 || cluster_version_ < DATA_VERSION_4_3_0_0; if (OB_FAIL(default_row_.init(allocator_, column_cnt_))) { STORAGE_LOG(WARN, "Failed to init datum row", K(ret), K_(column_cnt)); } else if (OB_FAIL(schema->get_orig_default_row(multi_version_column_ids, need_trim_default_row, default_row_))) { STORAGE_LOG(WARN, "Failed to get default row from table schema", K(ret), K(multi_version_column_ids)); } else if (OB_FAIL(ObLobManager::fill_lob_header(allocator_, multi_version_column_ids, default_row_))) { STORAGE_LOG(WARN, "fail to fill lob header for default row", K(ret), K(multi_version_column_ids)); } else if (FALSE_IT(default_row_.row_flag_.set_flag(ObDmlFlag::DF_UPDATE))) { } else if (OB_FAIL(generated_cols_.init(column_cnt_))) { LOG_WARN("Fail to init generated_cols", K(ret), K_(column_cnt)); } else { const ObColumnSchemaV2 *column_schema = NULL; for (int64_t i = 0; OB_SUCC(ret) && i < column_cnt_; ++i) { if (OB_HIDDEN_TRANS_VERSION_COLUMN_ID == multi_version_column_ids.at(i).col_id_ || OB_HIDDEN_SQL_SEQUENCE_COLUMN_ID == multi_version_column_ids.at(i).col_id_) { // continue; } else{ const ObStorageColumnSchema *column_schema = NULL; if (OB_ISNULL(column_schema = schema->get_column_schema( multi_version_column_ids.at(i).col_id_))) { ret = OB_ERR_UNEXPECTED; STORAGE_LOG(WARN, "The column schema is NULL", K(ret), K(i), K(multi_version_column_ids.at(i))); } else if (column_schema->is_generated_column() && !schema->is_storage_index_table()) { // the generated columns in index are always filled before insert if (OB_FAIL(generated_cols_.push_back(i))) { LOG_WARN("Fail to push_back generated_cols", K(ret)); } } } } } return ret; } int ObMajorPartitionMergeFuser::end_fuse_row(const storage::ObNopPos &nop_pos, blocksstable::ObDatumRow &result_row) { int ret = OB_SUCCESS; if (nop_pos.count() > 0 && result_row.row_flag_.is_exist_without_delete()) { if (generated_cols_.count() > 0) { // add defense for generated exprs int64_t idx = -1; for (int64_t i = 0; OB_SUCC(ret) && i < nop_pos.count(); i++) { if (OB_FAIL(nop_pos.get_nop_pos(i, idx))) { LOG_WARN("Failed to get nop pos", K(i), K(ret)); } else { for (int64_t j = 0; OB_SUCC(ret) && j < generated_cols_.count(); j++) { if (idx == generated_cols_.at(j)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("Found nop generated columns in major fuser", K(ret), K(i), K(idx), K(nop_pos), K_(generated_cols), K(result_row)); } } } } } if (OB_SUCC(ret)) { bool final_result = false; if (OB_FAIL(add_fuse_row(default_row_, final_result))) { STORAGE_LOG(WARN, "Failed to fuse default row", K(ret)); } } } if (OB_SUCC(ret) && result_row.row_flag_.is_exist_without_delete()) { result_row.row_flag_.reset(); result_row.row_flag_.set_flag(ObDmlFlag::DF_INSERT); } return ret; } /* *ObMinorPartitionMergeFuser */ bool ObMinorPartitionMergeFuser::is_valid() const { return ObIPartitionMergeFuser::is_valid() && multi_version_rowkey_column_cnt_ > 0; } int ObMinorPartitionMergeFuser::inner_init(const ObMergeParameter &merge_param) { int ret = OB_SUCCESS; int64_t column_cnt = 0; if (OB_UNLIKELY(is_inited_)) { ret = OB_INIT_TWICE; STORAGE_LOG(WARN, "ObIPartitionMergeFuser init twice", K(ret)); } else if (OB_FAIL(merge_param.get_schema()->get_store_column_count(column_cnt, true/*full_col*/))) { STORAGE_LOG(WARN, "failed to get store column count", K(ret), K(merge_param.get_schema())); } else { column_cnt_ = column_cnt + storage::ObMultiVersionRowkeyHelpper::get_extra_rowkey_col_cnt(); multi_version_rowkey_column_cnt_ = merge_param.static_param_.multi_version_column_descs_.count(); } return ret; } int ObMinorPartitionMergeFuser::end_fuse_row(const storage::ObNopPos &nop_pos, blocksstable::ObDatumRow &result_row) { int ret = OB_SUCCESS; if (result_row.row_flag_.is_delete() || nop_pos.count_ == 0) { result_row.set_compacted_multi_version_row(); } return ret; } int ObMinorPartitionMergeFuser::preprocess_fuse_row(const blocksstable::ObDatumRow &row, bool &is_need_fuse) { int ret = OB_SUCCESS; is_need_fuse = true; clean_nop_pos_and_result_row(); if (row.trans_id_.is_valid()) { if (!row.mvcc_row_flag_.is_uncommitted_row()) { ret = OB_ERR_UNEXPECTED; LOG_ERROR("uncommitted row is not valid",K(ret), K(row), K(row.trans_id_)); } else { set_trans_id(row.trans_id_); } } if (OB_FAIL(ret)) { } else if (row.row_flag_.is_delete()) { if (OB_FAIL(fuse_delete_row(row, multi_version_rowkey_column_cnt_))) { STORAGE_LOG(WARN, "failed to fuse_delete_row", K(ret), K(row), K(multi_version_rowkey_column_cnt_)); } else { is_need_fuse = false; } } return ret; } int ObMergeFuserBuilder::build(const ObMergeParameter &merge_param, const int64_t cluster_version, ObIAllocator &allocator, ObIPartitionMergeFuser *&partition_fuser) { int ret = OB_SUCCESS; bool is_fuse_row_flag = true; partition_fuser = nullptr; if (OB_UNLIKELY(!merge_param.is_valid())) { ret = OB_INVALID_ARGUMENT; STORAGE_LOG(WARN, "Invalid argument to build MergeFuser", K(merge_param), K(ret)); } else { const ObMergeType merge_type = merge_param.static_param_.get_merge_type(); if (is_major_merge_type(merge_type) && !merge_param.get_schema()->is_row_store()) { partition_fuser = alloc_helper(allocator, allocator); } else if (is_major_or_meta_merge_type(merge_type)) { is_fuse_row_flag = false; partition_fuser = alloc_helper(allocator, allocator, cluster_version); } else { partition_fuser = alloc_helper(allocator, allocator); } if (OB_ISNULL(partition_fuser)) { ret = OB_ALLOCATE_MEMORY_FAILED; STORAGE_LOG(WARN, "Failed to allocate memory for partition fuser", K(ret), K(merge_param)); } else if (OB_FAIL(partition_fuser->init(merge_param, is_fuse_row_flag))) { STORAGE_LOG(WARN, "Failed to init partition fuser", K(ret)); } if (OB_FAIL(ret)) { if (OB_NOT_NULL(partition_fuser)) { partition_fuser->~ObIPartitionMergeFuser(); allocator.free(partition_fuser); partition_fuser = nullptr; } } } return ret; } } //compaction } //oceanbase