Fix: fix duplicate delete same row induced by fk cascade delete

This commit is contained in:
obdev
2022-11-15 08:07:59 +00:00
committed by wangzelin.wzl
parent 2542756c05
commit 0b9d1f8cd0
18 changed files with 316 additions and 51 deletions

View File

@ -282,6 +282,7 @@ public:
: ref_action_(share::schema::ACTION_INVALID),
database_name_(),
table_name_(),
table_id_(0),
columns_(),
is_self_ref_(false)
{}
@ -290,6 +291,7 @@ public:
: ref_action_(share::schema::ACTION_INVALID),
database_name_(),
table_name_(),
table_id_(0),
columns_(alloc),
is_self_ref_(false)
{}
@ -298,6 +300,7 @@ public:
ref_action_ = share::schema::ACTION_INVALID;
database_name_.reset();
table_name_.reset();
table_id_ = 0;
columns_.reset();
}
TO_STRING_KV(K_(ref_action), K_(database_name), K_(table_name), K_(columns), K_(is_self_ref));
@ -305,6 +308,7 @@ public:
share::schema::ObReferenceAction ref_action_;
common::ObString database_name_;
common::ObString table_name_;
uint64_t table_id_;
common::ObFixedArray<ObForeignKeyColumn, common::ObIAllocator> columns_;
bool is_self_ref_;
};
@ -746,7 +750,7 @@ public:
virtual ~ObDelRtDef()
{
if (se_rowkey_dist_ctx_ != nullptr) {
se_rowkey_dist_ctx_->destroy();
// se_rowkey_dist_ctx_->destroy();
se_rowkey_dist_ctx_ = nullptr;
}
}
@ -757,7 +761,6 @@ public:
DASDelRtDefArray related_rtdefs_;
SeRowkeyDistCtx *se_rowkey_dist_ctx_;
};
struct ObMergeCtDef
{
OB_UNIS_VERSION(1);

View File

@ -120,43 +120,19 @@ int ObDMLService::check_rowkey_whether_distinct(const ObExprPtrIArray &row,
int64_t estimate_row,
DistinctType distinct_algo,
ObEvalCtx &eval_ctx,
SeRowkeyDistCtx *&rowkey_dist_ctx,
ObExecContext &root_ctx,
SeRowkeyDistCtx *rowkey_dist_ctx,
bool &is_dist)
{
int ret = OB_SUCCESS;
is_dist = true;
if (T_DISTINCT_NONE != distinct_algo) {
if (T_HASH_DISTINCT == distinct_algo) {
ObIAllocator &allocator = eval_ctx.exec_ctx_.get_allocator();
ObIAllocator &allocator = root_ctx.get_allocator();
if (OB_ISNULL(rowkey_dist_ctx)) {
//create rowkey distinct context
void *buf = allocator.alloc(sizeof(SeRowkeyDistCtx));
ObSQLSessionInfo *my_session = eval_ctx.exec_ctx_.get_my_session();
if (OB_ISNULL(buf)) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_WARN("allocate memory failed", K(ret), "size", sizeof(SeRowkeyDistCtx));
} else if (OB_ISNULL(my_session)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("my session is null", K(ret));
} else {
rowkey_dist_ctx = new (buf) SeRowkeyDistCtx();
int64_t match_rows = estimate_row > ObDMLBaseCtDef::MIN_ROWKEY_DISTINCT_BUCKET_NUM ?
estimate_row : ObDMLBaseCtDef::MIN_ROWKEY_DISTINCT_BUCKET_NUM;
// https://work.aone.alibaba-inc.com/issue/23348769
// match_rows是优化器估行的结果,如果这个值很大,
// 直接创建有这么多bucket的hashmap会申请
// 不到内存,这里做了限制为64k,防止报内存不足的错误
const int64_t max_bucket_num = match_rows > ObDMLBaseCtDef::MAX_ROWKEY_DISTINCT_BUCKET_NUM ?
ObDMLBaseCtDef::MAX_ROWKEY_DISTINCT_BUCKET_NUM : match_rows;
if (OB_FAIL(rowkey_dist_ctx->create(max_bucket_num,
ObModIds::OB_DML_CHECK_ROWKEY_DISTINCT_BUCKET,
ObModIds::OB_DML_CHECK_ROWKEY_DISTINCT_NODE,
my_session->get_effective_tenant_id()))) {
LOG_WARN("create rowkey distinct context failed", K(ret), "rows", estimate_row);
}
}
}
if (OB_SUCC(ret)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("distinct check hash set is null", K(ret));
} else {
SeRowkeyItem rowkey_item;
if (OB_ISNULL(rowkey_dist_ctx)) {
ret = OB_ERR_UNEXPECTED;
@ -188,6 +164,46 @@ int ObDMLService::check_rowkey_whether_distinct(const ObExprPtrIArray &row,
return ret;
}
int ObDMLService::create_rowkey_check_hashset(int64_t estimate_row,
ObExecContext *root_ctx,
SeRowkeyDistCtx *&rowkey_dist_ctx)
{
int ret = OB_SUCCESS;
ObIAllocator &allocator = root_ctx->get_allocator();
if (OB_ISNULL(rowkey_dist_ctx)) {
//create rowkey distinct context
void *buf = allocator.alloc(sizeof(SeRowkeyDistCtx));
ObSQLSessionInfo *my_session = root_ctx->get_my_session();
if (OB_ISNULL(buf)) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_WARN("allocate memory failed", K(ret), "size", sizeof(SeRowkeyDistCtx));
} else if (OB_ISNULL(my_session)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("my session is null", K(ret));
} else {
rowkey_dist_ctx = new (buf) SeRowkeyDistCtx();
int64_t match_rows = estimate_row > ObDMLBaseCtDef::MIN_ROWKEY_DISTINCT_BUCKET_NUM ?
estimate_row : ObDMLBaseCtDef::MIN_ROWKEY_DISTINCT_BUCKET_NUM;
// https://work.aone.alibaba-inc.com/issue/23348769
// match_rows是优化器估行的结果,如果这个值很大,
// 直接创建有这么多bucket的hashmap会申请
// 不到内存,这里做了限制为64k,防止报内存不足的错误
const int64_t max_bucket_num = match_rows > ObDMLBaseCtDef::MAX_ROWKEY_DISTINCT_BUCKET_NUM ?
ObDMLBaseCtDef::MAX_ROWKEY_DISTINCT_BUCKET_NUM : match_rows;
if (OB_FAIL(rowkey_dist_ctx->create(max_bucket_num,
ObModIds::OB_DML_CHECK_ROWKEY_DISTINCT_BUCKET,
ObModIds::OB_DML_CHECK_ROWKEY_DISTINCT_NODE,
my_session->get_effective_tenant_id()))) {
LOG_WARN("create rowkey distinct context failed", K(ret), "rows", estimate_row);
}
}
} else {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("Create hash set on a pointer that is not null", K(ret));
}
return ret;
}
int ObDMLService::check_row_whether_changed(const ObUpdCtDef &upd_ctdef,
ObUpdRtDef &upd_rtdef,
ObEvalCtx &eval_ctx)
@ -542,21 +558,30 @@ int ObDMLService::process_delete_row(const ObDelCtDef &del_ctdef,
is_skipped = true;
}
}
if (OB_SUCC(ret) && !is_skipped && !has_instead_of_trg) {
if (OB_SUCC(ret) && !is_skipped && !OB_ISNULL(del_rtdef.se_rowkey_dist_ctx_) && !has_instead_of_trg) {
bool is_distinct = false;
if (OB_FAIL(check_rowkey_whether_distinct(del_ctdef.distinct_key_,
del_ctdef.distinct_key_.count(),
dml_op.get_spec().rows_,
del_ctdef.distinct_algo_,
dml_op.get_eval_ctx(),
del_rtdef.se_rowkey_dist_ctx_,
is_distinct))) {
ObExecContext *root_ctx = nullptr;
if (OB_FAIL(dml_op.get_exec_ctx().get_root_ctx(root_ctx))) {
LOG_WARN("get root ExecContext failed", K(ret));
} else if (OB_ISNULL(root_ctx)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("the root ctx of foreign key nested session is null", K(ret));
} else if (OB_FAIL(check_rowkey_whether_distinct(del_ctdef.distinct_key_,
del_ctdef.distinct_key_.count(),
dml_op.get_spec().rows_,
T_HASH_DISTINCT,
dml_op.get_eval_ctx(),
*root_ctx,
del_rtdef.se_rowkey_dist_ctx_,
is_distinct))) {
LOG_WARN("check rowkey whether distinct failed", K(ret),
K(del_ctdef), K(del_rtdef), K(dml_op.get_spec().rows_));
K(del_ctdef), K(del_rtdef), K(dml_op.get_spec().rows_));
} else if (!is_distinct) {
is_skipped = true;
}
}
if (OB_SUCC(ret) && !is_skipped) {
if (!has_instead_of_trg && OB_FAIL(ForeignKeyHandle::do_handle(dml_op, del_ctdef, del_rtdef))) {
LOG_WARN("do handle old row for delete op failed", K(ret), K(del_ctdef), K(del_rtdef));
@ -619,6 +644,7 @@ int ObDMLService::process_update_row(const ObUpdCtDef &upd_ctdef,
dml_op.get_spec().rows_,
upd_ctdef.distinct_algo_,
dml_op.get_eval_ctx(),
dml_op.get_exec_ctx(),
upd_rtdef.se_rowkey_dist_ctx_,
is_distinct))) {
LOG_WARN("check rowkey whether distinct failed", K(ret),
@ -1058,6 +1084,39 @@ int ObDMLService::init_del_rtdef(ObDMLRtCtx &dml_rtctx,
del_rtdef.das_rtdef_.related_ctdefs_ = &del_ctdef.related_ctdefs_;
del_rtdef.das_rtdef_.related_rtdefs_ = &del_rtdef.related_rtdefs_;
}
if (OB_SUCC(ret)) {
ObTableModifyOp &dml_op = dml_rtctx.op_;
const uint64_t del_table_id = del_ctdef.das_base_ctdef_.index_tid_;
ObExecContext *root_ctx = nullptr;
if (OB_FAIL(dml_op.get_exec_ctx().get_root_ctx(root_ctx))) {
LOG_WARN("failed to get root exec ctx", K(ret));
} else if (OB_ISNULL(root_ctx)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("the root exec ctx is nullptr", K(ret));
} else {
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) && T_DISTINCT_NONE != del_ctdef.distinct_algo_) {
DmlRowkeyDistCtx del_ctx;
del_ctx.table_id_ = 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))) {
LOG_WARN("failed to push del ctx to list", K(ret));
} else {
del_rtdef.se_rowkey_dist_ctx_ = del_ctx.deleted_rows_;
}
} else if (T_DISTINCT_NONE != del_ctdef.distinct_algo_ &&
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));
} else if (dml_op.is_fk_nested_session() && 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));
}
}
}
return ret;
}
@ -1114,6 +1173,15 @@ int ObDMLService::init_upd_rtdef(
upd_rtdef.dupd_rtdef_.related_rtdefs_ = &upd_rtdef.related_upd_rtdefs_;
dml_rtctx.get_exec_ctx().set_update_columns(&upd_ctdef.assign_columns_);
}
if (OB_SUCC(ret) && T_DISTINCT_NONE != upd_ctdef.distinct_algo_) {
ObTableModifyOp &dml_op = dml_rtctx.op_;
if (OB_FAIL(create_rowkey_check_hashset(dml_op.get_spec().rows_,
&dml_op.get_exec_ctx(),
upd_rtdef.se_rowkey_dist_ctx_))) {
LOG_WARN("failed to create distinct check hash set", K(ret));
}
}
return ret;
}
@ -1657,5 +1725,33 @@ int ObDMLService::convert_exprs_to_row(const ExprFixedArray &exprs,
}
return ret;
}
bool ObDMLService::is_nested_dup_table(const uint64_t table_id, DASDelCtxList& del_ctx_list)
{
bool ret = false;
DASDelCtxList::iterator iter = del_ctx_list.begin();
for (; !ret && iter != del_ctx_list.end(); iter++) {
DmlRowkeyDistCtx del_ctx = *iter;
if (del_ctx.table_id_ == table_id) {
ret = true;
}
}
return ret;
}
int ObDMLService::get_nested_dup_table_ctx(const uint64_t table_id, DASDelCtxList& del_ctx_list, SeRowkeyDistCtx* &rowkey_dist_ctx)
{
int ret = OB_SUCCESS;
bool find = false;
DASDelCtxList::iterator iter = del_ctx_list.begin();
for (; !find && iter != del_ctx_list.end(); iter++) {
DmlRowkeyDistCtx del_ctx = *iter;
if (del_ctx.table_id_ == table_id) {
find = true;
rowkey_dist_ctx = del_ctx.deleted_rows_;
}
}
return ret;
}
} // namespace sql
} // namespace oceanbase

