/** * 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 SQL_ENG #include "sql/engine/dml/ob_conflict_checker.h" #include "sql/das/ob_das_dml_ctx_define.h" #include "sql/engine/dml/ob_dml_ctx_define.h" #include "sql/engine/dml/ob_table_modify_op.h" #include "sql/engine/expr/ob_expr_calc_partition_id.h" #include "sql/das/ob_data_access_service.h" #include "sql/das/ob_das_utils.h" #include "sql/engine/ob_exec_context.h" namespace oceanbase { using namespace common; using namespace share; using namespace storage; using namespace share::schema; namespace sql { OB_SERIALIZE_MEMBER(ObRowkeyCstCtdef, constraint_name_, rowkey_expr_, calc_exprs_, rowkey_accuracys_); OB_DEF_SERIALIZE(ObConflictCheckerCtdef) { int ret = OB_SUCCESS; int64_t cons_info_cnt = cst_ctdefs_.count(); OB_UNIS_ENCODE(cons_info_cnt); for (int64_t i = 0; OB_SUCC(ret) && i < cons_info_cnt; ++i) { ObRowkeyCstCtdef *cst_ctdef = cst_ctdefs_.at(i); if (OB_ISNULL(cst_ctdef)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("constraint_info is nullptr", K(ret)); } OB_UNIS_ENCODE(*cst_ctdef); } if (OB_SUCC(ret)) { OB_UNIS_ENCODE(calc_part_id_expr_); OB_UNIS_ENCODE(part_id_dep_exprs_); OB_UNIS_ENCODE(das_scan_ctdef_); OB_UNIS_ENCODE(partition_cnt_); OB_UNIS_ENCODE(data_table_rowkey_expr_); OB_UNIS_ENCODE(table_column_exprs_); OB_UNIS_ENCODE(use_dist_das_); OB_UNIS_ENCODE(rowkey_count_); } return ret; } OB_DEF_DESERIALIZE(ObConflictCheckerCtdef) { int ret = OB_SUCCESS; int64_t cons_info_cnt = 0; OB_UNIS_DECODE(cons_info_cnt); ObDMLCtDefAllocator cons_info_allocator(alloc_); OZ(cst_ctdefs_.init(cons_info_cnt)); for (int64_t i = 0; OB_SUCC(ret) && i < cons_info_cnt; ++i) { ObRowkeyCstCtdef *cst_ctdef = cons_info_allocator.alloc(); if (OB_ISNULL(cst_ctdef)) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_WARN("alloc constraint_info failed", K(ret)); } OB_UNIS_DECODE(*cst_ctdef); OZ(cst_ctdefs_.push_back(cst_ctdef)); } if (OB_SUCC(ret)) { OB_UNIS_DECODE(calc_part_id_expr_); OB_UNIS_DECODE(part_id_dep_exprs_); OB_UNIS_DECODE(das_scan_ctdef_); OB_UNIS_DECODE(partition_cnt_); OB_UNIS_DECODE(data_table_rowkey_expr_); OB_UNIS_DECODE(table_column_exprs_); OB_UNIS_DECODE(use_dist_das_); OB_UNIS_DECODE(rowkey_count_); } return ret; } OB_DEF_SERIALIZE_SIZE(ObConflictCheckerCtdef) { int64_t len = 0; int64_t cons_info_cnt = cst_ctdefs_.count(); OB_UNIS_ADD_LEN(cons_info_cnt); for (int64_t i = 0; i < cons_info_cnt; ++i) { ObRowkeyCstCtdef *cts_ctdef = cst_ctdefs_.at(i); if (cts_ctdef != nullptr) { OB_UNIS_ADD_LEN(*cts_ctdef); } } OB_UNIS_ADD_LEN(calc_part_id_expr_); OB_UNIS_ADD_LEN(part_id_dep_exprs_); OB_UNIS_ADD_LEN(das_scan_ctdef_); OB_UNIS_ADD_LEN(partition_cnt_); OB_UNIS_ADD_LEN(data_table_rowkey_expr_); OB_UNIS_ADD_LEN(table_column_exprs_); OB_UNIS_ADD_LEN(use_dist_das_); OB_UNIS_ADD_LEN(rowkey_count_); return len; } bool ObConflictValue::operator==(const ObConflictValue &other) const { return current_datum_row_ == other.current_datum_row_; } int ObConflictRowMapCtx::reuse() { int ret = OB_SUCCESS; if (conflict_map_.created()) { OZ(conflict_map_.reuse()); } return ret; } int ObConflictRowMapCtx::destroy() { int ret = OB_SUCCESS; if (conflict_map_.created()) { OZ(conflict_map_.destroy()); } return ret; } int ObConflictRowMapCtx::init_conflict_map(int64_t replace_row_cnt, int64_t rowkey_cnt, common::ObIAllocator *allocator) { int ret = OB_SUCCESS; if (OB_ISNULL(allocator)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("Allocator used to init conflict map is null", K(ret)); } else { allocator_ = allocator; } if (OB_SUCC(ret) && !conflict_map_.created()) { ObObj *objs = NULL; int64_t bucket_num = 0; bucket_num = replace_row_cnt < MAX_ROW_BATCH_SIZE ? replace_row_cnt : MAX_ROW_BATCH_SIZE; // map 没创建的场景下才需要创建, 这里可能被重复调用 if (NULL == (rowkey_ = static_cast(allocator_->alloc(sizeof(ObRowkey))))) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_WARN("fail to alloc memory", K(ret)); } else if (NULL == (objs = static_cast(allocator_->alloc(sizeof(ObObj)* rowkey_cnt)))){ ret = OB_ALLOCATE_MEMORY_FAILED; LOG_WARN("fail to alloc memory", K(ret)); } else if (OB_FAIL(conflict_map_.create(bucket_num, ObModIds::OB_HASH_BUCKET))) { LOG_WARN("fail to create conflict map", K(ret)); } else { rowkey_->assign(objs, rowkey_cnt); } } return ret; } ObConflictChecker::ObConflictChecker(common::ObIAllocator &allocator, ObEvalCtx &eval_ctx, const ObConflictCheckerCtdef &checker_ctdef) : eval_ctx_(eval_ctx), checker_ctdef_(checker_ctdef), das_scan_rtdef_(), allocator_(allocator), das_ref_(eval_ctx, eval_ctx.exec_ctx_), local_tablet_loc_(nullptr), table_loc_(nullptr), tmp_mem_ctx_() { } //初始conflict_map int ObConflictChecker::create_conflict_map(int64_t replace_row_cnt) { int ret = OB_SUCCESS; int64_t constraint_cnt = checker_ctdef_.cst_ctdefs_.count(); for (int64_t i = 0; OB_SUCC(ret) && i < constraint_cnt; ++i) { ObRowkeyCstCtdef *rowkey_cst_ctdef = checker_ctdef_.cst_ctdefs_.at(i); int64_t rowkey_cnt = rowkey_cst_ctdef->rowkey_expr_.count(); // 在init_conflict_map 函数内保证了map不会被重复created if (OB_FAIL(conflict_map_array_.at(i).init_conflict_map(replace_row_cnt, rowkey_cnt, &allocator_))) { LOG_WARN("fail to init conflict_map", K(ret), K(rowkey_cnt)); } } return ret; } //初始化map array, map创建hash_bucket将会在延后 int ObConflictChecker::init_conflict_checker(const ObExprFrameInfo *expr_frame_info, ObDASTableLoc *table_loc) { int ret = OB_SUCCESS; ObSQLSessionInfo *session = eval_ctx_.exec_ctx_.get_my_session(); int64_t constraint_cnt = checker_ctdef_.cst_ctdefs_.count(); table_loc_ = table_loc; OZ(conflict_map_array_.allocate_array(allocator_, constraint_cnt), constraint_cnt); if (OB_SUCC(ret)) { ObMemAttr mem_attr; mem_attr.tenant_id_ = session->get_effective_tenant_id(); mem_attr.label_ = "SqlConflictCkr"; das_ref_.set_expr_frame_info(expr_frame_info); // 这里需要注意 das_ref_.set_execute_directly(!checker_ctdef_.use_dist_das_); das_ref_.set_mem_attr(mem_attr); } OZ(init_das_scan_rtdef()); return ret; } int ObConflictChecker::get_tmp_string_buffer(ObIAllocator *&allocator) { int ret = OB_SUCCESS; allocator = nullptr; if (OB_ISNULL(tmp_mem_ctx_)) { lib::ContextParam param; param.set_mem_attr(MTL_ID(), "ConflictRowkey", ObCtxIds::DEFAULT_CTX_ID) .set_properties(lib::USE_TL_PAGE_OPTIONAL); if (OB_FAIL(CURRENT_CONTEXT->CREATE_CONTEXT(tmp_mem_ctx_, param))) { LOG_WARN("create conflict rowkey checker context entity failed", K(ret)); } } else { tmp_mem_ctx_->reset_remain_one_page(); } if (OB_SUCC(ret)) { allocator = &tmp_mem_ctx_->get_allocator(); } return ret; } int ObConflictChecker::build_rowkey(ObRowkey *&rowkey, ObRowkeyCstCtdef *rowkey_info) { int ret = OB_SUCCESS; ObObj *objs = NULL; int64_t rowkey_cnt = rowkey_info->rowkey_expr_.count(); ObIAllocator &alloc = das_ref_.get_das_alloc(); ObIAllocator *tmp_string_buffer = nullptr; if (NULL == (rowkey = static_cast(alloc.alloc(sizeof(ObRowkey))))) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_WARN("fail to alloc memory", K(ret)); } else if (NULL == (objs = static_cast(das_ref_.get_das_alloc().alloc(sizeof(ObObj)* rowkey_cnt)))){ ret = OB_ALLOCATE_MEMORY_FAILED; LOG_WARN("fail to alloc memory", K(ret)); } else if (OB_FAIL(ObSQLUtils::clear_evaluated_flag(rowkey_info->calc_exprs_, eval_ctx_))) { LOG_WARN("fail to clear rowkey flag", K(ret), K(rowkey_info->calc_exprs_)); } else if (OB_FAIL(get_tmp_string_buffer(tmp_string_buffer))) { LOG_WARN("get tmp string buffer failed", K(ret)); } for (int i = 0; OB_SUCC(ret) && i < rowkey_cnt; ++i) { ObDatum *datum = NULL; ObObj tmp_obj; const ObObjMeta &col_obj_meta = rowkey_info->rowkey_expr_.at(i)->obj_meta_; ObExpr *expr = rowkey_info->rowkey_expr_.at(i); const ObAccuracy *col_accuracy = nullptr; if (rowkey_info->rowkey_accuracys_.count() == rowkey_cnt) { //To maintain compatibility with older versions, //reshape_storage_value is only performed when rowkey_accuracys is not empty. col_accuracy = &rowkey_info->rowkey_accuracys_.at(i); } if (OB_ISNULL(expr)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("expr is null", K(ret), K(i)); } else if (OB_FAIL(expr->eval(eval_ctx_, datum))) { LOG_WARN("expr eval fail", K(ret), K(i), KPC(expr)); } // 这里对datum要做浅拷贝,因为此时的datum来自于从存储层table_scan出来的数据, // 读后续行的时候会覆盖前边的数据,所以这里一定要做深拷贝 else if (OB_FAIL(datum->to_obj(tmp_obj, col_obj_meta))) { LOG_WARN("datum to obj fail", K(ret), K(i), KPC(expr), KPC(datum)); } else if (col_accuracy != nullptr && OB_FAIL(ObDASUtils::reshape_storage_value(col_obj_meta, *col_accuracy, *tmp_string_buffer, tmp_obj))) { LOG_WARN("reshape storage value failed", K(ret)); } else if (OB_FAIL(ob_write_obj(alloc, tmp_obj, objs[i]))) { LOG_WARN("deep copy rowkey value failed", K(ret), K(tmp_obj)); } } if (OB_SUCC(ret)) { rowkey->assign(objs, rowkey_cnt); LOG_DEBUG("succeed to build rowkey", KPC(rowkey)); } return ret; } int ObConflictChecker::build_tmp_rowkey(ObRowkey *rowkey, ObRowkeyCstCtdef *rowkey_info) { int ret = OB_SUCCESS; if (OB_ISNULL(rowkey)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("rowkey is null", K(ret)); } else if (OB_ISNULL(rowkey_info)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("rowkey_info is null", K(ret)); } if (OB_SUCC(ret)) { ObObj *obj_ptr = rowkey->get_obj_ptr(); ObIAllocator *tmp_string_buffer = nullptr; if (OB_ISNULL(obj_ptr)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("obj_ptr is null", K(ret)); } else if (OB_FAIL(ObSQLUtils::clear_evaluated_flag(rowkey_info->calc_exprs_, eval_ctx_))) { LOG_WARN("clear eval flag failed", K(ret), K(rowkey_info->calc_exprs_)); } else if (OB_FAIL(get_tmp_string_buffer(tmp_string_buffer))) { LOG_WARN("get tmp string buffer failed", K(ret)); } for (int i = 0; OB_SUCC(ret) && i < rowkey_info->rowkey_expr_.count(); ++i) { ObDatum *datum = NULL; const ObObjMeta &col_obj_meta = rowkey_info->rowkey_expr_.at(i)->obj_meta_; ObExpr *expr = rowkey_info->rowkey_expr_.at(i); const ObAccuracy *col_accuracy = nullptr; if (rowkey_info->rowkey_accuracys_.count() == rowkey_info->rowkey_expr_.count()) { //To maintain compatibility with older versions, //reshape_storage_value is only performed when rowkey_accuracys is not empty. col_accuracy = &rowkey_info->rowkey_accuracys_.at(i); } if (OB_ISNULL(expr)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("expr is null", K(ret), K(i)); } else if (OB_FAIL(expr->eval(eval_ctx_, datum))) { LOG_WARN("expr eval fail", K(ret), K(i), KPC(expr)); } else if (OB_FAIL(datum->to_obj(obj_ptr[i], col_obj_meta))) { LOG_WARN("datum to obj fail", K(ret), K(i), KPC(expr), KPC(datum)); } else if (col_accuracy != nullptr && OB_FAIL(ObDASUtils::reshape_storage_value(col_obj_meta, *col_accuracy, *tmp_string_buffer, obj_ptr[i]))) { LOG_WARN("reshape storage value failed", K(ret)); } else { LOG_DEBUG("succ to build tmp rowkey obj", K(i), K(obj_ptr[i])); } } } return ret; } // 用回表拉回的数据来构建map int ObConflictChecker::build_base_conflict_map( int64_t replace_row_cnt, const ObChunkDatumStore::StoredRow *conflict_row) { int ret = OB_SUCCESS; bool is_duplicated = false; if (replace_row_cnt <= 0) { ret = OB_ERR_UNEXPECTED; LOG_WARN("replace_row_cnt is unexpected", K(ret), K(replace_row_cnt)); } else if (OB_ISNULL(conflict_row)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("conflict_row is unexpected", K(ret)); } for (int64_t i = 0; OB_SUCC(ret) && !is_duplicated && i < checker_ctdef_.cst_ctdefs_.count(); ++i) { ObRowkey *rowkey = nullptr; if (OB_FAIL(build_rowkey(rowkey, checker_ctdef_.cst_ctdefs_.at(i)))) { LOG_WARN("fail to build rowkey", K(ret)); } else if (OB_ISNULL(rowkey)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("rowkey is null", K(ret)); } else { ObConflictValue constraint_value; constraint_value.baseline_datum_row_ = conflict_row; constraint_value.current_datum_row_ = conflict_row; constraint_value.new_row_source_ = ObNewRowSource::FROM_SCAN; if (OB_FAIL(conflict_map_array_.at(i).conflict_map_.set_refactored(*rowkey, constraint_value))) { if (OB_HASH_EXIST == ret && 0 == i) { // 回表查出来的结果很可能有相同主表行 // create table t1 (c1 int primary key, c2 int unique) partition by hash(c1) ....; // insert into t1 values(1,1),(2,2),(3,3),(4,4); // replace into t1 values(1,1); // 此时主表和索引表冲突的行都是主表中(1,1)行 // 所以这里跳过duplicated row is_duplicated = true; ret = OB_SUCCESS; } else { // 如果主键不相同,但是后边有唯一性索引相同,绝对的不符合预期 LOG_WARN("set constraint key failed", K(ret), K(i)); } } LOG_DEBUG("build base line conflict infos", K(ret), K(i), KPC(rowkey), K(constraint_value), K(is_duplicated), "conflict row", ROWEXPR2STR(eval_ctx_, checker_ctdef_.table_column_exprs_)); } } return ret; } // 检查当前的主键是否冲突 int ObConflictChecker::check_duplicate_rowkey(const ObChunkDatumStore::StoredRow *replace_row, ObIArray &constraint_values, bool is_insert_up) { int ret = OB_SUCCESS; bool is_break = false; if (OB_FAIL(to_expr(replace_row))) { LOG_WARN("do to_expr failed", K(ret), KPC(replace_row)); } else { LOG_DEBUG("check one row whether duplicated in hash_map", K(ret), "check row", ROWEXPR2STR(eval_ctx_, checker_ctdef_.table_column_exprs_)); } int64_t N = checker_ctdef_.cst_ctdefs_.count(); for (int64_t i = 0; OB_SUCC(ret) && !is_break && i < N; ++i) { ObRowkeyCstCtdef *rowkey_cst_ctdef = checker_ctdef_.cst_ctdefs_.at(i); ObRowkey *constraint_key = nullptr; ObConflictValue constraint_value; ObConflictRowMapCtx &map_ctx = conflict_map_array_.at(i); constraint_key = map_ctx.rowkey_; if (OB_FAIL(ObSQLUtils::clear_evaluated_flag(rowkey_cst_ctdef->calc_exprs_, eval_ctx_))) { LOG_WARN("fail to clear constraint eval flag", K(ret), K(rowkey_cst_ctdef->calc_exprs_)); } else if (OB_FAIL(build_tmp_rowkey(constraint_key, rowkey_cst_ctdef))) { LOG_WARN("fail to build_tmp_rowkey", K(ret)); } else if (OB_FAIL(map_ctx.conflict_map_.get_refactored(*constraint_key, constraint_value))) { if (OB_HASH_NOT_EXIST != ret) { LOG_WARN("get duplicated row from constraint contexts failed", K(ret), KPC(constraint_key)); } else { ret = OB_SUCCESS; } } // todo @kaizhan.dkz 这里需要检查是否需要update_incremental_row_检查, // insert_up才需要,replace 暂时用不到 if (OB_SUCC(ret) && constraint_value.current_datum_row_ != NULL) { if (OB_FAIL(add_var_to_array_no_dup(constraint_values, constraint_value))) { LOG_WARN("add constraint value no duplicate failed", K(ret)); } else if (is_insert_up) { //is_insert_up = true: // only need one duplicated row(compatible with MySQL insert_up) //is_insert_up = false: // need all duplicated row(compatible with MySQL replace) is_break = true; } } LOG_DEBUG("check duplicate rowkey", K(ret), K(is_insert_up), K(constraint_key), K(constraint_value), "check row", ROWEXPR2STR(eval_ctx_, checker_ctdef_.table_column_exprs_)); } return ret; } // 从hash map中删除冲突base行 int ObConflictChecker::delete_old_row(const ObChunkDatumStore::StoredRow *replace_row, ObNewRowSource from) { int ret = OB_SUCCESS; if (OB_FAIL(to_expr(replace_row))) { LOG_WARN("do to_expr failed", K(ret), KPC(replace_row)); } else { LOG_DEBUG("delete one row from hash_map", K(ret), "delete row", ROWEXPR2STR(eval_ctx_, checker_ctdef_.table_column_exprs_)); } for (int64_t i = 0; OB_SUCC(ret) && i < checker_ctdef_.cst_ctdefs_.count(); ++i) { ObRowkey *constraint_key; ObConflictValue *constraint_value = NULL; ObRowkeyCstCtdef *rowkey_cst_ctdef = checker_ctdef_.cst_ctdefs_.at(i); ObConflictRowMapCtx &map_ctx = conflict_map_array_.at(i); constraint_key = map_ctx.rowkey_; if (OB_FAIL(ObSQLUtils::clear_evaluated_flag(rowkey_cst_ctdef->calc_exprs_, eval_ctx_))) { LOG_WARN("fail to clear constraint_info eval flag", K(ret)); } else if (OB_FAIL(build_tmp_rowkey(constraint_key, rowkey_cst_ctdef))) { LOG_WARN("fail to build_tmp_rowkey", K(ret)); } else { constraint_value = const_cast(map_ctx.conflict_map_.get(*constraint_key)); if (OB_ISNULL(constraint_value)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("constraint_value is unexpected", K(ret)); } else { constraint_value->current_datum_row_ = NULL; constraint_value->new_row_source_ = from; } } } return ret; } // 插入新行到hash map中 int ObConflictChecker::insert_new_row(const ObChunkDatumStore::StoredRow *new_row, ObNewRowSource from) { int ret = OB_SUCCESS; if (OB_FAIL(to_expr(new_row))) { LOG_WARN("do to_expr failed", K(ret), KPC(new_row)); } else { LOG_DEBUG("insert one row to hash_map", K(ret), "insert row", ROWEXPR2STR(eval_ctx_, checker_ctdef_.table_column_exprs_)); } for (int64_t i = 0; OB_SUCC(ret) && i < checker_ctdef_.cst_ctdefs_.count(); ++i) { ObRowkey *constraint_key = nullptr; ObConflictValue *constraint_value = NULL; ObRowkeyCstCtdef *rowkey_cst_ctdef = checker_ctdef_.cst_ctdefs_.at(i); ObConflictRowMapCtx &map_ctx = conflict_map_array_.at(i); ObRowkey *insert_rowkey = nullptr; constraint_key = map_ctx.rowkey_; if (OB_FAIL(ObSQLUtils::clear_evaluated_flag(rowkey_cst_ctdef->calc_exprs_, eval_ctx_))) { LOG_WARN("fail to clear eval flag", K(ret)); } else if (OB_FAIL(build_tmp_rowkey(constraint_key, rowkey_cst_ctdef))) { LOG_WARN("fail to build tmp rowkey", K(ret), KPC(rowkey_cst_ctdef)); } else if (FALSE_IT(constraint_value = const_cast(map_ctx.conflict_map_.get(*constraint_key)))) { } else if (constraint_value != NULL) { if (OB_NOT_NULL(constraint_value->current_datum_row_)) { // insert up 在update之后的行仍然是存在唯一约束冲突,在这里模拟存储层报错 ret = OB_ERR_PRIMARY_KEY_DUPLICATE; char rowkey_buffer[OB_TMP_BUF_SIZE_256]; if (OB_SUCCESS != (extract_rowkey_info(rowkey_cst_ctdef, rowkey_buffer, OB_TMP_BUF_SIZE_256))) { LOG_WARN("extract rowkey info failed", K(ret), KPC(rowkey_cst_ctdef), K(new_row)); } else { const ObString &constraint_name = rowkey_cst_ctdef->constraint_name_; LOG_USER_ERROR(OB_ERR_PRIMARY_KEY_DUPLICATE, rowkey_buffer, constraint_name.length(), constraint_name.ptr()); } } else { // map中命中,说明base行被删除了,然后插入新行 constraint_value->current_datum_row_ = new_row; constraint_value->new_row_source_ = from; LOG_DEBUG("add one row to hash map and current_datum_row_ is null", K(i), KPC(constraint_key), KPC(constraint_value)); } } else { // 真实插入新行到map ObConflictValue new_constraint_value; new_constraint_value.current_datum_row_ = new_row; new_constraint_value.new_row_source_ = from; if (OB_FAIL(build_rowkey(insert_rowkey, rowkey_cst_ctdef))) { LOG_WARN("fail to build insert_rowkey rowkey", K(ret), K(i), KPC(rowkey_cst_ctdef)); } else if (OB_ISNULL(insert_rowkey)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("insert rowkey is null", K(ret), K(i), KPC(rowkey_cst_ctdef)); } else if (OB_FAIL(map_ctx.conflict_map_.set_refactored(*insert_rowkey, new_constraint_value))) { LOG_WARN("insert to map failed", K(ret), K(i), KPC(insert_rowkey)); } else { LOG_DEBUG("real add one row to hash_map", K(i), KPC(insert_rowkey), K(new_constraint_value)); } } } return ret; } int ObConflictChecker::update_row(const ObChunkDatumStore::StoredRow *new_row, const ObChunkDatumStore::StoredRow *old_row) { int ret = OB_SUCCESS; if (OB_FAIL(delete_old_row(old_row, ObNewRowSource::FROM_INSERT))) { LOG_WARN("fail to delete old_row", K(ret), KPC(old_row)); } else if (OB_FAIL(insert_new_row(new_row, ObNewRowSource::FROM_UPDATE))) { LOG_WARN("fail to insert new_row", K(ret), KPC(new_row)); } return ret; } int ObConflictChecker::lock_row(const ObChunkDatumStore::StoredRow *lock_row) { int ret = OB_SUCCESS; if (OB_FAIL(to_expr(lock_row))) { LOG_WARN("do to_expr failed", K(ret), KPC(lock_row)); } else { LOG_DEBUG("lock one row from hash_map", K(ret), "lock row", ROWEXPR2STR(eval_ctx_, checker_ctdef_.table_column_exprs_)); } for (int64_t i = 0; OB_SUCC(ret) && i < checker_ctdef_.cst_ctdefs_.count(); ++i) { ObRowkey *constraint_key; ObConflictValue *constraint_value = NULL; ObRowkeyCstCtdef *rowkey_cst_ctdef = checker_ctdef_.cst_ctdefs_.at(i); ObConflictRowMapCtx &map_ctx = conflict_map_array_.at(i); constraint_key = map_ctx.rowkey_; if (OB_FAIL(ObSQLUtils::clear_evaluated_flag(rowkey_cst_ctdef->calc_exprs_, eval_ctx_))) { LOG_WARN("fail to clear constraint_info eval flag", K(ret)); } else if (OB_FAIL(build_tmp_rowkey(constraint_key, rowkey_cst_ctdef))) { LOG_WARN("fail to build_tmp_rowkey", K(ret)); } else { // get one duplicate row, this row must current_datum_row_ == baseline_datum_row_ and // new_row_source_ is FROM_SCAN, can do row lock constraint_value = const_cast(map_ctx.conflict_map_.get(*constraint_key)); if (OB_ISNULL(constraint_value)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("constraint_value is unexpected", K(ret)); } else if (constraint_value->new_row_source_ == ObNewRowSource::FROM_SCAN) { if (constraint_value->current_datum_row_ != constraint_value->baseline_datum_row_) { ret = OB_ERR_UNEXPECTED; LOG_WARN("curr_datum_row is not equal with baseline_datum_row", K(ret), K(constraint_value)); } else if (OB_ISNULL(constraint_value->baseline_datum_row_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("baseline_datum_row is null", K(ret), K(constraint_value)); } else { constraint_value->new_row_source_ = ObNewRowSource::NEED_DO_LOCK; } } else { // create table t1 (c1 int primary key, c2 int unique, c3 int); // insert into t1 values(1,1,1); // insert into t1 values(1,1,1),(2,2,2) on duplicate key update c1 = 2, c2 = 2, c3 = 2; // (1,1,1) will do update --> (2,2,2), then insert (2,2,2) will do update, but row not changed // (2,2,2) in hash_map don't do lock, but do ins_upd_new_row } } } return ret; } int ObConflictChecker::convert_exprs_to_stored_row(const ObExprPtrIArray &exprs, ObChunkDatumStore::StoredRow *&new_row) { return ObChunkDatumStore::StoredRow::build(new_row, exprs, eval_ctx_, das_ref_.get_das_alloc()); } int ObConflictChecker::close() { int ret = OB_SUCCESS; // close回表的task if (das_ref_.has_task()) { if (OB_FAIL(das_ref_.close_all_task())) { LOG_WARN("close all das task failed", K(ret)); } } return ret; } int ObConflictChecker::reuse() { int ret = OB_SUCCESS; if (das_ref_.has_task()) { if (OB_FAIL(das_ref_.close_all_task())) { LOG_WARN("close all insert das task failed", K(ret)); } } if (OB_SUCC(ret)) { das_ref_.reuse(); } for (int64_t i = 0; OB_SUCC(ret) && i < conflict_map_array_.count(); ++i) { if (OB_FAIL(conflict_map_array_.at(i).reuse())) { LOG_WARN("fail to reuse conflict_map", K(ret), K(i)); } } if (tmp_mem_ctx_ != nullptr) { tmp_mem_ctx_->reset_remain_one_page(); } return ret; } int ObConflictChecker::destroy() { int ret = OB_SUCCESS; // 在这里析构 conflict_map_array_和das_scan_rtdef_ for (int64_t i = 0; OB_SUCC(ret) && i < conflict_map_array_.count(); ++i) { if (OB_FAIL(conflict_map_array_.at(i).destroy())) { LOG_WARN("fail to destroy conflict_map", K(ret), K(i)); } } das_ref_.reset(); das_scan_rtdef_.~ObDASScanRtDef(); if (tmp_mem_ctx_ != nullptr) { DESTROY_CONTEXT(tmp_mem_ctx_); tmp_mem_ctx_ = nullptr; } return ret; } // todo @kaizhan.dkz 向主表执行回表操作,返回主表中冲突的行 int ObConflictChecker::build_primary_table_lookup_das_task() { int ret = OB_SUCCESS; ObDASScanOp *das_scan_op = nullptr; ObDASTabletLoc *tablet_loc = nullptr; ObNewRange lookup_range; // data_table_rowkey_expr_ 是column_ref expr if (OB_FAIL(calc_lookup_tablet_loc(tablet_loc))) { LOG_WARN("calc lookup pkey fail", K(ret)); } else if (OB_FAIL(get_das_scan_op(tablet_loc, das_scan_op))) { LOG_WARN("get_das_scan_op failed", K(ret), K(tablet_loc)); } else if (OB_ISNULL(das_scan_op)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("das_scan_op should be not null", K(ret)); } else { storage::ObTableScanParam &scan_param = das_scan_op->get_scan_param(); if (OB_FAIL(build_data_table_range(lookup_range))) { LOG_WARN("build data table range failed", K(ret), KPC(tablet_loc)); } else if (OB_FAIL(add_var_to_array_no_dup(scan_param.key_ranges_, lookup_range))) { LOG_WARN("store lookup key range failed", K(ret), K(lookup_range), K(scan_param)); } else { LOG_TRACE("after build conflict rowkey", K(scan_param.tablet_id_), K(scan_param.key_ranges_.count()), K(lookup_range)); } } return ret; } int ObConflictChecker::to_expr(const ObChunkDatumStore::StoredRow *replace_row) { int ret = OB_SUCCESS; if (OB_ISNULL(replace_row)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("replace_row is null", K(ret)); } else if (OB_FAIL(replace_row->to_expr(checker_ctdef_.table_column_exprs_, eval_ctx_, checker_ctdef_.table_column_exprs_.count()))) { LOG_WARN("fail to do to_expr", K(ret), KPC(replace_row), K(checker_ctdef_.table_column_exprs_.count())); } return ret; } int ObConflictChecker::calc_lookup_tablet_loc(ObDASTabletLoc *&tablet_loc) { int ret = OB_SUCCESS; ObExpr *part_id_expr = NULL; ObTabletID tablet_id; ObObjectID partition_id = OB_INVALID_ID; tablet_loc = nullptr; if (checker_ctdef_.use_dist_das_) { // data_table_rowkey_expr_ 是column_ref expr if (OB_ISNULL(part_id_expr = checker_ctdef_.calc_part_id_expr_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("calc_part_id_expr_ is null", K(ret)); } // 清除回表使用的分区计算表达式的eval flag else if (OB_FAIL(ObSQLUtils::clear_evaluated_flag(checker_ctdef_.part_id_dep_exprs_, eval_ctx_))) { LOG_WARN("fail to clear rowkey flag", K(ret), K(checker_ctdef_.part_id_dep_exprs_)); } else if (OB_FAIL(ObExprCalcPartitionBase::calc_part_and_tablet_id(part_id_expr, eval_ctx_, partition_id, tablet_id))) { LOG_WARN("fail to calc part id", K(ret), KPC(part_id_expr)); } else if (OB_FAIL(DAS_CTX(das_ref_.get_exec_ctx()).extended_tablet_loc(*table_loc_, tablet_id, tablet_loc))) { LOG_WARN("extended tablet loc failed", K(ret)); } } else { tablet_loc = local_tablet_loc_; } return ret; } // get当前行对应的scan_op int ObConflictChecker::get_das_scan_op(ObDASTabletLoc *tablet_loc, ObDASScanOp *&das_scan_op) { int ret = OB_SUCCESS; if (OB_UNLIKELY(!das_ref_.has_das_op(tablet_loc, das_scan_op))) { if (OB_FAIL(das_ref_.prepare_das_task(tablet_loc, das_scan_op))) { LOG_WARN("prepare das task failed", K(ret)); } else { das_scan_op->set_scan_ctdef(&checker_ctdef_.das_scan_ctdef_); das_scan_op->set_scan_rtdef(&das_scan_rtdef_); table_loc_->is_reading_ = true; //mark the table location with reading action } } return ret; } int ObConflictChecker::build_data_table_range(ObNewRange &lookup_range) { int ret = OB_SUCCESS; ObObj *obj_ptr = nullptr; void *buf = nullptr; int64_t rowkey_cnt = checker_ctdef_.rowkey_count_; if (OB_ISNULL(buf = das_ref_.get_das_alloc().alloc(sizeof(ObObj) * rowkey_cnt))) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_WARN("allocate buffer failed", K(ret), K(rowkey_cnt)); } else { obj_ptr = new(buf) ObObj[rowkey_cnt]; } for (int64_t i = 0; OB_SUCC(ret) && i < rowkey_cnt; ++i) { ObObj tmp_obj; ObExpr *expr = checker_ctdef_.data_table_rowkey_expr_.at(i); ObDatum *col_datum = nullptr; if (OB_ISNULL(expr)) { LOG_WARN("expr in rowkey is nullptr", K(ret), K(i)); } else if (OB_FAIL(expr->eval(eval_ctx_, col_datum))) { LOG_WARN("failed to evaluate expr in rowkey", K(ret), K(i)); } else if (OB_ISNULL(col_datum)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("evaluated column datum in rowkey is nullptr", K(ret), K(i)); } else if (OB_FAIL(col_datum->to_obj(tmp_obj, expr->obj_meta_, expr->obj_datum_map_))) { LOG_WARN("convert datum to obj failed", K(ret)); } // 这里需要做深拷贝 else if (OB_FAIL(ob_write_obj(das_ref_.get_das_alloc(), tmp_obj, obj_ptr[i]))) { LOG_WARN("deep copy rowkey value failed", K(ret), K(tmp_obj)); } } if (OB_SUCC(ret)) { ObRowkey table_rowkey(obj_ptr, rowkey_cnt); uint64_t ref_table_id = checker_ctdef_.das_scan_ctdef_.ref_table_id_; if (OB_FAIL(lookup_range.build_range(ref_table_id, table_rowkey))) { LOG_WARN("build lookup range failed", K(ret), K(ref_table_id), K(table_rowkey)); } } return ret; } // lookup from primary table int ObConflictChecker::do_lookup_and_build_base_map(int64_t replace_row_cnt) { int ret = OB_SUCCESS; const ExprFixedArray &storage_output = checker_ctdef_.das_scan_ctdef_.pd_expr_spec_.access_exprs_; if (OB_FAIL(das_ref_.execute_all_task())) { LOG_WARN("execute all delete das task failed", K(ret)); } else { DASOpResultIter result_iter = das_ref_.begin_result_iter(); if (OB_FAIL(create_conflict_map(replace_row_cnt))) { LOG_WARN("create conflict map failed", K(ret)); } ObChunkDatumStore::StoredRow *conflict_row = NULL; while (OB_SUCC(ret) && OB_SUCC(get_next_row_from_data_table(result_iter, conflict_row))) { if (OB_FAIL(build_base_conflict_map(replace_row_cnt, conflict_row))) { LOG_WARN("build conflict map failed", K(ret)); } } ret = OB_ITER_END == ret ? OB_SUCCESS : ret; } return ret; } int ObConflictChecker::get_next_row_from_data_table(DASOpResultIter &result_iter, ObChunkDatumStore::StoredRow *&conflict_row) { int ret = OB_SUCCESS; bool got_row = false; const ExprFixedArray &storage_output = checker_ctdef_.table_column_exprs_; while (OB_SUCC(ret) && !got_row) { das_scan_rtdef_.p_pd_expr_op_->clear_datum_eval_flag(); if (OB_FAIL(result_iter.get_next_row())) { if (OB_ITER_END == ret) { if (OB_FAIL(result_iter.next_result())) { if (OB_ITER_END != ret) { LOG_WARN("fetch next task failed", K(ret)); } } } else { LOG_WARN("get next row from das result failed", K(ret)); } } else if (OB_FAIL(convert_exprs_to_stored_row(storage_output, conflict_row))) { LOG_WARN("convert expr to stored row failed", K(ret), "lookup one row", ROWEXPR2STR(eval_ctx_, storage_output)); } else { got_row = true; // 这里打印回表拿到的行信息 LOG_DEBUG("success to get row from data_table", K(ret), K(storage_output), "lookup one row", ROWEXPR2STR(eval_ctx_, storage_output)); } } return ret; } // 主表的ObRowkeyCstCtdef就是主表主键的表达式 // unique索引表的主键组成: unique column + shadow_pk int ObConflictChecker::extract_rowkey_info(const ObRowkeyCstCtdef *constraint_info, char *buf, int64_t buf_len) { int ret = OB_SUCCESS; ObDatum *r_datum = NULL; ObObj r_obj; int64_t unique_key_cnt = 0; int64_t pos = 0; ObSQLSessionInfo *session = eval_ctx_.exec_ctx_.get_my_session(); const ObRowkeyCstCtdef *primary_cst_info =checker_ctdef_.cst_ctdefs_.at(0); int64_t primary_rowkey_cnt = primary_cst_info->rowkey_expr_.count(); if (constraint_info == primary_cst_info) { unique_key_cnt = primary_rowkey_cnt; } else { // unique索引表需要排除shadow_pk // shadow_pk的位置顺序在主键最后边 unique_key_cnt = constraint_info->rowkey_expr_.count() - primary_rowkey_cnt; } if (unique_key_cnt <= 0) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unique_key_cnt is unexpected", K(unique_key_cnt), K(primary_rowkey_cnt), K(constraint_info)); } else if (OB_ISNULL(session)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("session is null", K(ret)); } for (int64_t i = 0; OB_SUCC(ret) && i < unique_key_cnt; i++) { ObExpr *expr = constraint_info->rowkey_expr_.at(i); if (OB_ISNULL(expr)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("expr is null", K(ret)); } else if (OB_FAIL(expr->eval(eval_ctx_, r_datum))) { LOG_WARN("expr eval failed", K(ret), K(expr)); } else if (OB_ISNULL(r_datum)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("after eval r_datum is null", K(ret), K(expr)); } else if (OB_FAIL(r_datum->to_obj(r_obj, expr->obj_meta_))) { LOG_WARN("r_datum to obj failed", K(ret), KPC(r_datum)); } else if (OB_FAIL(r_obj.print_plain_str_literal(buf, buf_len - 1, pos, session->get_timezone_info()))) { LOG_WARN("print obj failed", K(ret), K(r_obj)); } else if (i == unique_key_cnt - 1) { // do nothing } else if (OB_FAIL(databuff_printf(buf, buf_len - 1, pos, "-"))) { LOG_WARN("print - failed", K(ret), K(pos)); } } if (OB_SUCC(ret)) { ObString rowkey_info(buf); LOG_DEBUG("after extract rowkey info", K(rowkey_info)); } return ret; } //这个函数类似于shuffle_final_data,只不过这里只是返回主表的hash map的指针,外层函数迭代map,将行分别插入对应的das task int ObConflictChecker::get_primary_table_map(ObConflictRowMap *&primary_map) { int ret = OB_SUCCESS; primary_map = NULL; CK(conflict_map_array_.count() > 0) if (conflict_map_array_.count() <= 0) { ret = OB_INVALID_ARGUMENT; LOG_WARN("conflict map count must greater than 0", K(ret)); } else { primary_map = &conflict_map_array_.at(0).conflict_map_; } return ret; } int ObConflictChecker::init_das_scan_rtdef() { int ret = OB_SUCCESS; ObPhysicalPlanCtx *plan_ctx = eval_ctx_.exec_ctx_.get_physical_plan_ctx(); ObSQLSessionInfo *my_session = eval_ctx_.exec_ctx_.get_my_session(); ObTaskExecutorCtx &task_exec_ctx = eval_ctx_.exec_ctx_.get_task_exec_ctx(); das_scan_rtdef_.timeout_ts_ = plan_ctx->get_ps_timeout_timestamp(); das_scan_rtdef_.sql_mode_ = my_session->get_sql_mode(); das_scan_rtdef_.stmt_allocator_.set_alloc(&das_ref_.get_das_alloc()); das_scan_rtdef_.scan_allocator_.set_alloc(&das_ref_.get_das_alloc()); ObQueryFlag query_flag(ObQueryFlag::Forward, // scan_order false, // daily_merge false, // optimize false, // sys scan false, // full_row false, // index_back false, // query_stat ObQueryFlag::MysqlMode, // sql_mode true // read_latest ); das_scan_rtdef_.scan_flag_.flag_ = query_flag.flag_; int64_t schema_version = task_exec_ctx.get_query_tenant_begin_schema_version(); das_scan_rtdef_.tenant_schema_version_ = schema_version; das_scan_rtdef_.eval_ctx_ = &eval_ctx_; if (OB_FAIL(das_scan_rtdef_.init_pd_op(eval_ctx_.exec_ctx_, checker_ctdef_.das_scan_ctdef_))) { LOG_WARN("init pushdown storage filter failed", K(ret)); } return ret; } } // namespace sql } // namespace oceanbase