[CP] Fix: fix memory leak induced by foreign key casacde delete check duplicate rowkey

This commit is contained in:
obdev
2024-03-27 04:15:38 +00:00
committed by ob-robot
parent c169610149
commit 62eb9bd22b
10 changed files with 68 additions and 193 deletions

View File

@ -742,7 +742,7 @@ int ObDMLService::process_delete_row(const ObDelCtDef &del_ctdef,
if (OB_SUCC(ret) && !is_skipped && OB_NOT_NULL(del_rtdef.se_rowkey_dist_ctx_) && !has_instead_of_trg) {
bool is_distinct = false;
ObExecContext *root_ctx = nullptr;
if (OB_FAIL(dml_op.get_exec_ctx().get_fk_root_ctx(root_ctx))) {
if (OB_FAIL(get_exec_ctx_for_duplicate_rowkey_check(&dml_op.get_exec_ctx(), root_ctx))) {
LOG_WARN("get root ExecContext failed", K(ret));
} else if (OB_ISNULL(root_ctx)) {
ret = OB_ERR_UNEXPECTED;
@ -1401,7 +1401,7 @@ int ObDMLService::init_del_rtdef(ObDMLRtCtx &dml_rtctx,
if (dml_op.is_fk_nested_session()) {
// for delete distinct check that has foreign key, perform global distinct check between nested session,
// to avoid delete same row mutiple times between different nested sqls
if (OB_FAIL(dml_op.get_exec_ctx().get_fk_root_ctx(root_ctx))) {
if (OB_FAIL(get_exec_ctx_for_duplicate_rowkey_check(&dml_op.get_exec_ctx(), root_ctx))) {
LOG_WARN("failed to get root exec ctx", K(ret));
} else if (OB_ISNULL(root_ctx)) {
ret = OB_ERR_UNEXPECTED;
@ -1417,6 +1417,7 @@ int ObDMLService::init_del_rtdef(ObDMLRtCtx &dml_rtctx,
// for table not deleted at parent session, create a new hash set and add to the list at root ctx
DmlRowkeyDistCtx del_ctx;
del_ctx.table_id_ = del_table_id;
LOG_TRACE("[FOREIGN KEY] create hash set used for checking duplicate rowkey due to cascade delete", K(del_table_id));
if (OB_FAIL(ObDMLService::create_rowkey_check_hashset(dml_op.get_spec().rows_, root_ctx, del_ctx.deleted_rows_))) {
LOG_WARN("failed to create hash set", K(ret));
} else if (OB_FAIL(del_ctx_list.push_back(del_ctx))) {
@ -1435,7 +1436,8 @@ int ObDMLService::init_del_rtdef(ObDMLRtCtx &dml_rtctx,
DASDelCtxList& del_ctx_list = dml_op.get_exec_ctx().get_das_ctx().get_das_del_ctx_list();
DmlRowkeyDistCtx del_ctx;
del_ctx.table_id_ = del_table_id;
if (OB_FAIL(dml_op.get_exec_ctx().get_fk_root_ctx(root_ctx))) {
LOG_TRACE("[FOREIGN KEY] create hash set used for checking duplicate rowkey due to cascade delete", K(del_table_id));
if (OB_FAIL(get_exec_ctx_for_duplicate_rowkey_check(&dml_op.get_exec_ctx(), root_ctx))) {
LOG_WARN("failed to get root exec ctx", K(ret));
} else if (OB_ISNULL(root_ctx) || root_ctx != &dml_op.get_exec_ctx()) {
ret = OB_ERR_UNEXPECTED;
@ -1453,7 +1455,7 @@ int ObDMLService::init_del_rtdef(ObDMLRtCtx &dml_rtctx,
}
} else { //T_DISTINCT_NONE == del_ctdef.distinct_algo_, means optimizer think don't need to create a new hash set for distinct check
if (dml_op.is_fk_nested_session()) { //for delete triggered by delete cascade, need to check whether upper nested sqls will delete the same table
if (OB_FAIL(dml_op.get_exec_ctx().get_fk_root_ctx(root_ctx))) {
if (OB_FAIL(get_exec_ctx_for_duplicate_rowkey_check(&dml_op.get_exec_ctx(), root_ctx))) {
LOG_WARN("failed to get root exec ctx", K(ret));
} else if (OB_ISNULL(root_ctx)) {
ret = OB_ERR_UNEXPECTED;
@ -1462,6 +1464,7 @@ int ObDMLService::init_del_rtdef(ObDMLRtCtx &dml_rtctx,
DASDelCtxList& del_ctx_list = root_ctx->get_das_ctx().get_das_del_ctx_list();
if (ObDMLService::is_nested_dup_table(del_table_id, del_ctx_list)) {
// A duplicate table was found
LOG_TRACE("[FOREIGN KEY] get hash set used for checking duplicate rowkey due to cascade delete", K(del_table_id));
if (OB_FAIL(ObDMLService::get_nested_dup_table_ctx(del_table_id, del_ctx_list, del_rtdef.se_rowkey_dist_ctx_))) {
LOG_WARN("failed to get nested duplicate delete table ctx for fk nested session", K(ret));
}
@ -2448,5 +2451,31 @@ int ObDMLService::log_user_error_inner(
return ret;
}
// get the exec_ctx to create hash set to perform duplicate rowkey check,
// which is used to avoid delete or update same row mutiple times
int ObDMLService::get_exec_ctx_for_duplicate_rowkey_check(ObExecContext *ctx, ObExecContext* &needed_ctx)
{
int ret = OB_SUCCESS;
ObExecContext *parent_ctx = nullptr;
needed_ctx = nullptr;
if (OB_ISNULL(ctx)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null pointer", K(ret));
} else if (OB_ISNULL(parent_ctx = ctx->get_parent_ctx())) {
// case1: current ctx is the root ctx, means current stmt is the root stmt,
// create hash set at current ctx to perform duplicate rowkey check
needed_ctx = ctx;
} else if (!(parent_ctx->get_das_ctx().is_fk_cascading_)) {
// case2: current stmt is not the root stmt, is not triggered by foreign key cascade operations
// may be trigger or PL instead, create hash set at current ctx to perform duplicate rowkey check
needed_ctx = ctx;
} else if (OB_FAIL(SMART_CALL(get_exec_ctx_for_duplicate_rowkey_check(parent_ctx, needed_ctx)))) {
// case3: current stmt is a nested stmt, and is triggered by foreign key cascade operations
// need to find it's ancestor ctx which is not triggered by cascade operations
LOG_WARN("failed to get the exec_ctx to perform duplicate rowkey check between nested sqls", K(ret), K(ctx->get_nested_level()));
}
return ret;
}
} // namespace sql
} // namespace oceanbase

View File

@ -249,6 +249,7 @@ public:
int64_t row_num,
common::ObString &column_name,
ObExecContext &ctx);
static int get_exec_ctx_for_duplicate_rowkey_check(ObExecContext *ctx, ObExecContext* &needed_ctx);
private:
template <int N>

View File

@ -57,10 +57,8 @@ int ForeignKeyHandle::do_handle(ObTableModifyOp &op,
if (OB_SUCC(ret) && !new_row.empty()) {
if (ACTION_CHECK_EXIST == fk_arg.ref_action_) {
// insert or update.
bool is_foreign_key_cascade = false;
if (OB_FAIL(op.get_foreign_key_cascade(is_foreign_key_cascade))) {
LOG_WARN("failed to get foreign key cascade", K(ret), K(fk_arg), K(new_row));
} else if (is_foreign_key_cascade) {
bool is_foreign_key_cascade = ObSQLUtils::is_fk_nested_sql(&op.get_exec_ctx());
if (is_foreign_key_cascade) {
// nested update can not check parent row.
LOG_DEBUG("skip foreign_key_check_exist in nested session");
} else if (OB_FAIL(check_exist(op, fk_arg, new_row, fk_checker, false))) {
@ -247,8 +245,6 @@ int ForeignKeyHandle::check_exist_inner_sql(ObTableModifyOp &op,
SMART_VAR(ObMySQLProxy::MySQLResult, res) {
if (OB_FAIL(op.begin_nested_session(fk_arg.is_self_ref_))) {
LOG_WARN("failed to begin nested session", K(ret), K(stmt_buf));
} else if (OB_FAIL(op.set_foreign_key_check_exist(true))) {
LOG_WARN("failed to set foreign key cascade", K(ret));
} else {
// must call end_nested_session() if begin_nested_session() success.
bool is_zero = false;
@ -314,13 +310,6 @@ int ForeignKeyHandle::check_exist_inner_sql(ObTableModifyOp &op,
}
}
}
int reset_ret = op.set_foreign_key_check_exist(false);
if (OB_SUCCESS != reset_ret) {
LOG_WARN("failed to reset foreign key cascade", K(reset_ret));
if (OB_SUCCESS == ret) {
ret = reset_ret;
}
}
int end_ret = op.end_nested_session();
if (OB_SUCCESS != end_ret) {
LOG_WARN("failed to end nested session", K(end_ret));
@ -408,22 +397,9 @@ int ForeignKeyHandle::cascade(ObTableModifyOp &op,
if (OB_FAIL(op.begin_nested_session(fk_arg.is_self_ref_))) {
LOG_WARN("failed to begin nested session", K(ret));
} else {
// must call end_nested_session() if begin_nested_session() success.
//
// skip modify_ctx.set_foreign_key_cascade when cascade update and self ref.
if (!(fk_arg.is_self_ref_ && !new_row.empty()) &&
OB_FAIL(op.set_foreign_key_cascade(true))) {
LOG_WARN("failed to set foreign key cascade", K(ret));
} else if (OB_FAIL(op.execute_write(stmt_buf))) {
if (OB_FAIL(op.execute_write(stmt_buf))) {
LOG_WARN("failed to execute stmt", K(ret), K(stmt_buf));
}
int reset_ret = op.set_foreign_key_cascade(false);
if (OB_SUCCESS != reset_ret) {
LOG_WARN("failed to reset foreign key cascade", K(reset_ret));
if (OB_SUCCESS == ret) {
ret = reset_ret;
}
}
int end_ret = op.end_nested_session();
if (OB_SUCCESS != end_ret) {
LOG_WARN("failed to end nested session", K(end_ret));
@ -490,18 +466,9 @@ int ForeignKeyHandle::set_null(ObTableModifyOp &op,
if (OB_FAIL(op.begin_nested_session(fk_arg.is_self_ref_))) {
LOG_WARN("failed to begin nested session", K(ret));
} else {
if (OB_FAIL(op.set_foreign_key_cascade(true))) {
LOG_WARN("failed to set foreign key cascade", K(ret));
} else if (OB_FAIL(op.execute_write(stmt_buf))) {
if (OB_FAIL(op.execute_write(stmt_buf))) {
LOG_WARN("failed to execute stmt", K(ret), K(stmt_buf));
}
int reset_ret = op.set_foreign_key_cascade(false);
if (OB_SUCCESS != reset_ret) {
LOG_WARN("failed to reset foreign key cascade", K(reset_ret));
if (OB_SUCCESS == ret) {
ret = reset_ret;
}
}
int end_ret = op.end_nested_session();
if (OB_SUCCESS != end_ret) {
LOG_WARN("failed to end nested session", K(end_ret));
@ -718,20 +685,6 @@ ObTableModifyOp::ObTableModifyOp(ObExecContext &ctx,
GET_SQL_MODE_BIT(IS_NO_BACKSLASH_ESCAPES, ctx_.get_my_session()->get_sql_mode(), obj_print_params_.skip_escape_);
}
bool ObTableModifyOp::is_fk_root_session() {
bool ret = false;
if (OB_ISNULL(ctx_.get_parent_ctx())) {
if (this->need_foreign_key_checks()) {
ret = true;
}
} else {
if (!ctx_.get_parent_ctx()->get_das_ctx().is_fk_cascading_ && need_foreign_key_checks()) {
ret = true;
}
}
return ret;
}
int ObTableModifyOp::inner_open()
{
int ret = OB_SUCCESS;
@ -879,19 +832,6 @@ int ObTableModifyOp::inner_close()
}
}
dml_modify_rows_.clear();
// Release the hash sets created at fk root ctx for delete distinct checks
if (OB_SUCC(ret) && get_exec_ctx().is_fk_root_ctx()) {
DASDelCtxList& del_ctx_list = get_exec_ctx().get_das_ctx().get_das_del_ctx_list();
DASDelCtxList::iterator iter = del_ctx_list.begin();
for (; OB_SUCC(ret)&& iter != del_ctx_list.end(); iter++) {
DmlRowkeyDistCtx del_ctx = *iter;
if (del_ctx.deleted_rows_ != nullptr) {
del_ctx.deleted_rows_->destroy();
del_ctx.deleted_rows_ = nullptr;
}
}
del_ctx_list.destroy();
}
return ret;
}
@ -1086,43 +1026,6 @@ int ObTableModifyOp::end_nested_session()
}
return ret;
}
int ObTableModifyOp::set_foreign_key_cascade(bool is_cascade)
{
int ret = OB_SUCCESS;
OV (OB_NOT_NULL(inner_conn_));
OZ (inner_conn_->set_foreign_key_cascade(is_cascade));
return ret;
}
int ObTableModifyOp::get_foreign_key_cascade(bool &is_cascade) const
{
int ret = OB_SUCCESS;
if (OB_NOT_NULL(inner_conn_)) {
OZ (inner_conn_->get_foreign_key_cascade(is_cascade));
} else {
//if inner connection is null, it means not need use inner sql, so set is_cascade false
is_cascade = false;
}
return ret;
}
int ObTableModifyOp::set_foreign_key_check_exist(bool is_check_exist)
{
int ret = OB_SUCCESS;
OV (OB_NOT_NULL(inner_conn_));
OZ (inner_conn_->set_foreign_key_check_exist(is_check_exist));
return ret;
}
int ObTableModifyOp::get_foreign_key_check_exist(bool &is_check_exist) const
{
int ret = OB_SUCCESS;
OV (OB_NOT_NULL(inner_conn_));
OZ (inner_conn_->get_foreign_key_check_exist(is_check_exist));
return ret;
}
int ObTableModifyOp::execute_write(const char *sql)
{
int ret = OB_SUCCESS;

View File

@ -233,31 +233,6 @@ void ObExecContext::reset_op_env()
udf_ctx_mgr_->reset();
}
}
int ObExecContext::get_fk_root_ctx(ObExecContext* &fk_root_ctx)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(this->get_parent_ctx())) {
fk_root_ctx = this;
} else if (!this->get_my_session()->is_foreign_key_cascade()) {
fk_root_ctx = this;
} else if (OB_FAIL(SMART_CALL(get_parent_ctx()->get_fk_root_ctx(fk_root_ctx)))) {
LOG_WARN("failed to get fk root ctx", K(ret));
}
return ret;
}
bool ObExecContext::is_fk_root_ctx()
{
bool ret = false;
if (OB_ISNULL(this->get_parent_ctx())) {
ret = true;
} else if (!this->get_my_session()->is_foreign_key_cascade()) {
ret = true;
}
return ret;
}
int ObExecContext::init_phy_op(const uint64_t phy_op_size)
{
int ret = OB_SUCCESS;

View File

@ -208,9 +208,6 @@ public:
inline ObSQLSessionInfo *get_my_session() const;
//get the parent execute context in nested sql
ObExecContext *get_parent_ctx() { return parent_ctx_; }
//get the root execute context of foreign key in nested sql
int get_fk_root_ctx(ObExecContext* &root_ctx);
bool is_fk_root_ctx();
int64_t get_nested_level() const { return nested_level_; }
/**
* @brief set sql proxy