From 98a45e1a0ffd1379910d36738e462960e478d9b8 Mon Sep 17 00:00:00 2001 From: "kostja@bodhi.(none)" <> Date: Wed, 18 Jul 2007 14:42:06 +0400 Subject: [PATCH 1/6] Add a test case for Bug#27248 Triggers: error if insert affects temporary table. The bug itself is yet another manifestation of Bug 26141. --- mysql-test/r/trigger.result | 28 ++++++++++++++++++++++++++++ mysql-test/t/trigger.test | 23 +++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result index 4b18e525e62..a07318435f6 100644 --- a/mysql-test/r/trigger.result +++ b/mysql-test/r/trigger.result @@ -1933,4 +1933,32 @@ Before UPDATE, new=multi-UPDATE, SET for t2, but the trigger is fired, old=multi After UPDATE, new=multi-UPDATE, SET for t2, but the trigger is fired, old=multi-UPDATE drop view v1; drop table t1, t2, t1_op_log; + +Bug#27248 Triggers: error if insert affects temporary table + +The bug was fixed by the fix for Bug#26141 + +drop table if exists t1; +drop temporary table if exists t2; +create table t1 (s1 int); +create temporary table t2 (s1 int); +create trigger t1_bi before insert on t1 for each row insert into t2 values (0); +create trigger t1_bd before delete on t1 for each row delete from t2; +insert into t1 values (0); +insert into t1 values (0); +select * from t1; +s1 +0 +0 +select * from t2; +s1 +0 +0 +delete from t1; +select * from t1; +s1 +select * from t2; +s1 +drop table t1; +drop temporary table t2; End of 5.0 tests diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test index a6390036322..2f62ad38621 100644 --- a/mysql-test/t/trigger.test +++ b/mysql-test/t/trigger.test @@ -2194,4 +2194,27 @@ drop table t1, t2, t1_op_log; # # TODO: test LOAD DATA INFILE +# +--echo +--echo Bug#27248 Triggers: error if insert affects temporary table +--echo +--echo The bug was fixed by the fix for Bug#26141 +--echo +--disable_warnings +drop table if exists t1; +drop temporary table if exists t2; +--enable_warnings +create table t1 (s1 int); +create temporary table t2 (s1 int); +create trigger t1_bi before insert on t1 for each row insert into t2 values (0); +create trigger t1_bd before delete on t1 for each row delete from t2; +insert into t1 values (0); +insert into t1 values (0); +select * from t1; +select * from t2; +delete from t1; +select * from t1; +select * from t2; +drop table t1; +drop temporary table t2; --echo End of 5.0 tests From 9bac763cc153cd4e5221c7baf3e5b0cf8e829b50 Mon Sep 17 00:00:00 2001 From: "kostja@bodhi.(none)" <> Date: Wed, 18 Jul 2007 16:22:05 +0400 Subject: [PATCH 2/6] A fix and a test case for Bug#26104 Bug on foreign key class constructor. Fix the typo in the constructor. Cover a semantic check that previously never worked with a test. --- mysql-test/r/create.result | 16 ++++++++++++++++ mysql-test/r/innodb.result | 2 +- mysql-test/t/create.test | 19 +++++++++++++++++++ mysql-test/t/innodb.test | 2 +- sql/sql_class.h | 2 +- 5 files changed, 38 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/create.result b/mysql-test/r/create.result index e692dbf3938..a1843573794 100644 --- a/mysql-test/r/create.result +++ b/mysql-test/r/create.result @@ -1503,4 +1503,20 @@ t1 CREATE TABLE `t1` ( `c17` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; + +Bug #26104 Bug on foreign key class constructor + +Check that ref_columns is initalized correctly in the constructor +and semantic checks in mysql_prepare_table work. + +We do not need a storage engine that supports foreign keys +for this test, as the checks are purely syntax-based, and the +syntax is supported for all engines. + +drop table if exists t1,t2; +create table t1(a int not null, b int not null, primary key (a, b)); +create table t2(a int not null, b int not null, c int not null, primary key (a), +foreign key fk_bug26104 (b,c) references t1(a)); +ERROR 42000: Incorrect foreign key definition for 'fk_bug26104': Key reference and table reference don't match +drop table t1; End of 5.0 tests diff --git a/mysql-test/r/innodb.result b/mysql-test/r/innodb.result index 80b46e5098a..6082a30bce3 100644 --- a/mysql-test/r/innodb.result +++ b/mysql-test/r/innodb.result @@ -1648,7 +1648,7 @@ t2 CREATE TABLE `t2` ( ) ENGINE=InnoDB DEFAULT CHARSET=latin1 drop table t2; create table t2 (id int(11) not null, id2 int(11) not null, constraint t1_id_fk foreign key (id2,id) references t1 (id)) engine = innodb; -ERROR HY000: Can't create table './test/t2' (errno: 150) +ERROR 42000: Incorrect foreign key definition for 't1_id_fk': Key reference and table reference don't match create table t2 (a int auto_increment primary key, b int, index(b), foreign key (b) references t1(id), unique(b)) engine=innodb; show create table t2; Table Create Table diff --git a/mysql-test/t/create.test b/mysql-test/t/create.test index 99f3fea416a..a6679cd674a 100644 --- a/mysql-test/t/create.test +++ b/mysql-test/t/create.test @@ -1118,5 +1118,24 @@ show create table t1; drop table t1; +--echo +--echo Bug #26104 Bug on foreign key class constructor +--echo +--echo Check that ref_columns is initalized correctly in the constructor +--echo and semantic checks in mysql_prepare_table work. +--echo +--echo We do not need a storage engine that supports foreign keys +--echo for this test, as the checks are purely syntax-based, and the +--echo syntax is supported for all engines. +--echo +--disable_warnings +drop table if exists t1,t2; +--enable_warnings + +create table t1(a int not null, b int not null, primary key (a, b)); +--error ER_WRONG_FK_DEF +create table t2(a int not null, b int not null, c int not null, primary key (a), +foreign key fk_bug26104 (b,c) references t1(a)); +drop table t1; --echo End of 5.0 tests diff --git a/mysql-test/t/innodb.test b/mysql-test/t/innodb.test index a9679c01071..04dfa1d0836 100644 --- a/mysql-test/t/innodb.test +++ b/mysql-test/t/innodb.test @@ -1180,7 +1180,7 @@ drop table t2; # Clean up filename -- embedded server reports whole path without .frm, # regular server reports relative path with .frm (argh!) --replace_result \\ / $MYSQL_TEST_DIR . /var/master-data/ / t2.frm t2 ---error 1005 +--error ER_WRONG_FK_DEF create table t2 (id int(11) not null, id2 int(11) not null, constraint t1_id_fk foreign key (id2,id) references t1 (id)) engine = innodb; # bug#3749 diff --git a/sql/sql_class.h b/sql/sql_class.h index a5cbc21684f..1c52afb5bc5 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -450,7 +450,7 @@ public: Table_ident *table, List &ref_cols, uint delete_opt_arg, uint update_opt_arg, uint match_opt_arg) :Key(FOREIGN_KEY, name_arg, HA_KEY_ALG_UNDEF, 0, cols), - ref_table(table), ref_columns(cols), + ref_table(table), ref_columns(ref_cols), delete_opt(delete_opt_arg), update_opt(update_opt_arg), match_opt(match_opt_arg) {} From 198b97a62579c73bd455bc476822230324de8a34 Mon Sep 17 00:00:00 2001 From: "kostja@bodhi.(none)" <> Date: Wed, 18 Jul 2007 17:09:03 +0400 Subject: [PATCH 3/6] Add a test case for Bug#22427 create table if not exists + stored function results in inconsistent behavior. The bug itself was fixed by the patch for bug 20662. --- mysql-test/r/sp-prelocking.result | 22 ++++++++++++++++++++++ mysql-test/t/sp-prelocking.test | 23 +++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/mysql-test/r/sp-prelocking.result b/mysql-test/r/sp-prelocking.result index 5eac54803f0..c19bd1abd26 100644 --- a/mysql-test/r/sp-prelocking.result +++ b/mysql-test/r/sp-prelocking.result @@ -267,4 +267,26 @@ drop table bug_27907_logs; insert into bug_27907_t1(a) values (1); ERROR 42S02: Table 'test.bug_27907_logs' doesn't exist drop table bug_27907_t1; + +Bug#22427 create table if not exists + stored function results in +inconsistent behavior + +Add a test case, the bug itself was fixed by the patch for +Bug#20662 + +drop table if exists t1; +drop function if exists f_bug22427; +create table t1 (i int); +insert into t1 values (1); +create function f_bug22427() returns int return (select max(i) from t1); +select f_bug22427(); +f_bug22427() +1 +create table if not exists t1 select f_bug22427() as i; +Warnings: +Note 1050 Table 't1' already exists +create table t1 select f_bug22427() as i; +ERROR 42S01: Table 't1' already exists +drop table t1; +drop function f_bug22427; End of 5.0 tests diff --git a/mysql-test/t/sp-prelocking.test b/mysql-test/t/sp-prelocking.test index ec5b7fbad7c..60e97260839 100644 --- a/mysql-test/t/sp-prelocking.test +++ b/mysql-test/t/sp-prelocking.test @@ -333,4 +333,27 @@ insert into bug_27907_t1(a) values (1); drop table bug_27907_t1; +--echo +--echo Bug#22427 create table if not exists + stored function results in +--echo inconsistent behavior +--echo +--echo Add a test case, the bug itself was fixed by the patch for +--echo Bug#20662 +--echo +--disable_warnings +drop table if exists t1; +drop function if exists f_bug22427; +--enable_warnings +create table t1 (i int); +insert into t1 values (1); +create function f_bug22427() returns int return (select max(i) from t1); +select f_bug22427(); +# Until this bug was fixed, the following emitted error +# ERROR 1213: Deadlock found when trying to get lock +create table if not exists t1 select f_bug22427() as i; +--error ER_TABLE_EXISTS_ERROR +create table t1 select f_bug22427() as i; +drop table t1; +drop function f_bug22427; + --echo End of 5.0 tests From 1aae30060e22a5cfe5749ffb57bc42633e7dc7e8 Mon Sep 17 00:00:00 2001 From: "kostja@bodhi.(none)" <> Date: Thu, 19 Jul 2007 19:28:00 +0400 Subject: [PATCH 4/6] A fix for Bug#29431 killing an insert delayed thread causes crash No test case, since the bug requires a stress case with 30 INSERT DELAYED threads and 1 killer thread to repeat. The patch is verified manually. Review fixes. The server that is running DELAYED inserts would deadlock itself or crash under high load if some of the delayed threads were KILLed in the meanwhile. The fix is to change internal lock acquisition order of delayed inserts subsystem and to ensure that Delayed_insert::table_list::db does not point to volatile memory in some cases. For details, please see a comment for sql_insert.cc. --- sql/sql_insert.cc | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 73f8c5e4418..1a8cacd4f8e 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1705,8 +1705,8 @@ Delayed_insert *find_handler(THD *thd, TABLE_LIST *table_list) Delayed_insert *tmp; while ((tmp=it++)) { - if (!strcmp(tmp->thd.db,table_list->db) && - !strcmp(table_list->table_name,tmp->table->s->table_name)) + if (!strcmp(table_list->db, tmp->table_list.db) && + !strcmp(table_list->table_name, tmp->table_list.table_name)) { tmp->lock(); break; @@ -1739,7 +1739,27 @@ Delayed_insert *find_handler(THD *thd, TABLE_LIST *table_list) Two latter cases indicate a request for lock upgrade. XXX: why do we regard INSERT DELAYED into a view as an error and - do not simply a lock upgrade? + do not simply perform a lock upgrade? + + TODO: The approach with using two mutexes to work with the + delayed thread list -- LOCK_delayed_insert and + LOCK_delayed_create -- is redundant, and we only need one of + them to protect the list. The reason we have two locks is that + we do not want to block look-ups in the list while we're waiting + for the newly created thread to open the delayed table. However, + this wait itself is redundant -- we always call get_local_table + later on, and there wait again until the created thread acquires + a table lock. + + As is redundant the concept of locks_in_memory, since we already + have another counter with similar semantics - tables_in_use, + both of them are devoted to counting the number of producers for + a given consumer (delayed insert thread), only at different + stages of producer-consumer relationship. + + 'dead' and 'status' variables in Delayed_insert are redundant + too, since there is already 'di->thd.killed' and + di->stacked_inserts. */ static @@ -1788,7 +1808,9 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list) goto end_create; } tmp->table_list= *table_list; // Needed to open table + /* Replace volatile strings with local copies */ tmp->table_list.alias= tmp->table_list.table_name= tmp->thd.query; + tmp->table_list.db= tmp->thd.db; tmp->lock(); pthread_mutex_lock(&tmp->mutex); if ((error=pthread_create(&tmp->thd.real_id,&connection_attrib, @@ -1834,6 +1856,9 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list) tmp->unlock(); goto end_create; } + pthread_mutex_lock(&LOCK_delayed_insert); + delayed_threads.append(tmp); + pthread_mutex_unlock(&LOCK_delayed_insert); } pthread_mutex_unlock(&LOCK_delayed_create); } @@ -2176,11 +2201,6 @@ pthread_handler_t handle_delayed_insert(void *arg) } di->table->copy_blobs=1; - /* One can now use this */ - pthread_mutex_lock(&LOCK_delayed_insert); - delayed_threads.append(di); - pthread_mutex_unlock(&LOCK_delayed_insert); - /* Tell client that the thread is initialized */ pthread_cond_signal(&di->cond_client); From c4bcce64a4267cc9302b55155078e12d506f24d2 Mon Sep 17 00:00:00 2001 From: "kostja@bodhi.(none)" <> Date: Thu, 19 Jul 2007 19:36:52 +0400 Subject: [PATCH 5/6] Rename all references to 'Delayed_insert' instances from 'tmp' to 'di' for consistency. --- sql/sql_insert.cc | 100 +++++++++++++++++++++++----------------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 1a8cacd4f8e..19c9360b0ed 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1702,18 +1702,18 @@ Delayed_insert *find_handler(THD *thd, TABLE_LIST *table_list) thd->proc_info="waiting for delay_list"; pthread_mutex_lock(&LOCK_delayed_insert); // Protect master list I_List_iterator it(delayed_threads); - Delayed_insert *tmp; - while ((tmp=it++)) + Delayed_insert *di; + while ((di= it++)) { - if (!strcmp(table_list->db, tmp->table_list.db) && - !strcmp(table_list->table_name, tmp->table_list.table_name)) + if (!strcmp(table_list->db, di->table_list.db) && + !strcmp(table_list->table_name, di->table_list.table_name)) { - tmp->lock(); + di->lock(); break; } } pthread_mutex_unlock(&LOCK_delayed_insert); // For unlink from list - return tmp; + return di; } @@ -1766,14 +1766,14 @@ static bool delayed_get_table(THD *thd, TABLE_LIST *table_list) { int error; - Delayed_insert *tmp; + Delayed_insert *di; DBUG_ENTER("delayed_get_table"); /* Must be set in the parser */ DBUG_ASSERT(table_list->db); /* Find the thread which handles this table. */ - if (!(tmp=find_handler(thd,table_list))) + if (!(di= find_handler(thd, table_list))) { /* No match. Create a new thread to handle the table, but @@ -1787,9 +1787,9 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list) The first search above was done without LOCK_delayed_create. Another thread might have created the handler in between. Search again. */ - if (! (tmp= find_handler(thd, table_list))) + if (! (di= find_handler(thd, table_list))) { - if (!(tmp=new Delayed_insert())) + if (!(di= new Delayed_insert())) { my_error(ER_OUTOFMEMORY,MYF(0),sizeof(Delayed_insert)); thd->fatal_error(); @@ -1798,30 +1798,30 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list) pthread_mutex_lock(&LOCK_thread_count); thread_count++; pthread_mutex_unlock(&LOCK_thread_count); - tmp->thd.set_db(table_list->db, strlen(table_list->db)); - tmp->thd.query= my_strdup(table_list->table_name,MYF(MY_WME)); - if (tmp->thd.db == NULL || tmp->thd.query == NULL) + di->thd.set_db(table_list->db, strlen(table_list->db)); + di->thd.query= my_strdup(table_list->table_name, MYF(MY_WME)); + if (di->thd.db == NULL || di->thd.query == NULL) { /* The error is reported */ - delete tmp; + delete di; thd->fatal_error(); goto end_create; } - tmp->table_list= *table_list; // Needed to open table + di->table_list= *table_list; // Needed to open table /* Replace volatile strings with local copies */ - tmp->table_list.alias= tmp->table_list.table_name= tmp->thd.query; - tmp->table_list.db= tmp->thd.db; - tmp->lock(); - pthread_mutex_lock(&tmp->mutex); - if ((error=pthread_create(&tmp->thd.real_id,&connection_attrib, - handle_delayed_insert,(void*) tmp))) + di->table_list.alias= di->table_list.table_name= di->thd.query; + di->table_list.db= di->thd.db; + di->lock(); + pthread_mutex_lock(&di->mutex); + if ((error= pthread_create(&di->thd.real_id, &connection_attrib, + handle_delayed_insert, (void*) di))) { DBUG_PRINT("error", ("Can't create thread to handle delayed insert (error %d)", error)); - pthread_mutex_unlock(&tmp->mutex); - tmp->unlock(); - delete tmp; + pthread_mutex_unlock(&di->mutex); + di->unlock(); + delete di; my_error(ER_CANT_CREATE_THREAD, MYF(0), error); thd->fatal_error(); goto end_create; @@ -1829,15 +1829,15 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list) /* Wait until table is open */ thd->proc_info="waiting for handler open"; - while (!tmp->thd.killed && !tmp->table && !thd->killed) + while (!di->thd.killed && !di->table && !thd->killed) { - pthread_cond_wait(&tmp->cond_client,&tmp->mutex); + pthread_cond_wait(&di->cond_client, &di->mutex); } - pthread_mutex_unlock(&tmp->mutex); + pthread_mutex_unlock(&di->mutex); thd->proc_info="got old table"; - if (tmp->thd.killed) + if (di->thd.killed) { - if (tmp->thd.net.report_error) + if (di->thd.net.report_error) { /* Copy the error message. Note that we don't treat fatal @@ -1845,34 +1845,34 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list) main thread. Use of my_message will enable stored procedures continue handlers. */ - my_message(tmp->thd.net.last_errno, tmp->thd.net.last_error, + my_message(di->thd.net.last_errno, di->thd.net.last_error, MYF(0)); } - tmp->unlock(); + di->unlock(); goto end_create; } if (thd->killed) { - tmp->unlock(); + di->unlock(); goto end_create; } pthread_mutex_lock(&LOCK_delayed_insert); - delayed_threads.append(tmp); + delayed_threads.append(di); pthread_mutex_unlock(&LOCK_delayed_insert); } pthread_mutex_unlock(&LOCK_delayed_create); } - pthread_mutex_lock(&tmp->mutex); - table_list->table= tmp->get_local_table(thd); - pthread_mutex_unlock(&tmp->mutex); + pthread_mutex_lock(&di->mutex); + table_list->table= di->get_local_table(thd); + pthread_mutex_unlock(&di->mutex); if (table_list->table) { DBUG_ASSERT(thd->net.report_error == 0); - thd->di=tmp; + thd->di= di; } /* Unlock the delayed insert object after its last access. */ - tmp->unlock(); + di->unlock(); DBUG_RETURN(table_list->table == NULL); end_create: @@ -2102,26 +2102,26 @@ void kill_delayed_threads(void) VOID(pthread_mutex_lock(&LOCK_delayed_insert)); // For unlink from list I_List_iterator it(delayed_threads); - Delayed_insert *tmp; - while ((tmp=it++)) + Delayed_insert *di; + while ((di= it++)) { - tmp->thd.killed= THD::KILL_CONNECTION; - if (tmp->thd.mysys_var) + di->thd.killed= THD::KILL_CONNECTION; + if (di->thd.mysys_var) { - pthread_mutex_lock(&tmp->thd.mysys_var->mutex); - if (tmp->thd.mysys_var->current_cond) + pthread_mutex_lock(&di->thd.mysys_var->mutex); + if (di->thd.mysys_var->current_cond) { /* We need the following test because the main mutex may be locked in handle_delayed_insert() */ - if (&tmp->mutex != tmp->thd.mysys_var->current_mutex) - pthread_mutex_lock(tmp->thd.mysys_var->current_mutex); - pthread_cond_broadcast(tmp->thd.mysys_var->current_cond); - if (&tmp->mutex != tmp->thd.mysys_var->current_mutex) - pthread_mutex_unlock(tmp->thd.mysys_var->current_mutex); + if (&di->mutex != di->thd.mysys_var->current_mutex) + pthread_mutex_lock(di->thd.mysys_var->current_mutex); + pthread_cond_broadcast(di->thd.mysys_var->current_cond); + if (&di->mutex != di->thd.mysys_var->current_mutex) + pthread_mutex_unlock(di->thd.mysys_var->current_mutex); } - pthread_mutex_unlock(&tmp->thd.mysys_var->mutex); + pthread_mutex_unlock(&di->thd.mysys_var->mutex); } } VOID(pthread_mutex_unlock(&LOCK_delayed_insert)); // For unlink from list From 19ae62f3fba4084a244518e6872fb87482ef0cc5 Mon Sep 17 00:00:00 2001 From: "kostja@bodhi.(none)" <> Date: Fri, 20 Jul 2007 19:46:13 +0400 Subject: [PATCH 6/6] Remove obvious comments. --- sql/sql_base.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sql/sql_base.cc b/sql/sql_base.cc index bf1c6d7cb0d..3860dfa1ded 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -62,11 +62,11 @@ Prelock_error_handler::handle_error(uint sql_errno, if (sql_errno == ER_NO_SUCH_TABLE) { m_handled_errors++; - return TRUE; // 'TRUE', as per coding style + return TRUE; } m_unhandled_errors++; - return FALSE; // 'FALSE', as per coding style + return FALSE; } @@ -2674,7 +2674,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) */ for (tables= *start; tables ;tables= tables->next_global) { - safe_to_ignore_table= FALSE; // 'FALSE', as per coding style + safe_to_ignore_table= FALSE; if (tables->lock_type == TL_WRITE_DEFAULT) {