View File

@ -13,6 +13,7 @@
#ifndef DEV_SRC_SQL_ENGINE_DML_OB_DML_SERVICE_H_
#define DEV_SRC_SQL_ENGINE_DML_OB_DML_SERVICE_H_
#include "sql/engine/dml/ob_dml_ctx_define.h"
#include "sql/das/ob_das_context.h"
namespace oceanbase
{
namespace sql
@ -41,8 +42,13 @@ public:
int64_t estimate_row,
DistinctType distinct_algo,
ObEvalCtx &eval_ctx,
SeRowkeyDistCtx *&rowkey_dist_ctx,
ObExecContext &root_ctx,
SeRowkeyDistCtx *rowkey_dist_ctx,
bool &is_dist);
static int create_rowkey_check_hashset(int64_t estimate_row,
ObExecContext *root_ctx,
SeRowkeyDistCtx *&rowkey_dist_ctx);
static int check_row_whether_changed(const ObUpdCtDef &upd_ctdef, ObUpdRtDef &upd_rtdef, ObEvalCtx &eval_ctx);
static int filter_row_for_check_cst(const ExprFixedArray &cst_exprs,
ObEvalCtx &eval_ctx,
@ -200,6 +206,11 @@ public:
const ExprFixedArray &row,
const ObDMLBaseCtDef &dml_ctdef,
ObDMLBaseRtDef &dml_rtdef);
static bool is_nested_dup_table(const uint64_t table_id,DASDelCtxList& del_ctx_list);
static int get_nested_dup_table_ctx(const uint64_t table_id,
DASDelCtxList& del_ctx_list,
SeRowkeyDistCtx* &rowkey_dist_ctx);
private:
template <int N>
static int write_row_to_das_op(const ObDASDMLBaseCtDef &ctdef,

View File

@ -11,7 +11,7 @@
*/
#define USING_LOG_PREFIX SQL_ENG
#include "common/ob_smart_call.h"
#include "sql/engine/dml/ob_table_delete_op.h"
#include "sql/session/ob_sql_session_info.h"
#include "sql/executor/ob_task_executor_ctx.h"

View File

@ -169,6 +169,8 @@ int ObTableMergeOp::open_table_for_each()
int ret = OB_SUCCESS;
if (OB_FAIL(merge_rtdefs_.allocate_array(ctx_.get_allocator(), MY_SPEC.merge_ctdefs_.count()))) {
LOG_WARN("allocate merge rtdef failed", K(ret), K(MY_SPEC.merge_ctdefs_.count()));
} else if (OB_FAIL(ObDMLService::create_rowkey_check_hashset(get_spec().rows_, &ctx_, merge_rtdefs_.at(0).rowkey_dist_ctx_))) {
LOG_WARN("Failed to create hash set", K(ret));
}
trigger_clear_exprs_.reset();
for (int64_t i = 0; OB_SUCC(ret) && i < MY_SPEC.merge_ctdefs_.count(); ++i) {
@ -402,6 +404,7 @@ int ObTableMergeOp::check_is_distinct(bool &conflict)
MY_SPEC.rows_,
DistinctType::T_HASH_DISTINCT,
get_eval_ctx(),
get_exec_ctx(),
merge_rtdefs_.at(0).rowkey_dist_ctx_,
// merge_rtdefs_ length must > 0
is_distinct))) {

View File

@ -82,7 +82,7 @@ int ForeignKeyHandle::do_handle(ObTableModifyOp &op,
bool is_self_ref = false;
if (OB_FAIL(is_self_ref_row(op.get_eval_ctx(), old_row, fk_arg, is_self_ref))) {
LOG_WARN("is_self_ref_row failed", K(ret), K(old_row), K(fk_arg));
} else if (new_row.empty() && is_self_ref && op.is_nested_session()) {
} else if (new_row.empty() && is_self_ref && op.is_fk_nested_session()) {
// delete self refercnced row should not cascade delete.
} else if (OB_FAIL(cascade(op, fk_arg, old_row, new_row))) {
LOG_WARN("failed to cascade", K(ret), K(fk_arg), K(old_row), K(new_row));
@ -519,7 +519,6 @@ ObTableModifyOp::ObTableModifyOp(ObExecContext &ctx,
inner_conn_(NULL),
tenant_id_(0),
saved_conn_(),
is_nested_session_(false),
foreign_key_checks_(false),
need_close_conn_(false),
iter_end_(false),
@ -536,6 +535,20 @@ 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;
@ -679,6 +692,16 @@ int ObTableModifyOp::inner_close()
dml_rtctx_.das_ref_.reset();
}
}
// Release the hash sets created at root ctx for delete distinct check
if (OB_SUCC(ret) && get_exec_ctx().is_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;
del_ctx.deleted_rows_->destroy();
}
del_ctx_list.destroy();
}
return ret;
}
@ -813,7 +836,6 @@ int ObTableModifyOp::open_inner_conn()
if (OB_SUCC(ret)) {
inner_conn_ = static_cast<ObInnerSQLConnection *>(session->get_inner_conn());
tenant_id_ = session->get_effective_tenant_id();
is_nested_session_ = ObSQLUtils::is_nested_sql(&ctx_);
}
return ret;
}

