Fix: fix duplicate delete same row induced by fk cascade delete
This commit is contained in:
@ -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))))) {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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_;
|
||||
|
||||
Reference in New Issue
Block a user