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

@ -2369,6 +2369,9 @@ int ObDmlCgService::generate_fk_arg(ObForeignKeyArg &fk_arg,
} else if (OB_FAIL(fk_arg.columns_.reserve(name_column_ids.count()))) {
LOG_WARN("failed to reserve foreign key columns", K(name_column_ids.count()), K(ret));
}
if ( OB_SUCC(ret) && need_handle) {
fk_arg.table_id_ = name_table_id;
}
for (int64_t i = 0; OB_SUCC(ret) && need_handle && i < name_column_ids.count(); i++) {
ObForeignKeyColumn fk_column;
if (OB_ISNULL(column_schema = (table_schema->get_column_schema(name_column_ids.at(i))))) {

View File

@ -2036,6 +2036,23 @@ int ObStaticEngineCG::generate_delete_with_das(ObLogDelete &op, ObTableDeleteSpe
}
} // for index_dml_infos end
} //for table_columns end
for (int64_t i = 0; OB_SUCC(ret) && i < delete_table_list.count(); ++i) {
ObTableDeleteSpec::DelCtDefArray &ctdefs = spec.del_ctdefs_.at(i);
ObDelCtDef &del_ctdef = *ctdefs.at(0);
const uint64_t del_table_id = del_ctdef.das_base_ctdef_.index_tid_;
bool is_dup = false;
for (int j = 0; !is_dup && OB_SUCC(ret) && j < delete_table_list.count(); ++j) {
const uint64_t root_table_id = spec.del_ctdefs_.at(j).at(0)->das_base_ctdef_.index_tid_;
DASTableIdList parent_tables(phy_plan_->get_allocator());
if(OB_FAIL(check_fk_nested_dup_del(del_table_id, root_table_id, parent_tables, is_dup))) {
LOG_WARN("failed to perform nested duplicate table check", K(ret), K(del_table_id), K(root_table_id));
}
}
if (OB_SUCC(ret) && is_dup) {
del_ctdef.distinct_algo_ = T_HASH_DISTINCT;
}
}
return ret;
}
@ -2091,6 +2108,15 @@ int ObStaticEngineCG::generate_spec(ObLogInsert &op, ObTableReplaceSpec &spec, c
LOG_WARN("generate conflict_checker failed", K(ret));
} else if (OB_FAIL(mark_expr_self_produced(index_dml_info->column_exprs_))) {
LOG_WARN("mark self expr failed", K(ret));
} else {
bool is_dup = false;
const uint64_t replace_table_id = replace_ctdef->del_ctdef_->das_base_ctdef_.index_tid_;
DASTableIdList parent_tables(phy_plan_->get_allocator());
if(OB_FAIL(check_fk_nested_dup_del(replace_table_id, replace_table_id, parent_tables, is_dup))) {
LOG_WARN("failed to perform nested duplicate table check", K(ret), K(replace_table_id));
} else if (is_dup) {
replace_ctdef->del_ctdef_->distinct_algo_ = T_HASH_DISTINCT;
}
}
}
spec.replace_ctdefs_.at(i) = replace_ctdef;
@ -6702,5 +6728,58 @@ int ObStaticEngineCG::check_only_one_unique_key(const ObLogPlan& log_plan,
return ret;
}
bool ObStaticEngineCG::has_cycle_reference(DASTableIdList &parent_tables, const uint64_t table_id)
{
bool ret = false;
if (!parent_tables.empty()) {
DASTableIdList::iterator iter = parent_tables.begin();
for (; !ret && iter != parent_tables.end(); iter++) {
if (*iter == table_id) {
ret = true;
}
}
}
return ret;
}
int ObStaticEngineCG::check_fk_nested_dup_del(const uint64_t table_id,
const uint64_t root_table_id,
DASTableIdList &parent_tables,
bool &is_dup)
{
int ret = OB_SUCCESS;
ObSchemaGetterGuard schema_guard;
const ObTableSchema *table_schema = NULL;
const uint64_t tenant_id = MTL_ID();
if (OB_FAIL(parent_tables.push_back(root_table_id))) {
LOG_WARN("failed to push root_table_id to parent tables list", K(ret), K(root_table_id), K(parent_tables.size()));
} else if (OB_FAIL(GCTX.schema_service_->get_tenant_schema_guard(tenant_id, schema_guard))) {
LOG_WARN("get tenant schema guard failed", K(ret));
} else if (OB_FAIL(schema_guard.get_table_schema(tenant_id, root_table_id, table_schema))) {
LOG_WARN("get table schema failed", K(ret), K(tenant_id), K(root_table_id));
} else if (!OB_ISNULL(table_schema)) {
const common::ObIArray<ObForeignKeyInfo> &foreign_key_infos = table_schema->get_foreign_key_infos();
for (int64_t i = 0; OB_SUCC(ret) && i < foreign_key_infos.count() && !is_dup; ++i) {
const ObForeignKeyInfo &fk_info = foreign_key_infos.at(i);
const uint64_t child_table_id = fk_info.child_table_id_;
const uint64_t parent_table_id = fk_info.parent_table_id_;
ObReferenceAction del_act = fk_info.delete_action_;
if (child_table_id != common::OB_INVALID_ID && del_act == ACTION_CASCADE) {
if (child_table_id == table_id) {
is_dup = true;
} else if (has_cycle_reference(parent_tables, child_table_id)) {
LOG_DEBUG("This schema has a circular foreign key dependencies");
} else if (OB_FAIL(SMART_CALL(check_fk_nested_dup_del(table_id, child_table_id, parent_tables, is_dup)))) {
LOG_WARN("failed deep search nested duplicate delete table", K(ret), K(table_id), K(root_table_id), K(child_table_id));
}
}
}
}
if (OB_SUCC(ret) && OB_FAIL(parent_tables.pop_back())) {
LOG_WARN("failed to pop latest table id", K(ret));
}
return ret;
}
} // end namespace sql
} // end namespace oceanbase

View File

@ -124,6 +124,7 @@ class ObDuplicatedKeyChecker;
struct ObTableScanCtDef;
struct ObDASScanCtDef;
struct InsertAllTableInfo;
typedef common::ObList<uint64_t, common::ObIAllocator> DASTableIdList;
typedef common::ObSEArray<common::ObSEArray<int64_t, 8, common::ModulePageAllocator, true>,
1, common::ModulePageAllocator, true> RowParamMap;
//
@ -461,6 +462,11 @@ private:
|| T_FUN_SUM == expr_type
|| T_FUN_MAX == expr_type
|| T_FUN_MIN == expr_type; }
int check_fk_nested_dup_del(const uint64_t table_id,
const uint64_t root_table_id,
DASTableIdList &parent_tables,
bool &is_dup);
bool has_cycle_reference(DASTableIdList &parent_tables, const uint64_t table_id);
private:
ObPhysicalPlan *phy_plan_;
ObOptimizerContext *opt_ctx_;