View File

@ -194,9 +194,11 @@ public:
int execute_write(const char *sql);
int execute_read(const char *sql, common::ObMySQLProxy::MySQLResult &res);
int check_stack();
bool is_nested_session() { return is_nested_session_; }
bool is_nested_session() { return ObSQLUtils::is_nested_sql(&ctx_); }
bool is_fk_nested_session() { return ObSQLUtils::is_fk_nested_sql(&ctx_); }
void set_foreign_key_checks() { foreign_key_checks_ = true; }
bool need_foreign_key_checks() { return foreign_key_checks_; }
bool is_fk_root_session();
const ObObjPrintParams &get_obj_print_params() { return obj_print_params_; }
int init_foreign_key_operation();
void log_user_error_inner(int ret, int64_t col_idx, int64_t row_num,
@ -241,7 +243,6 @@ public:
observer::ObInnerSQLConnection *inner_conn_;
uint64_t tenant_id_;
observer::ObInnerSQLConnection::SavedValue saved_conn_;
bool is_nested_session_;
bool foreign_key_checks_;
bool need_close_conn_;

View File

@ -11,6 +11,7 @@
*/
#define USING_LOG_PREFIX SQL_ENG
#include "common/ob_smart_call.h"
#include "sql/engine/dml/ob_table_replace_op.h"
#include "share/ob_autoincrement_service.h"
#include "sql/engine/ob_physical_plan_ctx.h"
@ -93,6 +94,7 @@ OB_DEF_SERIALIZE_SIZE(ObTableReplaceSpec)
return len;
}
int ObTableReplaceOp::inner_open()
{
int ret = OB_SUCCESS;

View File

@ -268,9 +268,9 @@ int ObExprToOutfileRow::print_field(char *buf, const int64_t buf_len, int64_t &p
ObString tmp_str(out_info.tmp_buf_len_, tmp_pos, out_info.tmp_buf_);
OZ(ObCharsetUtils::foreach_char(tmp_str, out_info.print_params_.cs_type_, escape_func));
}
if (need_enclose) {
OZ(out_info.enclose_.print_plain_str_literal(buf, buf_len, pos, out_info.print_params_));
}
if (need_enclose) {
OZ(out_info.enclose_.print_plain_str_literal(buf, buf_len, pos, out_info.print_params_));
}
return ret;
}

