[CP] Fix: fix memory leak induced by foreign key casacde delete check duplicate rowkey
This commit is contained in:
@ -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
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
Reference in New Issue
Block a user