diff options
author | Nikita Malyavin <nikitamalyavin@gmail.com> | 2019-08-19 23:57:42 +1000 |
---|---|---|
committer | Nikita Malyavin <nikitamalyavin@gmail.com> | 2020-01-21 20:35:06 +1000 |
commit | d823652daa3bc37835ba7ebabf9c08dc740c5bd2 (patch) | |
tree | 8ccb388248b0fdd7f87022eb41225f152c6656fc | |
parent | 0e86b4a473d3dc0aeb9567f61f21208b010a7107 (diff) | |
download | mariadb-git-d823652daa3bc37835ba7ebabf9c08dc740c5bd2.tar.gz |
Prelock child tables in addition to parent ones
-rw-r--r-- | mysql-test/suite/innodb/r/truncate_foreign.result | 7 | ||||
-rw-r--r-- | mysql-test/suite/innodb/t/truncate_foreign.test | 4 | ||||
-rw-r--r-- | sql/sql_base.cc | 76 | ||||
-rw-r--r-- | sql/table.h | 4 |
4 files changed, 54 insertions, 37 deletions
diff --git a/mysql-test/suite/innodb/r/truncate_foreign.result b/mysql-test/suite/innodb/r/truncate_foreign.result index bcf5b16aa83..55e8b6b44be 100644 --- a/mysql-test/suite/innodb/r/truncate_foreign.result +++ b/mysql-test/suite/innodb/r/truncate_foreign.result @@ -33,6 +33,7 @@ TRUNCATE TABLE child; ERROR HY000: Lock wait timeout exceeded; try restarting transaction SET DEBUG_SYNC='now SIGNAL go'; connection dml; +# Truncate will not start until delete will release prelocked child ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`child`, CONSTRAINT `child_ibfk_1` FOREIGN KEY (`a`) REFERENCES `parent` (`a`) ON UPDATE CASCADE) SELECT * FROM child; a @@ -44,14 +45,18 @@ connection default; SET DEBUG_SYNC='now WAIT_FOR fk'; SET foreign_key_checks=0; TRUNCATE TABLE parent; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction SET DEBUG_SYNC='now SIGNAL go'; connection dml; -ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`child`, CONSTRAINT `child_ibfk_1` FOREIGN KEY (`a`) REFERENCES `parent` (`a`) ON UPDATE CASCADE) +# The row will be successfully inserted before truncation during prelocking SELECT * FROM parent; a +3 +5 SELECT * FROM child; a 3 +5 disconnect dml; connection default; SET DEBUG_SYNC = RESET; diff --git a/mysql-test/suite/innodb/t/truncate_foreign.test b/mysql-test/suite/innodb/t/truncate_foreign.test index 2c00c0641e9..b185d836b3a 100644 --- a/mysql-test/suite/innodb/t/truncate_foreign.test +++ b/mysql-test/suite/innodb/t/truncate_foreign.test @@ -42,6 +42,7 @@ TRUNCATE TABLE child; SET DEBUG_SYNC='now SIGNAL go'; connection dml; +--echo # Truncate will not start until delete will release prelocked child --error ER_ROW_IS_REFERENCED_2 reap; SELECT * FROM child; @@ -52,11 +53,12 @@ send INSERT INTO child SET a=5; connection default; SET DEBUG_SYNC='now WAIT_FOR fk'; SET foreign_key_checks=0; +--error ER_LOCK_WAIT_TIMEOUT TRUNCATE TABLE parent; SET DEBUG_SYNC='now SIGNAL go'; connection dml; ---error ER_NO_REFERENCED_ROW_2 +--echo # The row will be successfully inserted before truncation during prelocking reap; SELECT * FROM parent; SELECT * FROM child; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 766c5ec0d04..64434acd9e7 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -4625,6 +4625,41 @@ add_internal_tables(THD *thd, Query_tables_list *prelocking_ctx, DBUG_RETURN(FALSE); } +static +void prelock_fk_tables(THD *thd, TABLE_LIST *table_list, + Query_tables_list *prelocking_ctx, + List<FOREIGN_KEY_INFO> &fk_list, + bool is_foreign) +{ + for (auto fk: fk_list) + { + uint8 op= table_list->trg_event_map; + bool del_modifies_child= fk_modifies_child(fk.delete_method); + bool upd_modifiels_child= fk_modifies_child(fk.update_method); + + thr_lock_type lock_type= TL_READ; + if (!is_foreign + && ((op & (1u << TRG_EVENT_DELETE) && del_modifies_child) + || (op & (1u << TRG_EVENT_UPDATE) && upd_modifiels_child))) + lock_type= TL_WRITE_ALLOW_WRITE; + + auto *db= is_foreign ? fk.referenced_db : fk.foreign_db; + auto *table_name= is_foreign ? fk.referenced_table : fk.foreign_table; + + if (table_already_fk_prelocked(prelocking_ctx->query_tables, + db, table_name, lock_type)) + continue; + + TABLE_LIST *tl= (TABLE_LIST *) thd->alloc(sizeof(TABLE_LIST)); + tl->init_one_table_for_prelocking(db, table_name, + NULL, lock_type, + TABLE_LIST::PRELOCK_FK, + table_list->belong_to_view, + table_list->trg_event_map, + &prelocking_ctx->query_tables_last, + table_list->for_insert_data); + } +} /** @@ -4670,43 +4705,14 @@ handle_table(THD *thd, Query_tables_list *prelocking_ctx, return TRUE; } - if (table->s->referenced_by_foreign_key()) + if (table->s->referenced_by_foreign_key() || table->s->has_foreign_keys()) { - List_iterator<FOREIGN_KEY_INFO> fk_list_it(table->s->referenced_keys); - FOREIGN_KEY_INFO *fk; - Query_arena *arena, backup; - - arena= thd->activate_stmt_arena_if_needed(&backup); *need_prelocking= TRUE; - - while ((fk= fk_list_it++)) - { - // FK_OPTION_RESTRICT and FK_OPTION_NO_ACTION only need read access - uint8 op= table_list->trg_event_map; - thr_lock_type lock_type; - - if ((op & (1 << TRG_EVENT_DELETE) && fk_modifies_child(fk->delete_method)) - || (op & (1 << TRG_EVENT_UPDATE) && fk_modifies_child(fk->update_method))) - lock_type= TL_WRITE_ALLOW_WRITE; - else - lock_type= TL_READ; - - if (table_already_fk_prelocked(prelocking_ctx->query_tables, - fk->foreign_db, fk->foreign_table, - lock_type)) - continue; - - TABLE_LIST *tl= (TABLE_LIST *) thd->alloc(sizeof(TABLE_LIST)); - tl->init_one_table_for_prelocking(fk->foreign_db, - fk->foreign_table, - NULL, lock_type, - TABLE_LIST::PRELOCK_FK, - table_list->belong_to_view, op, - &prelocking_ctx->query_tables_last, - table_list->for_insert_data); - } - if (arena) - thd->restore_active_arena(arena, &backup); + Query_arena_stmt stmt(thd); + prelock_fk_tables(thd, table_list, prelocking_ctx, + table->s->foreign_keys, true); + prelock_fk_tables(thd, table_list, prelocking_ctx, + table->s->referenced_keys, false); } } diff --git a/sql/table.h b/sql/table.h index c674209c4d2..01c06e10405 100644 --- a/sql/table.h +++ b/sql/table.h @@ -653,6 +653,10 @@ struct TABLE_SHARE { return !referenced_keys.is_empty(); } + bool has_foreign_keys() const + { + return !foreign_keys.is_empty(); + } Virtual_column_info **check_constraints; uint *blob_field; /* Index to blobs in Field arrray*/ |