View File

@ -224,6 +224,21 @@ void ObExecContext::reset_op_env()
}
}
int ObExecContext::get_root_ctx(ObExecContext* &root_ctx)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(this->get_parent_ctx())) {
root_ctx = this;
} else if (OB_ISNULL(get_parent_ctx()->get_pl_stack_ctx())) {
root_ctx = this;
} else if (get_parent_ctx()->get_pl_stack_ctx()->in_autonomous()) {
root_ctx = this;
} else if (OB_FAIL( SMART_CALL(get_parent_ctx()->get_root_ctx(root_ctx)))) {
LOG_WARN("failed to get root ctx", K(ret));
}
return ret;
}
int ObExecContext::init_phy_op(const uint64_t phy_op_size)
{
int ret = OB_SUCCESS;

View File

@ -207,6 +207,9 @@ 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 in nested sql
int get_root_ctx(ObExecContext* &root_ctx);
bool is_root_ctx() {return parent_ctx_ == nullptr;}
int64_t get_nested_level() const { return nested_level_; }
/**
* @brief set sql proxy

View File

@ -90,6 +90,7 @@ int ObPxMultiPartDeleteOp::check_rowkey_distinct(const ObExprPtrIArray &row,
MY_SPEC.rows_,
MY_SPEC.del_ctdef_.distinct_algo_,
eval_ctx_,
ctx_,
del_rtdef_.se_rowkey_dist_ctx_,
is_distinct);
} else {