From e44024d47383a7e94b9ad54f0f058a16040837e7 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 4 Sep 2006 19:47:24 +0500 Subject: [PATCH 01/26] bug #12620 (UTF-8 indexing causes ER_NO_KEYFILE error) The problem is that on some Mac OS X-es the file writing/reading call with zero bytes to read/write returns error. So here i try to eliminate that kinds of calls. myisam/mi_check.c: zero length copying avoided mysys/my_chsize.c: no file operations if it's not necessary --- myisam/mi_check.c | 9 ++++++--- mysys/my_chsize.c | 4 +++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/myisam/mi_check.c b/myisam/mi_check.c index 1e62e5e641d..65ce70a97df 100644 --- a/myisam/mi_check.c +++ b/myisam/mi_check.c @@ -1366,7 +1366,8 @@ int mi_repair(MI_CHECK *param, register MI_INFO *info, param->temp_filename); goto err; } - if (filecopy(param,new_file,info->dfile,0L,new_header_length, + if (new_header_length && + filecopy(param,new_file,info->dfile,0L,new_header_length, "datafile-header")) goto err; info->s->state.dellink= HA_OFFSET_ERROR; @@ -2063,7 +2064,8 @@ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info, param->temp_filename); goto err; } - if (filecopy(param, new_file,info->dfile,0L,new_header_length, + if (new_header_length && + filecopy(param, new_file,info->dfile,0L,new_header_length, "datafile-header")) goto err; if (param->testflag & T_UNPACK) @@ -2431,7 +2433,8 @@ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info, param->temp_filename); goto err; } - if (filecopy(param, new_file,info->dfile,0L,new_header_length, + if (new_header_length && + filecopy(param, new_file,info->dfile,0L,new_header_length, "datafile-header")) goto err; if (param->testflag & T_UNPACK) diff --git a/mysys/my_chsize.c b/mysys/my_chsize.c index cf26428d65f..efc19927183 100644 --- a/mysys/my_chsize.c +++ b/mysys/my_chsize.c @@ -44,7 +44,9 @@ int my_chsize(File fd, my_off_t newlength, int filler, myf MyFlags) DBUG_PRINT("my",("fd: %d length: %lu MyFlags: %d",fd,(ulong) newlength, MyFlags)); - oldsize = my_seek(fd, 0L, MY_SEEK_END, MYF(MY_WME+MY_FAE)); + if ((oldsize = my_seek(fd, 0L, MY_SEEK_END, MYF(MY_WME+MY_FAE))) == newlength) + DBUG_RETURN(0); + DBUG_PRINT("info",("old_size: %ld", (ulong) oldsize)); if (oldsize > newlength) From 032de81438179ebfc047e6eb6e3c505400f3bb18 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 6 Sep 2006 19:56:19 +0500 Subject: [PATCH 02/26] bug #12991 (compile error --without-geometry) myisam/mi_check.c: proper #ifdef-s added --- myisam/mi_check.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/myisam/mi_check.c b/myisam/mi_check.c index 1e62e5e641d..1374cb05094 100644 --- a/myisam/mi_check.c +++ b/myisam/mi_check.c @@ -1153,9 +1153,12 @@ int chk_data_link(MI_CHECK *param, MI_INFO *info,int extend) /* We don't need to lock the key tree here as we don't allow concurrent threads when running myisamchk */ - int search_result= (keyinfo->flag & HA_SPATIAL) ? + int search_result= +#ifdef HAVE_RTREE_KEYS + (keyinfo->flag & HA_SPATIAL) ? rtree_find_first(info, key, info->lastkey, key_length, MBR_EQUAL | MBR_DATA) : +#endif _mi_search(info,keyinfo,info->lastkey,key_length, SEARCH_SAME, info->s->state.key_root[key]); if (search_result) From 2a00a073c2c07f2d73d60a6c74c538874f05158b Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 8 Sep 2006 19:04:46 +0300 Subject: [PATCH 03/26] Bug #21540: Subqueries with no from and aggregate functions return wrong results Mark the containing Item(s) (Item_subselect descendant usually) of a subselect as containing aggregate functions if it has references to aggregates functions that are calculated outside its context. This tels end_send_group() not to make an Item_subselect descendant in select list a copy and causes the correct value being returned. mysql-test/r/func_group.result: Bug #21540: Subqueries with no from and aggregate functions return wrong results - test cases mysql-test/r/subselect.result: Bug #21540: Subqueries with no from and aggregate functions return wrong results - fixed the result of an existing testcase. mysql-test/t/subselect.test: Bug #21540: Subqueries with no from and aggregate functions return wrong results - test cases sql/item_sum.cc: Bug #21540: Subqueries with no from and aggregate functions return wrong results Mark the containing Item (Item_subselect descendant usually) of a subselect as containing aggregate functions if it has references to aggregates functions that are calculated outside its context. This tels end_send_group() not to make an Item_subselect descendant in select list a copy and causes the correct value being returned. --- mysql-test/r/func_group.result | 2 +- mysql-test/r/subselect.result | 61 ++++++++++++++++++++++++++++++++++ mysql-test/t/subselect.test | 31 +++++++++++++++++ sql/item_sum.cc | 22 +++++++++++- 4 files changed, 114 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/func_group.result b/mysql-test/r/func_group.result index f57b4ad6ce9..da2a7518c86 100644 --- a/mysql-test/r/func_group.result +++ b/mysql-test/r/func_group.result @@ -997,7 +997,7 @@ SELECT SQL_NO_CACHE WHERE ttt.a = ccc.b AND ttt.a = t.a GROUP BY ttt.a) AS minid FROM t1 t, t2 c WHERE t.a = c.b; minid -NULL +1 DROP TABLE t1,t2; create table t1 select variance(0); show create table t1; diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 4ee6d3088a5..a6fb9a3eb61 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -3368,3 +3368,64 @@ ORDER BY t1.t DESC LIMIT 1); i1 i2 t i1 i2 t 24 1 2005-05-27 12:40:30 24 1 2006-06-20 12:29:40 DROP TABLE t1, t2; +CREATE TABLE t1 (a INT, b INT); +CREATE TABLE t2 (a INT); +INSERT INTO t2 values (1); +INSERT INTO t1 VALUES (1,1),(1,2),(2,3),(3,4); +SELECT (SELECT COUNT(DISTINCT t1.b) from t2) FROM t1 GROUP BY t1.a; +(SELECT COUNT(DISTINCT t1.b) from t2) +2 +1 +1 +SELECT (SELECT COUNT(DISTINCT t1.b) from t2 union select 1 from t2 where 12 < 3) +FROM t1 GROUP BY t1.a; +(SELECT COUNT(DISTINCT t1.b) from t2 union select 1 from t2 where 12 < 3) +2 +1 +1 +SELECT COUNT(DISTINCT t1.b), (SELECT COUNT(DISTINCT t1.b)) FROM t1 GROUP BY t1.a; +COUNT(DISTINCT t1.b) (SELECT COUNT(DISTINCT t1.b)) +2 2 +1 1 +1 1 +SELECT COUNT(DISTINCT t1.b), +(SELECT COUNT(DISTINCT t1.b) union select 1 from DUAL where 12 < 3) +FROM t1 GROUP BY t1.a; +COUNT(DISTINCT t1.b) (SELECT COUNT(DISTINCT t1.b) union select 1 from DUAL where 12 < 3) +2 2 +1 1 +1 1 +SELECT ( +SELECT ( +SELECT COUNT(DISTINCT t1.b) +) +) +FROM t1 GROUP BY t1.a; +( +SELECT ( +SELECT COUNT(DISTINCT t1.b) +) +) +2 +1 +1 +SELECT ( +SELECT ( +SELECT ( +SELECT COUNT(DISTINCT t1.b) +) +) +FROM t1 GROUP BY t1.a LIMIT 1) +FROM t1 t2 +GROUP BY t2.a; +( +SELECT ( +SELECT ( +SELECT COUNT(DISTINCT t1.b) +) +) +FROM t1 GROUP BY t1.a LIMIT 1) +2 +2 +2 +DROP TABLE t1,t2; diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index ed122e9ff5a..4d0bb9fd6c6 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -2280,3 +2280,34 @@ SELECT * FROM t1,t2 ORDER BY t1.t DESC LIMIT 1); DROP TABLE t1, t2; + +# +# Bug #21540: Subqueries with no from and aggregate functions return +# wrong results +CREATE TABLE t1 (a INT, b INT); +CREATE TABLE t2 (a INT); +INSERT INTO t2 values (1); +INSERT INTO t1 VALUES (1,1),(1,2),(2,3),(3,4); +SELECT (SELECT COUNT(DISTINCT t1.b) from t2) FROM t1 GROUP BY t1.a; +SELECT (SELECT COUNT(DISTINCT t1.b) from t2 union select 1 from t2 where 12 < 3) + FROM t1 GROUP BY t1.a; +SELECT COUNT(DISTINCT t1.b), (SELECT COUNT(DISTINCT t1.b)) FROM t1 GROUP BY t1.a; +SELECT COUNT(DISTINCT t1.b), + (SELECT COUNT(DISTINCT t1.b) union select 1 from DUAL where 12 < 3) + FROM t1 GROUP BY t1.a; +SELECT ( + SELECT ( + SELECT COUNT(DISTINCT t1.b) + ) +) +FROM t1 GROUP BY t1.a; +SELECT ( + SELECT ( + SELECT ( + SELECT COUNT(DISTINCT t1.b) + ) + ) + FROM t1 GROUP BY t1.a LIMIT 1) +FROM t1 t2 +GROUP BY t2.a; +DROP TABLE t1,t2; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 0d2a5b3b080..7dc90b11771 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -246,7 +246,27 @@ bool Item_sum::register_sum_func(THD *thd, Item **ref) aggr_sl->inner_sum_func_list->next= this; } aggr_sl->inner_sum_func_list= this; - + aggr_sl->with_sum_func= 1; + + /* + Mark Item_subselect(s) as containing aggregate function all the way up + to aggregate function's calculation context. + Note that we must not mark the Item of calculation context itself + because with_sum_func on the calculation context st_select_lex is + already set above. + + with_sum_func being set for an Item means that this Item refers + (somewhere in it, e.g. one of its arguments if it's a function) directly + or through intermediate items to an aggregate function that is calculated + in a context "outside" of the Item (e.g. in the current or outer select). + + with_sum_func being set for an st_select_lex means that this st_select_lex + has aggregate functions directly referenced (i.e. not through a sub-select). + */ + for (sl= thd->lex->current_select; + sl && sl != aggr_sl && sl->master_unit()->item; + sl= sl->master_unit()->outer_select() ) + sl->master_unit()->item->with_sum_func= 1; } return FALSE; } From c46151d96128a374ef286d6ff83d57b1f53bb2dc Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 16 Sep 2006 11:50:00 -0700 Subject: [PATCH 04/26] Fixed bug #22085: Crash on the execution of a prepared statement that uses an aggregating IN subquery with HAVING clause. A wrong order of the call of split_sum_func2 for the HAVING clause of the subquery and the transformation for the subquery resulted in the creation of a andor structure that could not be restored at an execution of the prepared statement. mysql-test/r/ps.result: Added a test cases for bug #22085. mysql-test/t/ps.test: Added a test cases for bug #22085. --- mysql-test/r/ps.result | 47 ++++++++++++++++++++++++++++++++++++++ mysql-test/t/ps.test | 52 ++++++++++++++++++++++++++++++++++++++++++ sql/sql_select.cc | 5 ++-- 3 files changed, 102 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 01aa4ddf859..78752622db7 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -889,3 +889,50 @@ create temporary table if not exists t1 (a1 int); execute stmt; drop temporary table t1; deallocate prepare stmt; +CREATE TABLE t1( +ID int(10) unsigned NOT NULL auto_increment, +Member_ID varchar(15) NOT NULL default '', +Action varchar(12) NOT NULL, +Action_Date datetime NOT NULL, +Track varchar(15) default NULL, +User varchar(12) default NULL, +Date_Updated timestamp NOT NULL default CURRENT_TIMESTAMP on update +CURRENT_TIMESTAMP, +PRIMARY KEY (ID), +KEY Action (Action), +KEY Action_Date (Action_Date) +); +INSERT INTO t1(Member_ID, Action, Action_Date, Track) VALUES +('111111', 'Disenrolled', '2006-03-01', 'CAD' ), +('111111', 'Enrolled', '2006-03-01', 'CAD' ), +('111111', 'Disenrolled', '2006-07-03', 'CAD' ), +('222222', 'Enrolled', '2006-03-07', 'CAD' ), +('222222', 'Enrolled', '2006-03-07', 'CHF' ), +('222222', 'Disenrolled', '2006-08-02', 'CHF' ), +('333333', 'Enrolled', '2006-03-01', 'CAD' ), +('333333', 'Disenrolled', '2006-03-01', 'CAD' ), +('444444', 'Enrolled', '2006-03-01', 'CAD' ), +('555555', 'Disenrolled', '2006-03-01', 'CAD' ), +('555555', 'Enrolled', '2006-07-21', 'CAD' ), +('555555', 'Disenrolled', '2006-03-01', 'CHF' ), +('666666', 'Enrolled', '2006-02-09', 'CAD' ), +('666666', 'Enrolled', '2006-05-12', 'CHF' ), +('666666', 'Disenrolled', '2006-06-01', 'CAD' ); +PREPARE STMT FROM +"SELECT GROUP_CONCAT(Track SEPARATOR ', ') FROM t1 + WHERE Member_ID=? AND Action='Enrolled' AND + (Track,Action_Date) IN (SELECT Track, MAX(Action_Date) FROM t1 + WHERE Member_ID=? + GROUP BY Track + HAVING Track>='CAD' AND + MAX(Action_Date)>'2006-03-01')"; +SET @id='111111'; +EXECUTE STMT USING @id,@id; +GROUP_CONCAT(Track SEPARATOR ', ') +NULL +SET @id='222222'; +EXECUTE STMT USING @id,@id; +GROUP_CONCAT(Track SEPARATOR ', ') +CAD +DEALLOCATE PREPARE STMT; +DROP TABLE t1; diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index 0ca293eb1ba..7c2a42404ad 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -951,4 +951,56 @@ execute stmt; drop temporary table t1; deallocate prepare stmt; +# +# BUG#22085: Crash on the execution of a prepared statement that +# uses an IN subquery with aggregate functions in HAVING +# + +CREATE TABLE t1( + ID int(10) unsigned NOT NULL auto_increment, + Member_ID varchar(15) NOT NULL default '', + Action varchar(12) NOT NULL, + Action_Date datetime NOT NULL, + Track varchar(15) default NULL, + User varchar(12) default NULL, + Date_Updated timestamp NOT NULL default CURRENT_TIMESTAMP on update + CURRENT_TIMESTAMP, + PRIMARY KEY (ID), + KEY Action (Action), + KEY Action_Date (Action_Date) +); + +INSERT INTO t1(Member_ID, Action, Action_Date, Track) VALUES + ('111111', 'Disenrolled', '2006-03-01', 'CAD' ), + ('111111', 'Enrolled', '2006-03-01', 'CAD' ), + ('111111', 'Disenrolled', '2006-07-03', 'CAD' ), + ('222222', 'Enrolled', '2006-03-07', 'CAD' ), + ('222222', 'Enrolled', '2006-03-07', 'CHF' ), + ('222222', 'Disenrolled', '2006-08-02', 'CHF' ), + ('333333', 'Enrolled', '2006-03-01', 'CAD' ), + ('333333', 'Disenrolled', '2006-03-01', 'CAD' ), + ('444444', 'Enrolled', '2006-03-01', 'CAD' ), + ('555555', 'Disenrolled', '2006-03-01', 'CAD' ), + ('555555', 'Enrolled', '2006-07-21', 'CAD' ), + ('555555', 'Disenrolled', '2006-03-01', 'CHF' ), + ('666666', 'Enrolled', '2006-02-09', 'CAD' ), + ('666666', 'Enrolled', '2006-05-12', 'CHF' ), + ('666666', 'Disenrolled', '2006-06-01', 'CAD' ); + +PREPARE STMT FROM +"SELECT GROUP_CONCAT(Track SEPARATOR ', ') FROM t1 + WHERE Member_ID=? AND Action='Enrolled' AND + (Track,Action_Date) IN (SELECT Track, MAX(Action_Date) FROM t1 + WHERE Member_ID=? + GROUP BY Track + HAVING Track>='CAD' AND + MAX(Action_Date)>'2006-03-01')"; +SET @id='111111'; +EXECUTE STMT USING @id,@id; +SET @id='222222'; +EXECUTE STMT USING @id,@id; + +DEALLOCATE PREPARE STMT; +DROP TABLE t1; + # End of 4.1 tests diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 5154bff78dd..62eb5235d70 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -290,8 +290,6 @@ JOIN::prepare(Item ***rref_pointer_array, select_lex->having_fix_field= 0; if (having_fix_rc || thd->net.report_error) DBUG_RETURN(-1); /* purecov: inspected */ - if (having->with_sum_func) - having->split_sum_func2(thd, ref_pointer_array, all_fields, &having); } // Is it subselect @@ -306,6 +304,9 @@ JOIN::prepare(Item ***rref_pointer_array, } } + if (having && having->with_sum_func) + having->split_sum_func2(thd, ref_pointer_array, all_fields, &having); + if (setup_ftfuncs(select_lex)) /* should be after having->fix_fields */ DBUG_RETURN(-1); From 76b354dcf73d87de3abb2bf482954c261e4d995b Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 19 Sep 2006 12:18:24 -0700 Subject: [PATCH 05/26] Post-merge corrections. --- mysql-test/r/ps.result | 98 ++++++++++++++++++------------------- mysql-test/t/ps.test | 108 ++++++++++++++++++++--------------------- 2 files changed, 103 insertions(+), 103 deletions(-) diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 888ba5bdb5b..1727c4ddfbe 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -253,10 +253,10 @@ execute `ü` ; 1234 1234 set names latin1; -execute `ü`; +execute `ü`; 1234 1234 -deallocate prepare `ü`; +deallocate prepare `ü`; set names default; create table t1 (a varchar(10)) charset=utf8; insert into t1 (a) values ('yahoo'); @@ -820,53 +820,6 @@ ERROR HY000: Variable 'max_prepared_stmt_count' is a GLOBAL variable and should set local max_prepared_stmt_count=1; ERROR HY000: Variable 'max_prepared_stmt_count' is a GLOBAL variable and should be set with SET GLOBAL set local prepared_stmt_count=0; -CREATE TABLE t1( -ID int(10) unsigned NOT NULL auto_increment, -Member_ID varchar(15) NOT NULL default '', -Action varchar(12) NOT NULL, -Action_Date datetime NOT NULL, -Track varchar(15) default NULL, -User varchar(12) default NULL, -Date_Updated timestamp NOT NULL default CURRENT_TIMESTAMP on update -CURRENT_TIMESTAMP, -PRIMARY KEY (ID), -KEY Action (Action), -KEY Action_Date (Action_Date) -); -INSERT INTO t1(Member_ID, Action, Action_Date, Track) VALUES -('111111', 'Disenrolled', '2006-03-01', 'CAD' ), -('111111', 'Enrolled', '2006-03-01', 'CAD' ), -('111111', 'Disenrolled', '2006-07-03', 'CAD' ), -('222222', 'Enrolled', '2006-03-07', 'CAD' ), -('222222', 'Enrolled', '2006-03-07', 'CHF' ), -('222222', 'Disenrolled', '2006-08-02', 'CHF' ), -('333333', 'Enrolled', '2006-03-01', 'CAD' ), -('333333', 'Disenrolled', '2006-03-01', 'CAD' ), -('444444', 'Enrolled', '2006-03-01', 'CAD' ), -('555555', 'Disenrolled', '2006-03-01', 'CAD' ), -('555555', 'Enrolled', '2006-07-21', 'CAD' ), -('555555', 'Disenrolled', '2006-03-01', 'CHF' ), -('666666', 'Enrolled', '2006-02-09', 'CAD' ), -('666666', 'Enrolled', '2006-05-12', 'CHF' ), -('666666', 'Disenrolled', '2006-06-01', 'CAD' ); -PREPARE STMT FROM -"SELECT GROUP_CONCAT(Track SEPARATOR ', ') FROM t1 - WHERE Member_ID=? AND Action='Enrolled' AND - (Track,Action_Date) IN (SELECT Track, MAX(Action_Date) FROM t1 - WHERE Member_ID=? - GROUP BY Track - HAVING Track>='CAD' AND - MAX(Action_Date)>'2006-03-01')"; -SET @id='111111'; -EXECUTE STMT USING @id,@id; -GROUP_CONCAT(Track SEPARATOR ', ') -NULL -SET @id='222222'; -EXECUTE STMT USING @id,@id; -GROUP_CONCAT(Track SEPARATOR ', ') -CAD -DEALLOCATE PREPARE STMT; -DROP TABLE t1; ERROR HY000: Variable 'prepared_stmt_count' is a read only variable set @@prepared_stmt_count=0; ERROR HY000: Variable 'prepared_stmt_count' is a read only variable @@ -956,6 +909,53 @@ create temporary table if not exists t1 (a1 int); execute stmt; drop temporary table t1; deallocate prepare stmt; +CREATE TABLE t1( +ID int(10) unsigned NOT NULL auto_increment, +Member_ID varchar(15) NOT NULL default '', +Action varchar(12) NOT NULL, +Action_Date datetime NOT NULL, +Track varchar(15) default NULL, +User varchar(12) default NULL, +Date_Updated timestamp NOT NULL default CURRENT_TIMESTAMP on update +CURRENT_TIMESTAMP, +PRIMARY KEY (ID), +KEY Action (Action), +KEY Action_Date (Action_Date) +); +INSERT INTO t1(Member_ID, Action, Action_Date, Track) VALUES +('111111', 'Disenrolled', '2006-03-01', 'CAD' ), +('111111', 'Enrolled', '2006-03-01', 'CAD' ), +('111111', 'Disenrolled', '2006-07-03', 'CAD' ), +('222222', 'Enrolled', '2006-03-07', 'CAD' ), +('222222', 'Enrolled', '2006-03-07', 'CHF' ), +('222222', 'Disenrolled', '2006-08-02', 'CHF' ), +('333333', 'Enrolled', '2006-03-01', 'CAD' ), +('333333', 'Disenrolled', '2006-03-01', 'CAD' ), +('444444', 'Enrolled', '2006-03-01', 'CAD' ), +('555555', 'Disenrolled', '2006-03-01', 'CAD' ), +('555555', 'Enrolled', '2006-07-21', 'CAD' ), +('555555', 'Disenrolled', '2006-03-01', 'CHF' ), +('666666', 'Enrolled', '2006-02-09', 'CAD' ), +('666666', 'Enrolled', '2006-05-12', 'CHF' ), +('666666', 'Disenrolled', '2006-06-01', 'CAD' ); +PREPARE STMT FROM +"SELECT GROUP_CONCAT(Track SEPARATOR ', ') FROM t1 + WHERE Member_ID=? AND Action='Enrolled' AND + (Track,Action_Date) IN (SELECT Track, MAX(Action_Date) FROM t1 + WHERE Member_ID=? + GROUP BY Track + HAVING Track>='CAD' AND + MAX(Action_Date)>'2006-03-01')"; +SET @id='111111'; +EXECUTE STMT USING @id,@id; +GROUP_CONCAT(Track SEPARATOR ', ') +NULL +SET @id='222222'; +EXECUTE STMT USING @id,@id; +GROUP_CONCAT(Track SEPARATOR ', ') +CAD +DEALLOCATE PREPARE STMT; +DROP TABLE t1; End of 4.1 tests create table t1 (a varchar(20)); insert into t1 values ('foo'); diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index ea9e98e1b1e..cc239306558 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -259,8 +259,8 @@ set names utf8; prepare `ü` from 'select 1234'; execute `ü` ; set names latin1; -execute `ü`; -deallocate prepare `ü`; +execute `ü`; +deallocate prepare `ü`; set names default; @@ -563,58 +563,6 @@ set @a=200887, @b=860; # this query did not return all matching rows execute stmt using @a, @b; deallocate prepare stmt; -# -# BUG#22085: Crash on the execution of a prepared statement that -# uses an IN subquery with aggregate functions in HAVING -# - -CREATE TABLE t1( - ID int(10) unsigned NOT NULL auto_increment, - Member_ID varchar(15) NOT NULL default '', - Action varchar(12) NOT NULL, - Action_Date datetime NOT NULL, - Track varchar(15) default NULL, - User varchar(12) default NULL, - Date_Updated timestamp NOT NULL default CURRENT_TIMESTAMP on update - CURRENT_TIMESTAMP, - PRIMARY KEY (ID), - KEY Action (Action), - KEY Action_Date (Action_Date) -); - -INSERT INTO t1(Member_ID, Action, Action_Date, Track) VALUES - ('111111', 'Disenrolled', '2006-03-01', 'CAD' ), - ('111111', 'Enrolled', '2006-03-01', 'CAD' ), - ('111111', 'Disenrolled', '2006-07-03', 'CAD' ), - ('222222', 'Enrolled', '2006-03-07', 'CAD' ), - ('222222', 'Enrolled', '2006-03-07', 'CHF' ), - ('222222', 'Disenrolled', '2006-08-02', 'CHF' ), - ('333333', 'Enrolled', '2006-03-01', 'CAD' ), - ('333333', 'Disenrolled', '2006-03-01', 'CAD' ), - ('444444', 'Enrolled', '2006-03-01', 'CAD' ), - ('555555', 'Disenrolled', '2006-03-01', 'CAD' ), - ('555555', 'Enrolled', '2006-07-21', 'CAD' ), - ('555555', 'Disenrolled', '2006-03-01', 'CHF' ), - ('666666', 'Enrolled', '2006-02-09', 'CAD' ), - ('666666', 'Enrolled', '2006-05-12', 'CHF' ), - ('666666', 'Disenrolled', '2006-06-01', 'CAD' ); - -PREPARE STMT FROM -"SELECT GROUP_CONCAT(Track SEPARATOR ', ') FROM t1 - WHERE Member_ID=? AND Action='Enrolled' AND - (Track,Action_Date) IN (SELECT Track, MAX(Action_Date) FROM t1 - WHERE Member_ID=? - GROUP BY Track - HAVING Track>='CAD' AND - MAX(Action_Date)>'2006-03-01')"; -SET @id='111111'; -EXECUTE STMT USING @id,@id; -SET @id='222222'; -EXECUTE STMT USING @id,@id; - -DEALLOCATE PREPARE STMT; -DROP TABLE t1; - drop table t1; @@ -1040,6 +988,58 @@ execute stmt; drop temporary table t1; deallocate prepare stmt; +# +# BUG#22085: Crash on the execution of a prepared statement that +# uses an IN subquery with aggregate functions in HAVING +# + +CREATE TABLE t1( + ID int(10) unsigned NOT NULL auto_increment, + Member_ID varchar(15) NOT NULL default '', + Action varchar(12) NOT NULL, + Action_Date datetime NOT NULL, + Track varchar(15) default NULL, + User varchar(12) default NULL, + Date_Updated timestamp NOT NULL default CURRENT_TIMESTAMP on update + CURRENT_TIMESTAMP, + PRIMARY KEY (ID), + KEY Action (Action), + KEY Action_Date (Action_Date) +); + +INSERT INTO t1(Member_ID, Action, Action_Date, Track) VALUES + ('111111', 'Disenrolled', '2006-03-01', 'CAD' ), + ('111111', 'Enrolled', '2006-03-01', 'CAD' ), + ('111111', 'Disenrolled', '2006-07-03', 'CAD' ), + ('222222', 'Enrolled', '2006-03-07', 'CAD' ), + ('222222', 'Enrolled', '2006-03-07', 'CHF' ), + ('222222', 'Disenrolled', '2006-08-02', 'CHF' ), + ('333333', 'Enrolled', '2006-03-01', 'CAD' ), + ('333333', 'Disenrolled', '2006-03-01', 'CAD' ), + ('444444', 'Enrolled', '2006-03-01', 'CAD' ), + ('555555', 'Disenrolled', '2006-03-01', 'CAD' ), + ('555555', 'Enrolled', '2006-07-21', 'CAD' ), + ('555555', 'Disenrolled', '2006-03-01', 'CHF' ), + ('666666', 'Enrolled', '2006-02-09', 'CAD' ), + ('666666', 'Enrolled', '2006-05-12', 'CHF' ), + ('666666', 'Disenrolled', '2006-06-01', 'CAD' ); + +PREPARE STMT FROM +"SELECT GROUP_CONCAT(Track SEPARATOR ', ') FROM t1 + WHERE Member_ID=? AND Action='Enrolled' AND + (Track,Action_Date) IN (SELECT Track, MAX(Action_Date) FROM t1 + WHERE Member_ID=? + GROUP BY Track + HAVING Track>='CAD' AND + MAX(Action_Date)>'2006-03-01')"; +SET @id='111111'; +EXECUTE STMT USING @id,@id; +SET @id='222222'; +EXECUTE STMT USING @id,@id; + +DEALLOCATE PREPARE STMT; +DROP TABLE t1; + --echo End of 4.1 tests ############################# 5.0 tests start ################################ # From 23fefc88010a0f1bbb1ac45bf41224b21fbb19c1 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 20 Sep 2006 01:40:59 +0500 Subject: [PATCH 06/26] BUG#10974 - No error message if merge table based on union of innodb, memory Fixed confusing error message from the storage engine when it fails to open underlying table. The error message is issued when a table is _opened_ (not when it is created). myisammrg/myrg_open.c: Set my_errno to HA_ERR_WRONG_MRG_TABLE_DEF if attempt to open underlying table failed. mysql-test/r/merge.result: A test case for bug#10974. mysql-test/r/repair.result: Fixed a test case according to patch for bug#10974. mysql-test/t/merge.test: A test case for bug#10974. sql/share/english/errmsg.txt: Better error message if we fail to open underlying table. sql/table.cc: Report error from handler with print_error instead of frm_error. This fixes confusing error message from the handler. Actually this is backported from 5.0. --- myisammrg/myrg_open.c | 3 +++ mysql-test/r/merge.result | 13 +++++++++++-- mysql-test/r/repair.result | 2 +- mysql-test/t/merge.test | 18 ++++++++++++++++-- sql/share/english/errmsg.txt | 2 +- sql/table.cc | 9 ++++++++- 6 files changed, 40 insertions(+), 7 deletions(-) diff --git a/myisammrg/myrg_open.c b/myisammrg/myrg_open.c index f9cdc2bb205..124d37904a6 100644 --- a/myisammrg/myrg_open.c +++ b/myisammrg/myrg_open.c @@ -89,7 +89,10 @@ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking) else fn_format(buff, buff, "", "", 0); if (!(isam=mi_open(buff,mode,(handle_locking?HA_OPEN_WAIT_IF_LOCKED:0)))) + { + my_errno= HA_ERR_WRONG_MRG_TABLE_DEF; goto err; + } if (!m_info) /* First file */ { key_parts=isam->s->base.key_parts; diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index 038ea43cabc..2d0ca55c74a 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -178,9 +178,9 @@ t3 CREATE TABLE `t3` ( ) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 UNION=(`t1`,`t2`) create table t4 (a int not null, b char(10), key(a)) engine=MERGE UNION=(t1,t2); select * from t4; -ERROR HY000: Can't open file: 't4.MRG' (errno: 143) +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exists alter table t4 add column c int; -ERROR HY000: Can't open file: 't4.MRG' (errno: 143) +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exists create database mysqltest; create table mysqltest.t6 (a int not null primary key auto_increment, message char(20)); create table t5 (a int not null, b char(20), key(a)) engine=MERGE UNION=(test.t1,mysqltest.t6); @@ -766,3 +766,12 @@ Table Op Msg_type Msg_text test.t1 check status OK test.t2 check status OK drop table t1, t2, t3; +CREATE TABLE t1(a INT) ENGINE=MEMORY; +CREATE TABLE t2(a INT) ENGINE=MERGE UNION=(t1); +SELECT * FROM t2; +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exists +DROP TABLE t1, t2; +CREATE TABLE t2(a INT) ENGINE=MERGE UNION=(t3); +SELECT * FROM t2; +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exists +DROP TABLE t2; diff --git a/mysql-test/r/repair.result b/mysql-test/r/repair.result index e0849452399..c069824e9f0 100644 --- a/mysql-test/r/repair.result +++ b/mysql-test/r/repair.result @@ -31,7 +31,7 @@ create table t1 engine=myisam SELECT 1,"table 1"; flush tables; repair table t1; Table Op Msg_type Msg_text -test.t1 repair error Can't open file: 't1.MYI' (errno: 130) +test.t1 repair error Got error 130 from storage engine repair table t1 use_frm; Table Op Msg_type Msg_text test.t1 repair warning Number of rows changed from 0 to 1 diff --git a/mysql-test/t/merge.test b/mysql-test/t/merge.test index a723443b395..93eda3aad82 100644 --- a/mysql-test/t/merge.test +++ b/mysql-test/t/merge.test @@ -47,9 +47,9 @@ show create table t3; # The following should give errors create table t4 (a int not null, b char(10), key(a)) engine=MERGE UNION=(t1,t2); ---error 1016 +--error 1168 select * from t4; ---error 1016 +--error 1168 alter table t4 add column c int; # @@ -376,4 +376,18 @@ select * from t3; check table t1, t2; drop table t1, t2, t3; +# +# BUG#10974 - No error message if merge table based on union of innodb, +# memory +# +CREATE TABLE t1(a INT) ENGINE=MEMORY; +CREATE TABLE t2(a INT) ENGINE=MERGE UNION=(t1); +--error 1168 +SELECT * FROM t2; +DROP TABLE t1, t2; +CREATE TABLE t2(a INT) ENGINE=MERGE UNION=(t3); +--error 1168 +SELECT * FROM t2; +DROP TABLE t2; + # End of 4.1 tests diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt index 300f3c6edfd..a8b06a07218 100644 --- a/sql/share/english/errmsg.txt +++ b/sql/share/english/errmsg.txt @@ -184,7 +184,7 @@ character-set=latin1 "INSERT DELAYED can't be used with table '%-.64s' because it is locked with LOCK TABLES", "Incorrect column name '%-.100s'", "The used storage engine can't index column '%-.64s'", -"All tables in the MERGE table are not identically defined", +"Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exists", "Can't write, because of unique constraint, to table '%-.64s'", "BLOB/TEXT column '%-.64s' used in key specification without a key length", "All parts of a PRIMARY KEY must be NOT NULL; if you need NULL in a key, use UNIQUE instead", diff --git a/sql/table.cc b/sql/table.cc index 7587531b2f9..7680c1ff7c0 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -77,6 +77,7 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, my_string record; const char **int_array; bool use_hash, null_field_first; + bool error_reported= FALSE; File file; Field **field_ptr,*reg_field; KEY *keyinfo; @@ -791,6 +792,11 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, error= 1; my_errno= ENOENT; } + else + { + outparam->file->print_error(err, MYF(0)); + error_reported= TRUE; + } goto err_not_open; /* purecov: inspected */ } } @@ -812,7 +818,8 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, err_end: /* Here when no file */ delete crypted; *root_ptr= old_root; - frm_error(error, outparam, name, ME_ERROR + ME_WAITTANG, errarg); + if (!error_reported) + frm_error(error, outparam, name, ME_ERROR + ME_WAITTANG, errarg); delete outparam->file; outparam->file=0; // For easyer errorchecking outparam->db_stat=0; From a952b3fcb21cd09ab00775d10d440cf8df0c48dd Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 20 Sep 2006 12:02:58 +0300 Subject: [PATCH 07/26] fixed merge --- mysql-test/r/func_group.result | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/r/func_group.result b/mysql-test/r/func_group.result index 32d6931c334..c6117053a60 100644 --- a/mysql-test/r/func_group.result +++ b/mysql-test/r/func_group.result @@ -860,8 +860,8 @@ CREATE TABLE t1 (a INT, b INT); INSERT INTO t1 VALUES (1,1),(1,2),(2,3); SELECT (SELECT COUNT(DISTINCT t1.b)) FROM t1 GROUP BY t1.a; (SELECT COUNT(DISTINCT t1.b)) -0 2 +1 SELECT (SELECT COUNT(DISTINCT 12)) FROM t1 GROUP BY t1.a; (SELECT COUNT(DISTINCT 12)) 1 From 15b1ee2fcfa1b90febc967fd33c486c0de56680d Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 20 Sep 2006 11:05:11 +0200 Subject: [PATCH 08/26] Bug#20830 - INSERT DELAYED does not honour SET INSERT_ID Bug#20627 - INSERT DELAYED does not honour auto_increment_* variables INSERT DELAYED ignored an explicitly set INSERT_ID and session specific auto_increment_* variables. The problem was that the inserts are done by a system thread, which does not have access to the session variables of the user thread. On a proposal of Guilhem I fixed it so that the variables are copied to the data structure for every delayed row. The system thread sets its session variables from these values. mysql-test/r/delayed.result: Bug#20830 - INSERT DELAYED does not honour SET INSERT_ID Bug#20627 - INSERT DELAYED does not honour auto_increment_* variables Turned some sleeps into FLUSH TABLEs. Added test cases. mysql-test/t/delayed.test: Bug#20830 - INSERT DELAYED does not honour SET INSERT_ID Bug#20627 - INSERT DELAYED does not honour auto_increment_* variables Turned some sleeps into FLUSH TABLEs. Added test cases. sql/sql_insert.cc: Bug#20830 - INSERT DELAYED does not honour SET INSERT_ID Bug#20627 - INSERT DELAYED does not honour auto_increment_* variables Added auto_increment/insert_id related variables to 'delayed_row'. The session values are copied to 'delayed_row' by the user thread. The delayed insert thread copies them to his session variables. --- mysql-test/r/delayed.result | 174 ++++++++++++++++++++++++++++++++++++ mysql-test/t/delayed.test | 148 ++++++++++++++++++++++++++++-- sql/sql_insert.cc | 46 ++++++++++ 3 files changed, 362 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/delayed.result b/mysql-test/r/delayed.result index a336f3b4108..6295fceec2b 100644 --- a/mysql-test/r/delayed.result +++ b/mysql-test/r/delayed.result @@ -7,6 +7,7 @@ insert delayed into t1 set a = 4; insert delayed into t1 set a = 5, tmsp = 19711006010203; insert delayed into t1 (a, tmsp) values (6, 19711006010203); insert delayed into t1 (a, tmsp) values (7, NULL); +FLUSH TABLE t1; insert into t1 set a = 8,tmsp=19711006010203; select * from t1 where tmsp=0; a tmsp @@ -22,6 +23,7 @@ insert delayed into t1 values (null,"c"); insert delayed into t1 values (3,"d"),(null,"e"); insert delayed into t1 values (3,"this will give an","error"); ERROR 21S01: Column count doesn't match value count at row 1 +FLUSH TABLE t1; show status like 'not_flushed_delayed_rows'; Variable_name Value Not_flushed_delayed_rows 0 @@ -54,6 +56,7 @@ insert delayed into t1 values(null); insert delayed into t1 values(null); insert delayed into t1 values(null); insert delayed into t1 values(null); +FLUSH TABLE t1; select * from t1 order by a; a 1 @@ -69,3 +72,174 @@ a 12 13 DROP TABLE t1; +SET @bug20627_old_auto_increment_offset= +@@auto_increment_offset= 2; +SET @bug20627_old_auto_increment_increment= +@@auto_increment_increment= 3; +SET @bug20627_old_session_auto_increment_offset= +@@session.auto_increment_offset= 4; +SET @bug20627_old_session_auto_increment_increment= +@@session.auto_increment_increment= 5; +SET @@auto_increment_offset= 2; +SET @@auto_increment_increment= 3; +SET @@session.auto_increment_offset= 4; +SET @@session.auto_increment_increment= 5; +CREATE TABLE t1 ( +c1 INT NOT NULL AUTO_INCREMENT, +PRIMARY KEY (c1) +); +INSERT INTO t1 VALUES (NULL),(NULL),(NULL); +SELECT * FROM t1; +c1 +4 +9 +14 +DROP TABLE t1; +CREATE TABLE t1 ( +c1 INT NOT NULL AUTO_INCREMENT, +PRIMARY KEY (c1) +); +INSERT DELAYED INTO t1 VALUES (NULL),(NULL),(NULL); +FLUSH TABLE t1; +SELECT * FROM t1; +c1 +4 +9 +14 +DROP TABLE t1; +SET @@auto_increment_offset= +@bug20627_old_auto_increment_offset; +SET @@auto_increment_increment= +@bug20627_old_auto_increment_increment; +SET @@session.auto_increment_offset= +@bug20627_old_session_auto_increment_offset; +SET @@session.auto_increment_increment= +@bug20627_old_session_auto_increment_increment; +SET @bug20830_old_auto_increment_offset= +@@auto_increment_offset= 2; +SET @bug20830_old_auto_increment_increment= +@@auto_increment_increment= 3; +SET @bug20830_old_session_auto_increment_offset= +@@session.auto_increment_offset= 4; +SET @bug20830_old_session_auto_increment_increment= +@@session.auto_increment_increment= 5; +SET @@auto_increment_offset= 2; +SET @@auto_increment_increment= 3; +SET @@session.auto_increment_offset= 4; +SET @@session.auto_increment_increment= 5; +CREATE TABLE t1 ( +c1 INT(11) NOT NULL AUTO_INCREMENT, +c2 INT(11) DEFAULT NULL, +PRIMARY KEY (c1) +); +SET insert_id= 14; +INSERT INTO t1 VALUES(NULL, 11), (NULL, 12), (NULL, 13); +INSERT INTO t1 VALUES(NULL, 21), (NULL, 22), (NULL, 23); +INSERT INTO t1 VALUES( 69, 31), (NULL, 32), (NULL, 33); +INSERT INTO t1 VALUES(NULL, 41), (NULL, 42), (NULL, 43); +SET insert_id= 114; +INSERT INTO t1 VALUES(NULL, 51), (NULL, 52), (NULL, 53); +INSERT INTO t1 VALUES(NULL, 61), (NULL, 62), (NULL, 63); +INSERT INTO t1 VALUES( 49, 71), (NULL, 72), (NULL, 73); +INSERT INTO t1 VALUES(NULL, 81), (NULL, 82), (NULL, 83); +SET insert_id= 114; +INSERT INTO t1 VALUES(NULL, 91); +ERROR 23000: Duplicate entry '114' for key 1 +INSERT INTO t1 VALUES (NULL, 92), (NULL, 93); +SELECT * FROM t1; +c1 c2 +14 11 +19 12 +24 13 +29 21 +34 22 +39 23 +69 31 +74 32 +79 33 +84 41 +89 42 +94 43 +114 51 +119 52 +124 53 +129 61 +134 62 +139 63 +49 71 +144 72 +149 73 +154 81 +159 82 +164 83 +169 92 +174 93 +SELECT COUNT(*) FROM t1; +COUNT(*) +26 +SELECT SUM(c1) FROM t1; +SUM(c1) +2569 +DROP TABLE t1; +CREATE TABLE t1 ( +c1 INT(11) NOT NULL AUTO_INCREMENT, +c2 INT(11) DEFAULT NULL, +PRIMARY KEY (c1) +); +SET insert_id= 14; +INSERT DELAYED INTO t1 VALUES(NULL, 11), (NULL, 12), (NULL, 13); +INSERT DELAYED INTO t1 VALUES(NULL, 21), (NULL, 22), (NULL, 23); +INSERT DELAYED INTO t1 VALUES( 69, 31), (NULL, 32), (NULL, 33); +INSERT DELAYED INTO t1 VALUES(NULL, 41), (NULL, 42), (NULL, 43); +SET insert_id= 114; +INSERT DELAYED INTO t1 VALUES(NULL, 51), (NULL, 52), (NULL, 53); +INSERT DELAYED INTO t1 VALUES(NULL, 61), (NULL, 62), (NULL, 63); +INSERT DELAYED INTO t1 VALUES( 49, 71), (NULL, 72), (NULL, 73); +INSERT DELAYED INTO t1 VALUES(NULL, 81), (NULL, 82), (NULL, 83); +SET insert_id= 114; +INSERT DELAYED INTO t1 VALUES(NULL, 91); +INSERT DELAYED INTO t1 VALUES (NULL, 92), (NULL, 93); +FLUSH TABLE t1; +SELECT * FROM t1; +c1 c2 +14 11 +19 12 +24 13 +29 21 +34 22 +39 23 +69 31 +74 32 +79 33 +84 41 +89 42 +94 43 +114 51 +119 52 +124 53 +129 61 +134 62 +139 63 +49 71 +144 72 +149 73 +154 81 +159 82 +164 83 +169 92 +174 93 +SELECT COUNT(*) FROM t1; +COUNT(*) +26 +SELECT SUM(c1) FROM t1; +SUM(c1) +2569 +DROP TABLE t1; +SET @@auto_increment_offset= +@bug20830_old_auto_increment_offset; +SET @@auto_increment_increment= +@bug20830_old_auto_increment_increment; +SET @@session.auto_increment_offset= +@bug20830_old_session_auto_increment_offset; +SET @@session.auto_increment_increment= +@bug20830_old_session_auto_increment_increment; diff --git a/mysql-test/t/delayed.test b/mysql-test/t/delayed.test index 55e8f81f763..03d8e20dd8f 100644 --- a/mysql-test/t/delayed.test +++ b/mysql-test/t/delayed.test @@ -17,7 +17,8 @@ insert delayed into t1 set a = 4; insert delayed into t1 set a = 5, tmsp = 19711006010203; insert delayed into t1 (a, tmsp) values (6, 19711006010203); insert delayed into t1 (a, tmsp) values (7, NULL); ---sleep 2 +# Wait until the rows are flushed to the table files. +FLUSH TABLE t1; insert into t1 set a = 8,tmsp=19711006010203; select * from t1 where tmsp=0; select * from t1 where tmsp=19711006010203; @@ -34,8 +35,8 @@ insert delayed into t1 values (null,"c"); insert delayed into t1 values (3,"d"),(null,"e"); --error 1136 insert delayed into t1 values (3,"this will give an","error"); -# 2 was not enough for --ps-protocol ---sleep 4 +# Wait until the rows are flushed to the table files. +FLUSH TABLE t1; show status like 'not_flushed_delayed_rows'; select * from t1; drop table t1; @@ -92,10 +93,145 @@ insert delayed into t1 values(null); # Works, since the delayed-counter is 8, which is unused insert delayed into t1 values(null); +# Wait until the rows are flushed to the table files. +FLUSH TABLE t1; # Check what we have now -# must wait so that the delayed thread finishes -# Note: this must be increased if the test fails ---sleep 1 select * from t1 order by a; DROP TABLE t1; + +# +# Bug#20627 - INSERT DELAYED does not honour auto_increment_* variables +# +SET @bug20627_old_auto_increment_offset= + @@auto_increment_offset= 2; +SET @bug20627_old_auto_increment_increment= + @@auto_increment_increment= 3; +SET @bug20627_old_session_auto_increment_offset= + @@session.auto_increment_offset= 4; +SET @bug20627_old_session_auto_increment_increment= + @@session.auto_increment_increment= 5; +SET @@auto_increment_offset= 2; +SET @@auto_increment_increment= 3; +SET @@session.auto_increment_offset= 4; +SET @@session.auto_increment_increment= 5; +# +# Normal insert as reference. +CREATE TABLE t1 ( + c1 INT NOT NULL AUTO_INCREMENT, + PRIMARY KEY (c1) + ); +INSERT INTO t1 VALUES (NULL),(NULL),(NULL); +# Check what we have now +SELECT * FROM t1; +DROP TABLE t1; +# +# Delayed insert. +CREATE TABLE t1 ( + c1 INT NOT NULL AUTO_INCREMENT, + PRIMARY KEY (c1) + ); +INSERT DELAYED INTO t1 VALUES (NULL),(NULL),(NULL); +# Wait until the rows are flushed to the table files. +FLUSH TABLE t1; +# Check what we have now +SELECT * FROM t1; +DROP TABLE t1; +# +# Cleanup +SET @@auto_increment_offset= + @bug20627_old_auto_increment_offset; +SET @@auto_increment_increment= + @bug20627_old_auto_increment_increment; +SET @@session.auto_increment_offset= + @bug20627_old_session_auto_increment_offset; +SET @@session.auto_increment_increment= + @bug20627_old_session_auto_increment_increment; + +# +# Bug#20830 - INSERT DELAYED does not honour SET INSERT_ID +# +SET @bug20830_old_auto_increment_offset= + @@auto_increment_offset= 2; +SET @bug20830_old_auto_increment_increment= + @@auto_increment_increment= 3; +SET @bug20830_old_session_auto_increment_offset= + @@session.auto_increment_offset= 4; +SET @bug20830_old_session_auto_increment_increment= + @@session.auto_increment_increment= 5; +SET @@auto_increment_offset= 2; +SET @@auto_increment_increment= 3; +SET @@session.auto_increment_offset= 4; +SET @@session.auto_increment_increment= 5; +# +# Normal insert as reference. +CREATE TABLE t1 ( + c1 INT(11) NOT NULL AUTO_INCREMENT, + c2 INT(11) DEFAULT NULL, + PRIMARY KEY (c1) + ); +SET insert_id= 14; +INSERT INTO t1 VALUES(NULL, 11), (NULL, 12), (NULL, 13); +INSERT INTO t1 VALUES(NULL, 21), (NULL, 22), (NULL, 23); +# Restart sequence at a different value. +INSERT INTO t1 VALUES( 69, 31), (NULL, 32), (NULL, 33); +INSERT INTO t1 VALUES(NULL, 41), (NULL, 42), (NULL, 43); +# Restart sequence at a different value. +SET insert_id= 114; +INSERT INTO t1 VALUES(NULL, 51), (NULL, 52), (NULL, 53); +INSERT INTO t1 VALUES(NULL, 61), (NULL, 62), (NULL, 63); +# Set one value below the maximum value. +INSERT INTO t1 VALUES( 49, 71), (NULL, 72), (NULL, 73); +INSERT INTO t1 VALUES(NULL, 81), (NULL, 82), (NULL, 83); +# Create a duplicate value. +SET insert_id= 114; +--error 1062 +INSERT INTO t1 VALUES(NULL, 91); +INSERT INTO t1 VALUES (NULL, 92), (NULL, 93); +# Check what we have now +SELECT * FROM t1; +SELECT COUNT(*) FROM t1; +SELECT SUM(c1) FROM t1; +DROP TABLE t1; +# +# Delayed insert. +CREATE TABLE t1 ( + c1 INT(11) NOT NULL AUTO_INCREMENT, + c2 INT(11) DEFAULT NULL, + PRIMARY KEY (c1) + ); +SET insert_id= 14; +INSERT DELAYED INTO t1 VALUES(NULL, 11), (NULL, 12), (NULL, 13); +INSERT DELAYED INTO t1 VALUES(NULL, 21), (NULL, 22), (NULL, 23); +# Restart sequence at a different value. +INSERT DELAYED INTO t1 VALUES( 69, 31), (NULL, 32), (NULL, 33); +INSERT DELAYED INTO t1 VALUES(NULL, 41), (NULL, 42), (NULL, 43); +# Restart sequence at a different value. +SET insert_id= 114; +INSERT DELAYED INTO t1 VALUES(NULL, 51), (NULL, 52), (NULL, 53); +INSERT DELAYED INTO t1 VALUES(NULL, 61), (NULL, 62), (NULL, 63); +# Set one value below the maximum value. +INSERT DELAYED INTO t1 VALUES( 49, 71), (NULL, 72), (NULL, 73); +INSERT DELAYED INTO t1 VALUES(NULL, 81), (NULL, 82), (NULL, 83); +# Create a duplicate value. +SET insert_id= 114; +INSERT DELAYED INTO t1 VALUES(NULL, 91); +INSERT DELAYED INTO t1 VALUES (NULL, 92), (NULL, 93); +# Wait until the rows are flushed to the table files. +FLUSH TABLE t1; +# Check what we have now +SELECT * FROM t1; +SELECT COUNT(*) FROM t1; +SELECT SUM(c1) FROM t1; +DROP TABLE t1; +# +# Cleanup +SET @@auto_increment_offset= + @bug20830_old_auto_increment_offset; +SET @@auto_increment_increment= + @bug20830_old_auto_increment_increment; +SET @@session.auto_increment_offset= + @bug20830_old_session_auto_increment_offset; +SET @@session.auto_increment_increment= + @bug20830_old_session_auto_increment_increment; + diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index eaa7d3a72db..e3aecbaace2 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1274,6 +1274,9 @@ public: time_t start_time; bool query_start_used,last_insert_id_used,insert_id_used, ignore, log_query; ulonglong last_insert_id; + ulonglong next_insert_id; + ulong auto_increment_increment; + ulong auto_increment_offset; timestamp_auto_set_type timestamp_field_type; uint query_length; @@ -1655,6 +1658,22 @@ static int write_delayed(THD *thd,TABLE *table,enum_duplicates duplic, bool igno row->last_insert_id= thd->last_insert_id; row->timestamp_field_type= table->timestamp_field_type; + /* The session variable settings can always be copied. */ + row->auto_increment_increment= thd->variables.auto_increment_increment; + row->auto_increment_offset= thd->variables.auto_increment_offset; + /* + Next insert id must be set for the first value in a multi-row insert + only. So clear it after the first use. Assume a multi-row insert. + Since the user thread doesn't really execute the insert, + thd->next_insert_id is left untouched between the rows. If we copy + the same insert id to every row of the multi-row insert, the delayed + insert thread would copy this before inserting every row. Thus it + tries to insert all rows with the same insert id. This fails on the + unique constraint. So just the first row would be really inserted. + */ + row->next_insert_id= thd->next_insert_id; + thd->next_insert_id= 0; + di->rows.push_back(row); di->stacked_inserts++; di->status=1; @@ -2026,6 +2045,14 @@ bool delayed_insert::handle_inserts(void) thd.insert_id_used=row->insert_id_used; table->timestamp_field_type= row->timestamp_field_type; + /* The session variable settings can always be copied. */ + thd.variables.auto_increment_increment= row->auto_increment_increment; + thd.variables.auto_increment_offset= row->auto_increment_offset; + /* Next insert id must be used only if non-zero. */ + if (row->next_insert_id) + thd.next_insert_id= row->next_insert_id; + DBUG_PRINT("loop", ("next_insert_id: %lu", (ulong) thd.next_insert_id)); + info.ignore= row->ignore; info.handle_duplicates= row->dup; if (info.ignore || @@ -2047,6 +2074,20 @@ bool delayed_insert::handle_inserts(void) info.error_count++; // Ignore errors thread_safe_increment(delayed_insert_errors,&LOCK_delayed_status); row->log_query = 0; + /* + We must reset next_insert_id. Otherwise all following rows may + become duplicates. If write_record() failed on a duplicate and + next_insert_id would be left unchanged, the next rows would also + be tried with the same insert id and would fail. Since the end + of a multi-row statement is unknown here, all following rows in + the queue would be dropped, regardless which thread added them. + After the queue is used up, next_insert_id is cleared and the + next run will succeed. This could even happen if these come from + the same multi-row statement as the current queue contents. That + way it would look somewhat random which rows are rejected after + a duplicate. + */ + thd.next_insert_id= 0; } if (using_ignore) { @@ -2092,6 +2133,7 @@ bool delayed_insert::handle_inserts(void) /* This should never happen */ table->file->print_error(error,MYF(0)); sql_print_error("%s",thd.net.last_error); + DBUG_PRINT("error", ("HA_EXTRA_NO_CACHE failed in loop")); goto err; } query_cache_invalidate3(&thd, table, 1); @@ -2117,6 +2159,7 @@ bool delayed_insert::handle_inserts(void) { // This shouldn't happen table->file->print_error(error,MYF(0)); sql_print_error("%s",thd.net.last_error); + DBUG_PRINT("error", ("HA_EXTRA_NO_CACHE failed after loop")); goto err; } query_cache_invalidate3(&thd, table, 1); @@ -2124,13 +2167,16 @@ bool delayed_insert::handle_inserts(void) DBUG_RETURN(0); err: + DBUG_EXECUTE("error", max_rows= 0;); /* Remove all not used rows */ while ((row=rows.get())) { delete row; thread_safe_increment(delayed_insert_errors,&LOCK_delayed_status); stacked_inserts--; + DBUG_EXECUTE("error", max_rows++;); } + DBUG_PRINT("error", ("dropped %lu rows after an error", max_rows)); thread_safe_increment(delayed_insert_errors, &LOCK_delayed_status); pthread_mutex_lock(&mutex); DBUG_RETURN(1); From 1eb82be26d93346a761b7201259d56d5f8590426 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 20 Sep 2006 17:12:37 +0500 Subject: [PATCH 09/26] BUG#21459 - myisam_ftdump gives bad counts for common words This problem affects myisam_ftdump tool only. For fulltext index positive subkeys means word weight, negative subkeys means number of documents in level 2 fulltext index. Fixed that document counter was not properly updated for keys having level 2 fulltext index. No test case for this bug. myisam/myisam_ftdump.c: For fulltext index positive subkeys means word weight, negative subkeys means number of documents in level 2 fulltext index. Fixed that document counter was not properly updated for keys having level 2 fulltext index. --- myisam/myisam_ftdump.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/myisam/myisam_ftdump.c b/myisam/myisam_ftdump.c index 809d7bcca89..2be95d11714 100644 --- a/myisam/myisam_ftdump.c +++ b/myisam/myisam_ftdump.c @@ -126,7 +126,6 @@ int main(int argc,char *argv[]) if (count || stats) { - doc_cnt++; if (strcmp(buf, buf2)) { if (*buf2) @@ -151,6 +150,7 @@ int main(int argc,char *argv[]) keylen2=keylen; doc_cnt=0; } + doc_cnt+= (subkeys >= 0 ? 1 : -subkeys); } if (dump) { @@ -166,7 +166,6 @@ int main(int argc,char *argv[]) if (count || stats) { - doc_cnt++; if (*buf2) { uniq++; From 1b5bd18dc6b34d3dc50b7495bae8c05e5125c0dc Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 20 Sep 2006 08:08:57 -0700 Subject: [PATCH 10/26] Fixed bug #22015: crash with GROUP_CONCAT over a derived table that returns the results of aggregation by GROUP_CONCAT. The crash was due to an overflow happened for the field sortoder->length. The fix prevents this overflow exploiting the fact that the value of sortoder->length cannot be greater than the value of thd->variables.max_sort_length. mysql-test/r/func_gconcat.result: Added a test case for bug #22015. mysql-test/t/func_gconcat.test: Added a test case for bug #22015. --- mysql-test/r/func_gconcat.result | 9 +++++++++ mysql-test/t/func_gconcat.test | 15 +++++++++++++++ sql/filesort.cc | 1 + 3 files changed, 25 insertions(+) diff --git a/mysql-test/r/func_gconcat.result b/mysql-test/r/func_gconcat.result index 6617ccc671e..93925670d01 100644 --- a/mysql-test/r/func_gconcat.result +++ b/mysql-test/r/func_gconcat.result @@ -654,3 +654,12 @@ CHAR_LENGTH( GROUP_CONCAT(b) ) 240001 SET GROUP_CONCAT_MAX_LEN = 1024; DROP TABLE t1; +CREATE TABLE t1 (a int, b int); +INSERT INTO t1 VALUES (2,1), (1,2), (2,2), (1,3); +SELECT GROUP_CONCAT(a), x +FROM (SELECT a, GROUP_CONCAT(b) x FROM t1 GROUP BY a) AS s +GROUP BY x; +GROUP_CONCAT(a) x +2 1,2 +1 2,3 +DROP TABLE t1; diff --git a/mysql-test/t/func_gconcat.test b/mysql-test/t/func_gconcat.test index 98c21986aa9..b5c468e1638 100644 --- a/mysql-test/t/func_gconcat.test +++ b/mysql-test/t/func_gconcat.test @@ -447,3 +447,18 @@ SELECT a, CHAR_LENGTH(b) FROM t1; SELECT CHAR_LENGTH( GROUP_CONCAT(b) ) FROM t1; SET GROUP_CONCAT_MAX_LEN = 1024; DROP TABLE t1; + +# +# Bug #22015: crash with GROUP_CONCAT over a derived table that +# returns the results of aggregation by GROUP_CONCAT +# + +CREATE TABLE t1 (a int, b int); + +INSERT INTO t1 VALUES (2,1), (1,2), (2,2), (1,3); + +SELECT GROUP_CONCAT(a), x + FROM (SELECT a, GROUP_CONCAT(b) x FROM t1 GROUP BY a) AS s + GROUP BY x; + +DROP TABLE t1; diff --git a/sql/filesort.cc b/sql/filesort.cc index 42d25dbbaee..f13354d5c72 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -1268,6 +1268,7 @@ sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length, switch ((sortorder->result_type=sortorder->item->result_type())) { case STRING_RESULT: sortorder->length=sortorder->item->max_length; + set_if_smaller(sortorder->length, thd->variables.max_sort_length); if (use_strnxfrm((cs=sortorder->item->collation.collation))) { sortorder->length= cs->coll->strnxfrmlen(cs, sortorder->length); From 0f50a8a7fea053127f236ca4956bba58370a8d81 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 20 Sep 2006 09:46:12 -0700 Subject: [PATCH 11/26] Fixed bug #20108. Any default value for a enum fields over UCS2 charsets was corrupted when we put it into the frm file, as it had been overwritten by its HEX representation. To fix it now we save a copy of structure that represents the enum type and when putting the default values we use this copy. mysql-test/r/ctype_ucs.result: Added a test case for bug #20108. mysql-test/t/ctype_ucs.test: Added a test case for bug #20108. --- mysql-test/r/ctype_ucs.result | 21 +++++++++++++++++++ mysql-test/t/ctype_ucs.test | 21 +++++++++++++++++++ sql/field.h | 2 ++ sql/unireg.cc | 39 ++++++++++++++++++++++++++++------- 4 files changed, 76 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/ctype_ucs.result b/mysql-test/r/ctype_ucs.result index 24a4ca9a85f..642a0fc13b5 100644 --- a/mysql-test/r/ctype_ucs.result +++ b/mysql-test/r/ctype_ucs.result @@ -747,6 +747,27 @@ select export_set(5, name, upper(name), ",", 5) from bug20536; export_set(5, name, upper(name), ",", 5) test1,TEST1,test1,TEST1,TEST1 'test\_2','TEST\_2','test\_2','TEST\_2','TEST\_2' +CREATE TABLE t1 ( +status enum('active','passive') collate latin1_general_ci +NOT NULL default 'passive' +); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `status` enum('active','passive') character set latin1 collate latin1_general_ci NOT NULL default 'passive' +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +ALTER TABLE t1 ADD a int NOT NULL AFTER status; +CREATE TABLE t2 ( +status enum('active','passive') collate ucs2_turkish_ci +NOT NULL default 'passive' +); +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `status` enum('active','passive') character set ucs2 collate ucs2_turkish_ci NOT NULL default 'passive' +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +ALTER TABLE t2 ADD a int NOT NULL AFTER status; +DROP TABLE t1,t2; select password(name) from bug20536; password(name) ???????????????????? diff --git a/mysql-test/t/ctype_ucs.test b/mysql-test/t/ctype_ucs.test index d96b9938f1b..641080f48ab 100644 --- a/mysql-test/t/ctype_ucs.test +++ b/mysql-test/t/ctype_ucs.test @@ -482,6 +482,27 @@ select make_set(3, name, upper(name)) from bug20536; select export_set(5, name, upper(name)) from bug20536; select export_set(5, name, upper(name), ",", 5) from bug20536; +# +# Bug #20108: corrupted default enum value for a ucs2 field +# + +CREATE TABLE t1 ( + status enum('active','passive') collate latin1_general_ci + NOT NULL default 'passive' +); +SHOW CREATE TABLE t1; +ALTER TABLE t1 ADD a int NOT NULL AFTER status; + +CREATE TABLE t2 ( + status enum('active','passive') collate ucs2_turkish_ci + NOT NULL default 'passive' +); +SHOW CREATE TABLE t2; +ALTER TABLE t2 ADD a int NOT NULL AFTER status; + +DROP TABLE t1,t2; + + # Some broken functions: add these tests just to document current behavior. # PASSWORD and OLD_PASSWORD don't work with UCS2 strings, but to fix it would diff --git a/sql/field.h b/sql/field.h index a33cb0a93aa..79fb7ff76d1 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1192,6 +1192,8 @@ public: uint decimals,flags,pack_length; Field::utype unireg_check; TYPELIB *interval; // Which interval to use + TYPELIB *save_interval; // Temporary copy for the above + // Used only for UCS2 intervals List interval_list; CHARSET_INFO *charset; Field::geometry_type geom_type; diff --git a/sql/unireg.cc b/sql/unireg.cc index e3bf763f700..e4fdc77912c 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -190,13 +190,19 @@ bool mysql_create_frm(THD *thd, my_string file_name, goto err3; { - /* Unescape all UCS2 intervals: were escaped in pack_headers */ + /* + Restore all UCS2 intervals. + HEX representation of them is not needed anymore. + */ List_iterator it(create_fields); create_field *field; while ((field=it++)) { - if (field->interval && field->charset->mbminlen > 1) - unhex_type2(field->interval); + if (field->save_interval) + { + field->interval= field->save_interval; + field->save_interval= 0; + } } } DBUG_RETURN(0); @@ -452,18 +458,36 @@ static bool pack_header(uchar *forminfo, enum db_type table_type, reclength=(uint) (field->offset+ data_offset + length); n_length+= (ulong) strlen(field->field_name)+1; field->interval_id=0; + field->save_interval= 0; if (field->interval) { uint old_int_count=int_count; if (field->charset->mbminlen > 1) { - /* Escape UCS2 intervals using HEX notation */ + /* + Escape UCS2 intervals using HEX notation to avoid + problems with delimiters between enum elements. + As the original representation is still needed in + the function make_empty_rec to create a record of + filled with default values it is saved in save_interval + The HEX representation is created from this copy. + */ + field->save_interval= field->interval; + field->interval= (TYPELIB*) sql_alloc(sizeof(TYPELIB)); + *field->interval= *field->save_interval; + field->interval->type_names= + (const char **) sql_alloc(sizeof(char*) * + (field->interval->count+1)); + field->interval->type_names[field->interval->count]= 0; + field->interval->type_lengths= + (uint *) sql_alloc(sizeof(uint) * field->interval->count); + for (uint pos= 0; pos < field->interval->count; pos++) { char *dst; - uint length= field->interval->type_lengths[pos], hex_length; - const char *src= field->interval->type_names[pos]; + uint length= field->save_interval->type_lengths[pos], hex_length; + const char *src= field->save_interval->type_names[pos]; const char *srcend= src + length; hex_length= length * 2; field->interval->type_lengths[pos]= hex_length; @@ -715,7 +739,8 @@ static bool make_empty_rec(File file,enum db_type table_type, field->charset, field->geom_type, field->unireg_check, - field->interval, + field->save_interval ? field->save_interval : + field->interval, field->field_name, &table); From ac746932e8e8505e33bf148234ba2b9d53e8b8b9 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 22 Sep 2006 17:23:25 +0200 Subject: [PATCH 12/26] Bug#14400 - Query joins wrong rows from table which is subject of "concurrent insert" After merge fix. --- myisam/mi_rkey.c | 1 + 1 file changed, 1 insertion(+) diff --git a/myisam/mi_rkey.c b/myisam/mi_rkey.c index 52403b110cb..be99d66618d 100644 --- a/myisam/mi_rkey.c +++ b/myisam/mi_rkey.c @@ -86,6 +86,7 @@ int mi_rkey(MI_INFO *info, byte *buf, int inx, const byte *key, uint key_len, if (info->lastpos >= info->state->data_file_length && (search_flag != HA_READ_KEY_EXACT || last_used_keyseg != keyinfo->seg + keyinfo->keysegs)) + { do { uint not_used; From 9bf2ed95539220b6da3452eecbab56ae33af2126 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 25 Sep 2006 05:24:07 -0700 Subject: [PATCH 13/26] Fixed bug #21853: assert failure for a grouping query with an ALL/ANY quantified subquery in HAVING. The Item::split_sum_func2 method should not create Item_ref for objects of any class derived from Item_subselect. mysql-test/r/subselect.result: Added a test case for bug #21853. mysql-test/t/subselect.test: Added a test case for bug #21853. --- mysql-test/r/subselect.result | 36 ++++++++++++++++++++++++++++++++++ mysql-test/t/subselect.test | 37 +++++++++++++++++++++++++++++++++++ sql/item.cc | 1 + 3 files changed, 74 insertions(+) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index ba4de7f6406..ad847b5f156 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -2946,3 +2946,39 @@ ORDER BY t2.c, t2.b LIMIT 1) WHERE t1.a = 10; a a b 10 1 359 drop table t1,t2; +CREATE TABLE t1 ( +field1 int NOT NULL, +field2 int NOT NULL, +field3 int NOT NULL, +PRIMARY KEY (field1,field2,field3) +); +CREATE TABLE t2 ( +fieldA int NOT NULL, +fieldB int NOT NULL, +PRIMARY KEY (fieldA,fieldB) +); +INSERT INTO t1 VALUES +(1,1,1), (1,1,2), (1,2,1), (1,2,2), (1,2,3), (1,3,1); +INSERT INTO t2 VALUES (1,1), (1,2), (1,3); +SELECT field1, field2, COUNT(*) +FROM t1 GROUP BY field1, field2; +field1 field2 COUNT(*) +1 1 2 +1 2 3 +1 3 1 +SELECT field1, field2 +FROM t1 +GROUP BY field1, field2 +HAVING COUNT(*) >= ALL (SELECT fieldB +FROM t2 WHERE fieldA = field1); +field1 field2 +1 2 +SELECT field1, field2 +FROM t1 +GROUP BY field1, field2 +HAVING COUNT(*) < ANY (SELECT fieldB +FROM t2 WHERE fieldA = field1); +field1 field2 +1 1 +1 3 +DROP TABLE t1, t2; diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 1018b5de005..6defa8b16a5 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -1911,4 +1911,41 @@ SELECT sql_no_cache t1.a, r.a, r.b FROM t1 LEFT JOIN t2 r drop table t1,t2; +# +# Bug #21853: assert failure for a grouping query with +# an ALL/ANY quantified subquery in HAVING +# + +CREATE TABLE t1 ( + field1 int NOT NULL, + field2 int NOT NULL, + field3 int NOT NULL, + PRIMARY KEY (field1,field2,field3) +); +CREATE TABLE t2 ( + fieldA int NOT NULL, + fieldB int NOT NULL, + PRIMARY KEY (fieldA,fieldB) +); + +INSERT INTO t1 VALUES + (1,1,1), (1,1,2), (1,2,1), (1,2,2), (1,2,3), (1,3,1); +INSERT INTO t2 VALUES (1,1), (1,2), (1,3); + +SELECT field1, field2, COUNT(*) + FROM t1 GROUP BY field1, field2; + +SELECT field1, field2 + FROM t1 + GROUP BY field1, field2 + HAVING COUNT(*) >= ALL (SELECT fieldB + FROM t2 WHERE fieldA = field1); +SELECT field1, field2 + FROM t1 + GROUP BY field1, field2 + HAVING COUNT(*) < ANY (SELECT fieldB + FROM t2 WHERE fieldA = field1); + +DROP TABLE t1, t2; + # End of 4.1 tests diff --git a/sql/item.cc b/sql/item.cc index 7c9f6ec77fb..94f0a24fcc3 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -387,6 +387,7 @@ void Item::split_sum_func2(THD *thd, Item **ref_pointer_array, } else if ((type() == SUM_FUNC_ITEM || (used_tables() & ~PARAM_TABLE_BIT)) && + type() != SUBSELECT_ITEM && type() != REF_ITEM) { /* From f0bd502ac8037341c49ff2d4e753407ccc6486ce Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 25 Sep 2006 06:15:14 -0700 Subject: [PATCH 14/26] Fixed bug #21646. Presence of a subquery in the ON expression of a join should not block merging the view that contains this join. Before this patch the such views were converted into into temporary table views. mysql-test/r/view.result: Added a test case for bug #21646. mysql-test/t/view.test: Added a test case for bug #21646. sql/mysql_priv.h: Fixed bug #21646. Added a new parsing state 'IN_ON', true when the parser is in an ON expression of a join. sql/sql_lex.cc: Fixed bug #21646. Presence of a subquery in the ON expression of a join should not block merging the view that contains this join. sql/sql_yacc.yy: Fixed bug #21646. Added a new parsing state 'IN_ON', true when the parser is in an ON expression of a join. --- mysql-test/r/view.result | 14 ++++++++++++++ mysql-test/t/view.test | 18 ++++++++++++++++++ sql/mysql_priv.h | 3 ++- sql/sql_lex.cc | 3 ++- sql/sql_yacc.yy | 8 ++++++++ 5 files changed, 44 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 9c05aba168c..7b81072cfd7 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -2935,4 +2935,18 @@ id select_type table type possible_keys key key_len ref rows Extra 2 SUBQUERY t1 ALL NULL NULL NULL NULL 3 DROP VIEW v1; DROP TABLE t1; +CREATE TABLE t1(pk int PRIMARY KEY); +CREATE TABLE t2(pk int PRIMARY KEY, fk int, ver int, org int); +CREATE ALGORITHM=MERGE VIEW v1 AS +SELECT t1.* +FROM t1 JOIN t2 +ON t2.fk = t1.pk AND +t2.ver = (SELECT MAX(t.ver) FROM t2 t WHERE t.org = t2.org); +SHOW WARNINGS; +Level Code Message +SHOW CREATE VIEW v1; +View Create View +v1 CREATE ALGORITHM=MERGE DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`pk` AS `pk` from (`t1` join `t2` on(((`t2`.`fk` = `t1`.`pk`) and (`t2`.`ver` = (select max(`t`.`ver`) AS `MAX(t.ver)` from `t2` `t` where (`t`.`org` = `t2`.`org`)))))) +DROP VIEW v1; +DROP TABLE t1, t2; End of 5.0 tests. diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index 4745804e847..b8cbb491832 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -2850,4 +2850,22 @@ EXPLAIN SELECT * FROM v1 t WHERE t.s1+1 < (SELECT MAX(t1.s1) FROM t1); DROP VIEW v1; DROP TABLE t1; + +# +# Bug #21646: view qith a subquery in ON expression +# + +CREATE TABLE t1(pk int PRIMARY KEY); +CREATE TABLE t2(pk int PRIMARY KEY, fk int, ver int, org int); + +CREATE ALGORITHM=MERGE VIEW v1 AS +SELECT t1.* + FROM t1 JOIN t2 + ON t2.fk = t1.pk AND + t2.ver = (SELECT MAX(t.ver) FROM t2 t WHERE t.org = t2.org); +SHOW WARNINGS; +SHOW CREATE VIEW v1; + +DROP VIEW v1; +DROP TABLE t1, t2; --echo End of 5.0 tests. diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index f293c769d75..f011aa42437 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -446,7 +446,8 @@ enum enum_parsing_place NO_MATTER, IN_HAVING, SELECT_LIST, - IN_WHERE + IN_WHERE, + IN_ON }; struct st_table; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index d0087b14d6a..788276ac654 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1710,7 +1710,8 @@ bool st_lex::can_be_merged() unit= unit->next_unit()) { if (unit->first_select()->parent_lex == this && - (unit->item == 0 || unit->item->place() != IN_WHERE)) + (unit->item == 0 || + (unit->item->place() != IN_WHERE && unit->item->place() != IN_ON))) { selects_allow_merge= 0; break; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index e39deadbf12..d027af8941b 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -5212,11 +5212,13 @@ join_table: /* Change the current name resolution context to a local context. */ if (push_new_name_resolution_context(YYTHD, $1, $3)) YYABORT; + Select->parsing_place= IN_ON; } expr { add_join_on($3,$6); Lex->pop_context(); + Select->parsing_place= NO_MATTER; } | table_ref STRAIGHT_JOIN table_factor ON @@ -5225,12 +5227,14 @@ join_table: /* Change the current name resolution context to a local context. */ if (push_new_name_resolution_context(YYTHD, $1, $3)) YYABORT; + Select->parsing_place= IN_ON; } expr { $3->straight=1; add_join_on($3,$6); Lex->pop_context(); + Select->parsing_place= NO_MATTER; } | table_ref normal_join table_ref USING @@ -5254,6 +5258,7 @@ join_table: /* Change the current name resolution context to a local context. */ if (push_new_name_resolution_context(YYTHD, $1, $5)) YYABORT; + Select->parsing_place= IN_ON; } expr { @@ -5261,6 +5266,7 @@ join_table: Lex->pop_context(); $5->outer_join|=JOIN_TYPE_LEFT; $$=$5; + Select->parsing_place= NO_MATTER; } | table_ref LEFT opt_outer JOIN_SYM table_factor { @@ -5285,6 +5291,7 @@ join_table: /* Change the current name resolution context to a local context. */ if (push_new_name_resolution_context(YYTHD, $1, $5)) YYABORT; + Select->parsing_place= IN_ON; } expr { @@ -5293,6 +5300,7 @@ join_table: YYABORT; add_join_on($$, $8); Lex->pop_context(); + Select->parsing_place= NO_MATTER; } | table_ref RIGHT opt_outer JOIN_SYM table_factor { From ca5e29475c8ae52035214c710be04bcb1ed13177 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 27 Sep 2006 12:53:53 +0300 Subject: [PATCH 15/26] Bug #21174: Index degrades sort performance and optimizer does not honor IGNORE INDEX - reversed the patch for 5.0 and moved to 5.1 mysql-test/r/group_by.result: Bug #21174: Index degrades sort performance and optimizer does not honor IGNORE INDEX - reversed the patch for 5.0 mysql-test/t/group_by.test: Bug #21174: Index degrades sort performance and optimizer does not honor IGNORE INDEX - reversed the patch for 5.0 sql/sql_select.cc: Bug #21174: Index degrades sort performance and optimizer does not honor IGNORE INDEX - reversed the patch for 5.0 --- mysql-test/r/group_by.result | 9 --------- mysql-test/t/group_by.test | 12 ------------ sql/sql_select.cc | 2 -- 3 files changed, 23 deletions(-) diff --git a/mysql-test/r/group_by.result b/mysql-test/r/group_by.result index ef057e04d1a..5eb2e5744c1 100644 --- a/mysql-test/r/group_by.result +++ b/mysql-test/r/group_by.result @@ -821,12 +821,3 @@ a b real_b 68 France France DROP VIEW v1; DROP TABLE t1,t2; -CREATE TABLE t1 (a INT, b INT, KEY(a)); -INSERT INTO t1 VALUES (1, 1), (2, 2), (3,3), (4,4); -EXPLAIN SELECT a, SUM(b) FROM t1 GROUP BY a LIMIT 2; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 index NULL a 5 NULL 4 -EXPLAIN SELECT a, SUM(b) FROM t1 IGNORE INDEX (a) GROUP BY a LIMIT 2; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 4 Using temporary; Using filesort -DROP TABLE t1; diff --git a/mysql-test/t/group_by.test b/mysql-test/t/group_by.test index 8a514108dc3..ce1e4e59600 100644 --- a/mysql-test/t/group_by.test +++ b/mysql-test/t/group_by.test @@ -655,15 +655,3 @@ where t2.b=v1.a GROUP BY t2.b; DROP VIEW v1; DROP TABLE t1,t2; - -# -# Bug #21174: Index degrades sort performance and -# optimizer does not honor IGNORE INDEX -# -CREATE TABLE t1 (a INT, b INT, KEY(a)); -INSERT INTO t1 VALUES (1, 1), (2, 2), (3,3), (4,4); - -EXPLAIN SELECT a, SUM(b) FROM t1 GROUP BY a LIMIT 2; -EXPLAIN SELECT a, SUM(b) FROM t1 IGNORE INDEX (a) GROUP BY a LIMIT 2; - -DROP TABLE t1; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index c38a9f4e637..d43f9fa214f 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -11735,8 +11735,6 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, We must not try to use disabled keys. */ usable_keys= table->s->keys_in_use; - /* we must not consider keys that are disabled by IGNORE INDEX */ - usable_keys.intersect(table->keys_in_use_for_query); for (ORDER *tmp_order=order; tmp_order ; tmp_order=tmp_order->next) { From 8948564e0d6fa1c7adfc599e95854f31f82790d5 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 27 Sep 2006 11:55:30 +0200 Subject: [PATCH 16/26] Bug#20719 - Reading dynamic records with write buffer could fail After merge fix. --- myisam/mi_dynrec.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/myisam/mi_dynrec.c b/myisam/mi_dynrec.c index e2d24ccb39e..4dec3055fa1 100644 --- a/myisam/mi_dynrec.c +++ b/myisam/mi_dynrec.c @@ -1148,9 +1148,6 @@ int _mi_read_dynamic_record(MI_INFO *info, my_off_t filepos, byte *buf) info->rec_cache.pos_in_file < filepos + MI_BLOCK_INFO_HEADER_LENGTH && flush_io_cache(&info->rec_cache)) goto err; - /* A corrupted table can have wrong pointers. (Bug# 19835) */ - if (block_info.next_filepos == HA_OFFSET_ERROR) - goto panic; info->rec_cache.seek_not_done=1; if ((b_type= _mi_get_block_info(&block_info, file, filepos)) & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR | From d6d1176eed9f249107410bc981f931dac81757ca Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 27 Sep 2006 18:33:31 +0500 Subject: [PATCH 17/26] After merge fix. --- mysql-test/r/merge.result | 18 +++++++++--------- sql/share/errmsg.txt | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index 50d392725e4..0e4cabc62e0 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -768,6 +768,15 @@ Table Op Msg_type Msg_text test.t1 check status OK test.t2 check status OK drop table t1, t2, t3; +CREATE TABLE t1(a INT) ENGINE=MEMORY; +CREATE TABLE t2(a INT) ENGINE=MERGE UNION=(t1); +SELECT * FROM t2; +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exists +DROP TABLE t1, t2; +CREATE TABLE t2(a INT) ENGINE=MERGE UNION=(t3); +SELECT * FROM t2; +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exists +DROP TABLE t2; create table t1 (b bit(1)); create table t2 (b bit(1)); create table tm (b bit(1)) engine = merge union = (t1,t2); @@ -785,13 +794,4 @@ insert into t1 values (1); ERROR HY000: Table 't1' is read only drop table t2; drop table t1; -CREATE TABLE t1(a INT) ENGINE=MEMORY; -CREATE TABLE t2(a INT) ENGINE=MERGE UNION=(t1); -SELECT * FROM t2; -ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exists -DROP TABLE t1, t2; -CREATE TABLE t2(a INT) ENGINE=MERGE UNION=(t3); -SELECT * FROM t2; -ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exists -DROP TABLE t2; End of 5.0 tests diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 05b31b1e8b2..a097b438fe0 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -3813,7 +3813,7 @@ ER_WRONG_MRG_TABLE cze "V-B¹echny tabulky v MERGE tabulce nejsou definovány stejnì" dan "Tabellerne i MERGE er ikke defineret ens" nla "Niet alle tabellen in de MERGE tabel hebben identieke gedefinities" - eng "All tables in the MERGE table are not identically defined" + eng "Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exists" est "Kõik tabelid MERGE tabeli määratluses ei ole identsed" fre "Toutes les tables de la table de type MERGE n'ont pas la même définition" ger "Nicht alle Tabellen in der MERGE-Tabelle sind gleich definiert" From 24a1e6f60504146789fba904ab93528bbc3b477e Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 28 Sep 2006 11:41:38 +0200 Subject: [PATCH 18/26] Bug#22384 - DELETE FROM table causes "Incorrect key file for table" Deletes on a big index could crash the index when it needs to shrink. Put a forgotten negation operator in. No test case. It is too big for the test suite. And it does not work with 4.0, only with higher versions. It is attached to the bug report. myisam/mi_delete.c: Bug#22384 - DELETE FROM table causes "Incorrect key file for table" Put a negation operator ('!') before _mi_get_last_key() in del(). It returns NULL on error, non-NULL on success. --- myisam/mi_delete.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/myisam/mi_delete.c b/myisam/mi_delete.c index 6f94e3c4256..2c7e2b28a3d 100644 --- a/myisam/mi_delete.c +++ b/myisam/mi_delete.c @@ -348,7 +348,7 @@ static int del(register MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *key, else { DBUG_PRINT("test",("Inserting of key when deleting")); - if (_mi_get_last_key(info,keyinfo,leaf_buff,keybuff,endpos, + if (!_mi_get_last_key(info,keyinfo,leaf_buff,keybuff,endpos, &tmp)) goto err; ret_value=_mi_insert(info,keyinfo,key,leaf_buff,endpos,keybuff, From 9e6eaaf08cd39e7372af9227a435e3f69f7ba493 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 28 Sep 2006 20:30:15 +0500 Subject: [PATCH 19/26] BUG#21675 - engine=archive 2GB file limit, when reached mysqld restarts on any query If mysqld is linked against system installed zlib (which is likely compiled w/o LFS) and archive table exceedes 2G, mysqld will likely be terminated with SIGXFSZ. Prior to actual write perform a check if there is space in data file. This fixes abnormal process termination with SIGXFSZ. No test case for this bugfix. sql/ha_archive.cc: Prior to actual write perform a check if there is space in data file. This fixes abnormal process termination with SIGXFSZ. sql/ha_archive.h: Added approximate archive data file size to archive share. --- sql/ha_archive.cc | 24 ++++++++++++++++++++---- sql/ha_archive.h | 1 + 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/sql/ha_archive.cc b/sql/ha_archive.cc index 3885defb4d5..de32f9b1fa4 100644 --- a/sql/ha_archive.cc +++ b/sql/ha_archive.cc @@ -120,6 +120,8 @@ static bool archive_inited= FALSE; /* Variables for archive share methods */ pthread_mutex_t archive_mutex; static HASH archive_open_tables; +static z_off_t max_zfile_size; +static int zoffset_size; /* The file extension */ #define ARZ ".ARZ" // The data file @@ -203,6 +205,8 @@ bool archive_db_init() } else { + zoffset_size= 2 << ((zlibCompileFlags() >> 6) & 3); + max_zfile_size= (z_off_t) (~(1 << (zoffset_size * 8 - 1))); archive_inited= TRUE; DBUG_RETURN(FALSE); } @@ -240,7 +244,7 @@ ha_archive::ha_archive(TABLE *table_arg) buffer.set((char *)byte_buffer, IO_SIZE, system_charset_info); /* The size of the offset value we will use for position() */ - ref_length = 2 << ((zlibCompileFlags() >> 6) & 3); + ref_length = zoffset_size; DBUG_ASSERT(ref_length <= sizeof(z_off_t)); } @@ -480,7 +484,8 @@ int ha_archive::init_archive_writer() DBUG_RETURN(1); } share->archive_write_open= TRUE; - + info(HA_STATUS_TIME); + share->approx_file_size= data_file_length; DBUG_RETURN(0); } @@ -651,10 +656,21 @@ error: */ int ha_archive::real_write_row(byte *buf, gzFile writer) { - z_off_t written; + z_off_t written, total_row_length; uint *ptr, *end; DBUG_ENTER("ha_archive::real_write_row"); - + total_row_length= table->s->reclength; + for (ptr= table->s->blob_field, end= ptr + table->s->blob_fields; + ptr != end; ptr++) + total_row_length+= ((Field_blob*) table->field[*ptr])->get_length(); + if (share->approx_file_size > max_zfile_size - total_row_length) + { + info(HA_STATUS_TIME); + share->approx_file_size= data_file_length; + if (share->approx_file_size > max_zfile_size - total_row_length) + DBUG_RETURN(HA_ERR_RECORD_FILE_FULL); + } + share->approx_file_size+= total_row_length; written= gzwrite(writer, buf, table->s->reclength); DBUG_PRINT("ha_archive::real_write_row", ("Wrote %d bytes expected %d", written, table->s->reclength)); if (!delayed_insert || !bulk_insert) diff --git a/sql/ha_archive.h b/sql/ha_archive.h index 2bac9fa605e..564b9f03bf5 100644 --- a/sql/ha_archive.h +++ b/sql/ha_archive.h @@ -38,6 +38,7 @@ typedef struct st_archive_share { bool dirty; /* Flag for if a flush should occur */ bool crashed; /* Meta file is crashed */ ha_rows rows_recorded; /* Number of rows in tables */ + z_off_t approx_file_size; /* Approximate archive data file size */ } ARCHIVE_SHARE; /* From 7d915f01934d73cf2500618fc7f5148768cfd1b8 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 28 Sep 2006 22:10:06 +0500 Subject: [PATCH 20/26] BUG#21617 - crash when selecting from merge table with inconsistent indexes Crash may happen when selecting from a merge table that has underlying tables with less indexes than in a merge table itself. If number of keys in merge table is not bigger than requested key number, return error. myisammrg/myrg_open.c: Store min(number of keys) in m_info instead of number of keys in last underlying table. myisammrg/myrg_queue.c: Return error if inx passed to _myrg_init_queue function is not less than number of keys. mysql-test/r/merge.result: A test case for bug#21617. mysql-test/t/merge.test: A test case for bug#21617. mysys/queues.c: Replaced annoying ifndef DBUG_OFF with DBUG_ASSERT, fixed coding style. The problem was that having queue overrun in debug build was hidden with this ifdef. --- myisammrg/myrg_open.c | 7 ++++-- myisammrg/myrg_queue.c | 2 ++ mysql-test/r/merge.result | 6 +++++ mysql-test/t/merge.test | 11 +++++++++ mysys/queues.c | 48 ++++++++++++++++----------------------- 5 files changed, 43 insertions(+), 31 deletions(-) diff --git a/myisammrg/myrg_open.c b/myisammrg/myrg_open.c index f9cdc2bb205..6527ac648f7 100644 --- a/myisammrg/myrg_open.c +++ b/myisammrg/myrg_open.c @@ -33,7 +33,7 @@ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking) { int save_errno,errpos=0; - uint files=0,i,dir_length,length,key_parts; + uint files= 0, i, dir_length, length, key_parts, min_keys= 0; ulonglong file_offset=0; char name_buff[FN_REFLEN*2],buff[FN_REFLEN],*end; MYRG_INFO *m_info=0; @@ -106,6 +106,7 @@ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking) files= 0; } m_info->reclength=isam->s->base.reclength; + min_keys= isam->s->base.keys; errpos=3; } m_info->open_tables[files].table= isam; @@ -121,6 +122,8 @@ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking) m_info->records+= isam->state->records; m_info->del+= isam->state->del; m_info->data_file_length+= isam->state->data_file_length; + if (min_keys > isam->s->base.keys) + min_keys= isam->s->base.keys; for (i=0; i < key_parts; i++) m_info->rec_per_key_part[i]+= (isam->s->state.rec_per_key_part[i] / m_info->tables); @@ -138,7 +141,7 @@ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking) my_errno=HA_ERR_RECORD_FILE_FULL; goto err; } - m_info->keys= files ? isam->s->base.keys : 0; + m_info->keys= min_keys; bzero((char*) &m_info->by_key,sizeof(m_info->by_key)); /* this works ok if the table list is empty */ diff --git a/myisammrg/myrg_queue.c b/myisammrg/myrg_queue.c index 7172b9f0e2a..bf99e8434a6 100644 --- a/myisammrg/myrg_queue.c +++ b/myisammrg/myrg_queue.c @@ -51,6 +51,8 @@ int _myrg_init_queue(MYRG_INFO *info,int inx,enum ha_rkey_function search_flag) error=my_errno; } } + else + my_errno= error= HA_ERR_WRONG_INDEX; return error; } diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index 038ea43cabc..bd9be0ae8b2 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -766,3 +766,9 @@ Table Op Msg_type Msg_text test.t1 check status OK test.t2 check status OK drop table t1, t2, t3; +CREATE TABLE t1(a INT); +INSERT INTO t1 VALUES(2),(1); +CREATE TABLE t2(a INT, KEY(a)) ENGINE=MERGE UNION=(t1); +SELECT * FROM t2 WHERE a=2; +ERROR HY000: Got error 124 from storage engine +DROP TABLE t1, t2; diff --git a/mysql-test/t/merge.test b/mysql-test/t/merge.test index a723443b395..219a33d344b 100644 --- a/mysql-test/t/merge.test +++ b/mysql-test/t/merge.test @@ -376,4 +376,15 @@ select * from t3; check table t1, t2; drop table t1, t2, t3; +# +# BUG#21617 - crash when selecting from merge table with inconsistent +# indexes +# +CREATE TABLE t1(a INT); +INSERT INTO t1 VALUES(2),(1); +CREATE TABLE t2(a INT, KEY(a)) ENGINE=MERGE UNION=(t1); +--error 1030 +SELECT * FROM t2 WHERE a=2; +DROP TABLE t1, t2; + # End of 4.1 tests diff --git a/mysys/queues.c b/mysys/queues.c index ecf1058af41..6a285ce7417 100644 --- a/mysys/queues.c +++ b/mysys/queues.c @@ -164,28 +164,22 @@ void delete_queue(QUEUE *queue) void queue_insert(register QUEUE *queue, byte *element) { - reg2 uint idx,next; + reg2 uint idx, next; int cmp; - -#ifndef DBUG_OFF - if (queue->elements < queue->max_elements) -#endif + DBUG_ASSERT(queue->elements < queue->max_elements); + queue->root[0]= element; + idx= ++queue->elements; + /* max_at_top swaps the comparison if we want to order by desc */ + while ((cmp= queue->compare(queue->first_cmp_arg, + element + queue->offset_to_key, + queue->root[(next= idx >> 1)] + + queue->offset_to_key)) && + (cmp ^ queue->max_at_top) < 0) { - queue->root[0]=element; - idx= ++queue->elements; - - /* max_at_top swaps the comparison if we want to order by desc */ - while ((cmp=queue->compare(queue->first_cmp_arg, - element+queue->offset_to_key, - queue->root[(next=idx >> 1)] + - queue->offset_to_key)) && - (cmp ^ queue->max_at_top) < 0) - { - queue->root[idx]=queue->root[next]; - idx=next; - } - queue->root[idx]=element; + queue->root[idx]= queue->root[next]; + idx= next; } + queue->root[idx]= element; } /* Remove item from queue */ @@ -193,16 +187,12 @@ void queue_insert(register QUEUE *queue, byte *element) byte *queue_remove(register QUEUE *queue, uint idx) { -#ifndef DBUG_OFF - if (idx >= queue->max_elements) - return 0; -#endif - { - byte *element=queue->root[++idx]; /* Intern index starts from 1 */ - queue->root[idx]=queue->root[queue->elements--]; - _downheap(queue,idx); - return element; - } + byte *element; + DBUG_ASSERT(idx < queue->max_elements); + element= queue->root[++idx]; /* Intern index starts from 1 */ + queue->root[idx]= queue->root[queue->elements--]; + _downheap(queue, idx); + return element; } /* Fix when element on top has been replaced */ From d332c37c584e294aa22e570808d8c3ac44f743ba Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 29 Sep 2006 01:00:18 +0400 Subject: [PATCH 21/26] Fixed bug#5505: Wrong error message on INSERT into a view On an INSERT into an updatable but non-insertable view an error message was issued stating the view being not updatable. This can lead to a confusion of a user. A new error message is introduced. Is is showed when a user tries to insert into a non-insertable view. sql/sql_base.cc: Fixed bug#5505: Wrong error message on INSERT into a view The update_non_unique_table_error() function now issues proper error for an INSERT. sql/sql_insert.cc: Fixed bug#5505: Wrong error message on INSERT into a view Issue the ER_NON_INSERTABLE_TABLE error instead of the ER_NON_UPDATABLE_TABLE on insert into a view. sql/sql_view.cc: Fixed bug#5505: Wrong error message on INSERT into a view Issue the ER_NON_INSERTABLE_TABLE error instead of the ER_NON_UPDATABLE_TABLE on insert into a view. mysql-test/r/view.result: Added the test case for bug#5505: Wrong error message on INSERT into a view Corrected a few test cases after fixing bug#5505 mysql-test/t/view.test: Added the test case for bug#5505: Wrong error message on INSERT into a view Corrected a few test cases after fixing bug#5505 sql/share/errmsg.txt: Fixed bug#5505: Wrong error message on INSERT into a view Added the ER_NON_INSERTABLE_TABLE error definition. --- mysql-test/r/view.result | 27 +++++++++++++++++---------- mysql-test/t/view.test | 32 ++++++++++++++++++++++---------- sql/share/errmsg.txt | 3 +++ sql/sql_base.cc | 5 ++++- sql/sql_insert.cc | 4 ++-- sql/sql_view.cc | 2 +- 6 files changed, 49 insertions(+), 24 deletions(-) diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 9c05aba168c..262ed48a0c4 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -472,11 +472,11 @@ create view v3 (x,y,z) as select b, a, b from t1; create view v4 (x,y,z) as select c+1, b, a from t1; create algorithm=temptable view v5 (x,y,z) as select c, b, a from t1; insert into v3 values (-60,4,30); -ERROR HY000: The target table v3 of the INSERT is not updatable +ERROR HY000: The target table v3 of the INSERT is not insertable-into insert into v4 values (-60,4,30); -ERROR HY000: The target table v4 of the INSERT is not updatable +ERROR HY000: The target table v4 of the INSERT is not insertable-into insert into v5 values (-60,4,30); -ERROR HY000: The target table v5 of the INSERT is not updatable +ERROR HY000: The target table v5 of the INSERT is not insertable-into insert into v1 values (-60,4,30); insert into v1 (z,y,x) values (50,6,-100); insert into v2 values (5,40); @@ -499,11 +499,11 @@ create view v3 (x,y,z) as select b, a, b from t1; create view v4 (x,y,z) as select c+1, b, a from t1; create algorithm=temptable view v5 (x,y,z) as select c, b, a from t1; insert into v3 select c, b, a from t2; -ERROR HY000: The target table v3 of the INSERT is not updatable +ERROR HY000: The target table v3 of the INSERT is not insertable-into insert into v4 select c, b, a from t2; -ERROR HY000: The target table v4 of the INSERT is not updatable +ERROR HY000: The target table v4 of the INSERT is not insertable-into insert into v5 select c, b, a from t2; -ERROR HY000: The target table v5 of the INSERT is not updatable +ERROR HY000: The target table v5 of the INSERT is not insertable-into insert into v1 select c, b, a from t2; insert into v1 (z,y,x) select a+20,b+2,-100 from t2; insert into v2 select b+1, a+10 from t2; @@ -1352,14 +1352,14 @@ drop table t1; create table t1 (s1 smallint); create view v1 as select * from t1 where 20 < (select (s1) from t1); insert into v1 values (30); -ERROR HY000: The target table v1 of the INSERT is not updatable +ERROR HY000: The target table v1 of the INSERT is not insertable-into create view v2 as select * from t1; create view v3 as select * from t1 where 20 < (select (s1) from v2); insert into v3 values (30); -ERROR HY000: The target table v3 of the INSERT is not updatable +ERROR HY000: The target table v3 of the INSERT is not insertable-into create view v4 as select * from v2 where 20 < (select (s1) from t1); insert into v4 values (30); -ERROR HY000: The target table v4 of the INSERT is not updatable +ERROR HY000: The target table v4 of the INSERT is not insertable-into drop view v4, v3, v2, v1; drop table t1; create table t1 (a int); @@ -2911,7 +2911,7 @@ INSERT INTO v2 VALUES (0); RETURN 0; END | SELECT f2(); -ERROR HY000: The target table v2 of the INSERT is not updatable +ERROR HY000: The target table v2 of the INSERT is not insertable-into DROP FUNCTION f1; DROP FUNCTION f2; DROP VIEW v1, v2; @@ -2935,4 +2935,11 @@ id select_type table type possible_keys key key_len ref rows Extra 2 SUBQUERY t1 ALL NULL NULL NULL NULL 3 DROP VIEW v1; DROP TABLE t1; +create table t1 (s1 int); +create view v1 as select s1 as a, s1 as b from t1; +insert into v1 values (1,1); +ERROR HY000: The target table v1 of the INSERT is not insertable-into +update v1 set a = 5; +drop view v1; +drop table t1; End of 5.0 tests. diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index 4745804e847..1a022cdbf66 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -347,13 +347,13 @@ create view v3 (x,y,z) as select b, a, b from t1; create view v4 (x,y,z) as select c+1, b, a from t1; create algorithm=temptable view v5 (x,y,z) as select c, b, a from t1; # try insert to VIEW with fields duplicate --- error 1288 +-- error 1470 insert into v3 values (-60,4,30); # try insert to VIEW with expression in SELECT list --- error 1288 +-- error 1470 insert into v4 values (-60,4,30); # try insert to VIEW using temporary table algorithm --- error 1288 +-- error 1470 insert into v5 values (-60,4,30); insert into v1 values (-60,4,30); insert into v1 (z,y,x) values (50,6,-100); @@ -375,13 +375,13 @@ create view v3 (x,y,z) as select b, a, b from t1; create view v4 (x,y,z) as select c+1, b, a from t1; create algorithm=temptable view v5 (x,y,z) as select c, b, a from t1; # try insert to VIEW with fields duplicate --- error 1288 +-- error 1470 insert into v3 select c, b, a from t2; # try insert to VIEW with expression in SELECT list --- error 1288 +-- error 1470 insert into v4 select c, b, a from t2; # try insert to VIEW using temporary table algorithm --- error 1288 +-- error 1470 insert into v5 select c, b, a from t2; insert into v1 select c, b, a from t2; insert into v1 (z,y,x) select a+20,b+2,-100 from t2; @@ -1249,14 +1249,14 @@ drop table t1; # create table t1 (s1 smallint); create view v1 as select * from t1 where 20 < (select (s1) from t1); --- error 1288 +-- error 1470 insert into v1 values (30); create view v2 as select * from t1; create view v3 as select * from t1 where 20 < (select (s1) from v2); --- error 1288 +-- error 1470 insert into v3 values (30); create view v4 as select * from v2 where 20 < (select (s1) from t1); --- error 1288 +-- error 1470 insert into v4 values (30); drop view v4, v3, v2, v1; drop table t1; @@ -2825,7 +2825,7 @@ BEGIN END | delimiter ;| ---error ER_NON_UPDATABLE_TABLE +--error ER_NON_INSERTABLE_TABLE SELECT f2(); DROP FUNCTION f1; @@ -2850,4 +2850,16 @@ EXPLAIN SELECT * FROM v1 t WHERE t.s1+1 < (SELECT MAX(t1.s1) FROM t1); DROP VIEW v1; DROP TABLE t1; + +# +# Bug #5505: Wrong error message on INSERT into a view +# +create table t1 (s1 int); +create view v1 as select s1 as a, s1 as b from t1; +--error 1470 +insert into v1 values (1,1); +update v1 set a = 5; +drop view v1; +drop table t1; + --echo End of 5.0 tests. diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index d10f66e3878..151e73460ba 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5629,3 +5629,6 @@ ER_HOSTNAME eng "host name" ER_WRONG_STRING_LENGTH eng "String '%-.70s' is too long for %s (should be no longer than %d)" +ER_NON_INSERTABLE_TABLE + eng "The target table %-.100s of the %s is not insertable-into" + diff --git a/sql/sql_base.cc b/sql/sql_base.cc index c29c610b200..85be84d1270 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -902,8 +902,11 @@ void update_non_unique_table_error(TABLE_LIST *update, */ if (update->view) { + /* Issue the ER_NON_INSERTABLE_TABLE error for an INSERT */ if (update->view == duplicate->view) - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), update->alias, operation); + my_error(!strncmp(operation, "INSERT", 6) ? + ER_NON_INSERTABLE_TABLE : ER_NON_UPDATABLE_TABLE, MYF(0), + update->alias, operation); else my_error(ER_VIEW_PREVENT_UPDATE, MYF(0), (duplicate->view ? duplicate->alias : update->alias), diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index b5dab814d08..3db6eae8695 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -111,7 +111,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, if (!table_list->updatable) { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "INSERT"); + my_error(ER_NON_INSERTABLE_TABLE, MYF(0), table_list->alias, "INSERT"); return -1; } @@ -214,7 +214,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, (table_list->view && check_view_insertability(thd, table_list))) { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "INSERT"); + my_error(ER_NON_INSERTABLE_TABLE, MYF(0), table_list->alias, "INSERT"); return -1; } diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 4e2b48d9faf..94c5ad331dd 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -1574,7 +1574,7 @@ bool insert_view_fields(THD *thd, List *list, TABLE_LIST *view) list->push_back(fld); else { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), view->alias, "INSERT"); + my_error(ER_NON_INSERTABLE_TABLE, MYF(0), view->alias, "INSERT"); DBUG_RETURN(TRUE); } } From 04bf9cc7c6f17d9c6ab14a7521c1ab1708f50993 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 29 Sep 2006 01:35:11 +0400 Subject: [PATCH 22/26] view.test: After merge fix mysql-test/t/view.test: After merge fix --- mysql-test/t/view.test | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index c732a019048..b631d2032c4 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -347,13 +347,13 @@ create view v3 (x,y,z) as select b, a, b from t1; create view v4 (x,y,z) as select c+1, b, a from t1; create algorithm=temptable view v5 (x,y,z) as select c, b, a from t1; # try insert to VIEW with fields duplicate --- error 1470 +-- error 1471 insert into v3 values (-60,4,30); # try insert to VIEW with expression in SELECT list --- error 1470 +-- error 1471 insert into v4 values (-60,4,30); # try insert to VIEW using temporary table algorithm --- error 1470 +-- error 1471 insert into v5 values (-60,4,30); insert into v1 values (-60,4,30); insert into v1 (z,y,x) values (50,6,-100); @@ -375,13 +375,13 @@ create view v3 (x,y,z) as select b, a, b from t1; create view v4 (x,y,z) as select c+1, b, a from t1; create algorithm=temptable view v5 (x,y,z) as select c, b, a from t1; # try insert to VIEW with fields duplicate --- error 1470 +-- error 1471 insert into v3 select c, b, a from t2; # try insert to VIEW with expression in SELECT list --- error 1470 +-- error 1471 insert into v4 select c, b, a from t2; # try insert to VIEW using temporary table algorithm --- error 1470 +-- error 1471 insert into v5 select c, b, a from t2; insert into v1 select c, b, a from t2; insert into v1 (z,y,x) select a+20,b+2,-100 from t2; @@ -1249,14 +1249,14 @@ drop table t1; # create table t1 (s1 smallint); create view v1 as select * from t1 where 20 < (select (s1) from t1); --- error 1470 +-- error 1471 insert into v1 values (30); create view v2 as select * from t1; create view v3 as select * from t1 where 20 < (select (s1) from v2); --- error 1470 +-- error 1471 insert into v3 values (30); create view v4 as select * from v2 where 20 < (select (s1) from t1); --- error 1470 +-- error 1471 insert into v4 values (30); drop view v4, v3, v2, v1; drop table t1; @@ -2856,7 +2856,7 @@ DROP TABLE t1; # create table t1 (s1 int); create view v1 as select s1 as a, s1 as b from t1; ---error 1470 +--error 1471 insert into v1 values (1,1); update v1 set a = 5; drop view v1; From f621def226d325aeb6551bd4b2cdd43ac6914acf Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 29 Sep 2006 19:00:52 +0500 Subject: [PATCH 23/26] Fix a test case according to fix for bug#10974. --- mysql-test/r/lowercase_table3.result | 2 +- mysql-test/t/lowercase_table3.test | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/lowercase_table3.result b/mysql-test/r/lowercase_table3.result index 8182d07c26b..2c31936f89e 100644 --- a/mysql-test/r/lowercase_table3.result +++ b/mysql-test/r/lowercase_table3.result @@ -6,5 +6,5 @@ drop table t1; flush tables; CREATE TABLE t1 (a int) ENGINE=INNODB; SELECT * from T1; -ERROR HY000: Can't open file: 'T1.ibd' (errno: 1) +ERROR HY000: Got error 1 from storage engine drop table t1; diff --git a/mysql-test/t/lowercase_table3.test b/mysql-test/t/lowercase_table3.test index 9208f3a76ec..549bd7d8045 100644 --- a/mysql-test/t/lowercase_table3.test +++ b/mysql-test/t/lowercase_table3.test @@ -32,7 +32,7 @@ flush tables; # CREATE TABLE t1 (a int) ENGINE=INNODB; ---error 1016 +--error 1030 SELECT * from T1; drop table t1; From 09f6d547b017bc9a9122dd8dc20f86dd7267b9b7 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 29 Sep 2006 21:36:17 +0500 Subject: [PATCH 24/26] Fixed that max_zfile_size was incorrectly calculated on big-endian boxes. Was introduced with patch for bug#21675. --- sql/ha_archive.cc | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sql/ha_archive.cc b/sql/ha_archive.cc index 3a30f404a40..bc3c819c4ed 100644 --- a/sql/ha_archive.cc +++ b/sql/ha_archive.cc @@ -206,7 +206,17 @@ bool archive_db_init() else { zoffset_size= 2 << ((zlibCompileFlags() >> 6) & 3); - max_zfile_size= (z_off_t) (~(1 << (zoffset_size * 8 - 1))); + switch (sizeof(z_off_t)) { + case 2: + max_zfile_size= INT_MAX16; + break; + case 8: + max_zfile_size= LONGLONG_MAX; + break; + case 4: + default: + max_zfile_size= INT_MAX32; + } archive_inited= TRUE; DBUG_RETURN(FALSE); } From be929087ec9a5b4e161591fcebab9687472779eb Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 2 Oct 2006 14:28:23 +0400 Subject: [PATCH 25/26] BUG#21726: Incorrect result with multiple invocations of LAST_INSERT_ID Non-upper-level INSERTs (the ones in the body of stored procedure, stored function, or trigger) into a table that have AUTO_INCREMENT column didn't affected the result of LAST_INSERT_ID() on this level. The problem was introduced with the fix of bug 6880, which in turn was introduced with the fix of bug 3117, where current insert_id value was remembered on the first call to LAST_INSERT_ID() (bug 3117) and was returned from that function until it was reset before the next _upper-level_ statement (bug 6880). The fix for bug#21726 brings back the behaviour of version 4.0, and implements the following: remember insert_id value at the beginning of the statement or expression (which at that point equals to the first insert_id value generated by the previous statement), and return that remembered value from LAST_INSERT_ID() or @@LAST_INSERT_ID. Thus, the value returned by LAST_INSERT_ID() is not affected by values generated by current statement, nor by LAST_INSERT_ID(expr) calls in this statement. Version 5.1 does not have this bug (it was fixed by WL 3146). mysql-test/r/rpl_insert_id.result: Add results for bug#21726: Incorrect result with multiple invocations of LAST_INSERT_ID, and bug#20339: stored procedure using LAST_INSERT_ID() does not replicate statement-based. mysql-test/t/rpl_insert_id.test: Add test cases for bug#21726: Incorrect result with multiple invocations of LAST_INSERT_ID, and bug#20339: stored procedure using LAST_INSERT_ID() does not replicate statement-based. sql/item_func.cc: Add implementation of Item_func_last_insert_id::fix_fields(), where we remember in THD::current_insert_id the first value generated during execution of the previous statement, which is returned then from Item_func_last_insert_id::val_int(). sql/item_func.h: Add declaration of Item_func_last_insert_id::fix_fields(). sql/log_event.cc: Do not set THD::last_insert_id_used on LAST_INSERT_ID_EVENT. Though we know the statement will call LAST_INSERT_ID(), it wasn't called yet. sql/set_var.cc: In sys_var_last_insert_id::value_ptr() remember in THD::current_insert_id the first value generated during execution of the previous statement, and return this value for @@LAST_INSERT_ID. sql/sql_class.cc: Reset THD::last_insert_id_used after each statement execution. sql/sql_class.h: Rather then remember current insert_id value on first invocation of THD::insert_id(), remember it in Item_func_last_insert_id::fix_fields(), sys_var_last_insert_id::value_ptr(), or mysql_execute_command(). Remove THD::insert_id(), as it lost its value now. sql/sql_insert.cc: THD::insert_id() is removed, use THD::last_insert_id directly. sql/sql_load.cc: THD::insert_id() is removed, using THD::last_insert_id directly is OK. sql/sql_parse.cc: Remember in THD::current_insert_id first generated insert id value of the previous statement in mysql_execute_command(). No need to reset THD::last_insert_id_used in mysql_reset_thd_for_next_command(), it will be reset after each statement. sql/sql_select.cc: If "IS NULL" is replaced with "= ", use right value, which is THD::current_insert_id, and also set THD::last_insert_id_used to issue binary log LAST_INSERT_ID_EVENT. sql/sql_update.cc: THD::insert_id() is removed, use THD::last_insert_id directly. tests/mysql_client_test.c: Add test case for bug#21726: Incorrect result with multiple invocations of LAST_INSERT_ID. --- mysql-test/r/rpl_insert_id.result | 129 ++++++++++++++++++++++++++++++ mysql-test/t/rpl_insert_id.test | 112 ++++++++++++++++++++++++++ sql/item_func.cc | 35 +++++++- sql/item_func.h | 1 + sql/log_event.cc | 1 - sql/set_var.cc | 13 ++- sql/sql_class.cc | 16 +++- sql/sql_class.h | 48 +++++++---- sql/sql_insert.cc | 8 +- sql/sql_load.cc | 12 +-- sql/sql_parse.cc | 16 +++- sql/sql_select.cc | 11 ++- sql/sql_update.cc | 4 +- tests/mysql_client_test.c | 40 ++++++++- 14 files changed, 405 insertions(+), 41 deletions(-) diff --git a/mysql-test/r/rpl_insert_id.result b/mysql-test/r/rpl_insert_id.result index e425cce9508..d0360c8b9a3 100644 --- a/mysql-test/r/rpl_insert_id.result +++ b/mysql-test/r/rpl_insert_id.result @@ -234,6 +234,135 @@ n b 2 100 3 350 drop table t1; +DROP PROCEDURE IF EXISTS p1; +DROP TABLE IF EXISTS t1, t2; +SELECT LAST_INSERT_ID(0); +LAST_INSERT_ID(0) +0 +CREATE TABLE t1 ( +id INT NOT NULL DEFAULT 0, +last_id INT, +PRIMARY KEY (id) +); +CREATE TABLE t2 ( +id INT NOT NULL AUTO_INCREMENT, +last_id INT, +PRIMARY KEY (id) +); +CREATE PROCEDURE p1() +BEGIN +INSERT INTO t2 (last_id) VALUES (LAST_INSERT_ID()); +INSERT INTO t1 (last_id) VALUES (LAST_INSERT_ID()); +END| +CALL p1(); +SELECT * FROM t1; +id last_id +0 1 +SELECT * FROM t2; +id last_id +1 0 +SELECT * FROM t1; +id last_id +0 1 +SELECT * FROM t2; +id last_id +1 0 +DROP PROCEDURE p1; +DROP TABLE t1, t2; +DROP PROCEDURE IF EXISTS p1; +DROP FUNCTION IF EXISTS f1; +DROP FUNCTION IF EXISTS f2; +DROP TABLE IF EXISTS t1, t2; +CREATE TABLE t1 ( +i INT NOT NULL AUTO_INCREMENT PRIMARY KEY, +j INT DEFAULT 0 +); +CREATE TABLE t2 (i INT); +CREATE PROCEDURE p1() +BEGIN +INSERT INTO t1 (i) VALUES (NULL); +INSERT INTO t2 (i) VALUES (LAST_INSERT_ID()); +INSERT INTO t1 (i) VALUES (NULL), (NULL); +INSERT INTO t2 (i) VALUES (LAST_INSERT_ID()); +END | +CREATE FUNCTION f1() RETURNS INT MODIFIES SQL DATA +BEGIN +INSERT INTO t1 (i) VALUES (NULL); +INSERT INTO t2 (i) VALUES (LAST_INSERT_ID()); +INSERT INTO t1 (i) VALUES (NULL), (NULL); +INSERT INTO t2 (i) VALUES (LAST_INSERT_ID()); +RETURN 0; +END | +CREATE FUNCTION f2() RETURNS INT NOT DETERMINISTIC +RETURN LAST_INSERT_ID() | +INSERT INTO t1 VALUES (NULL, -1); +CALL p1(); +SELECT f1(); +f1() +0 +INSERT INTO t1 VALUES (NULL, f2()), (NULL, LAST_INSERT_ID()), +(NULL, LAST_INSERT_ID()), (NULL, f2()), (NULL, f2()); +INSERT INTO t1 VALUES (NULL, f2()); +INSERT INTO t1 VALUES (NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID(5)), +(NULL, @@LAST_INSERT_ID); +INSERT INTO t1 VALUES (NULL, 0), (NULL, LAST_INSERT_ID()); +UPDATE t1 SET j= -1 WHERE i IS NULL; +SELECT * FROM t1; +i j +1 -1 +2 0 +3 0 +4 0 +5 0 +6 0 +7 0 +8 3 +9 3 +10 3 +11 3 +12 3 +13 8 +14 13 +15 5 +16 13 +17 -1 +18 14 +SELECT * FROM t2; +i +2 +3 +5 +6 +SELECT * FROM t1; +i j +1 -1 +2 0 +3 0 +4 0 +5 0 +6 0 +7 0 +8 3 +9 3 +10 3 +11 3 +12 3 +13 8 +14 13 +15 5 +16 13 +17 -1 +18 14 +SELECT * FROM t2; +i +2 +3 +5 +6 +DROP PROCEDURE p1; +DROP FUNCTION f1; +DROP FUNCTION f2; +DROP TABLE t1, t2; # End of 5.0 tests diff --git a/mysql-test/t/rpl_insert_id.test b/mysql-test/t/rpl_insert_id.test index 3c46a5b4ca1..126aad68df4 100644 --- a/mysql-test/t/rpl_insert_id.test +++ b/mysql-test/t/rpl_insert_id.test @@ -244,6 +244,118 @@ select * from t1 order by n; connection master; drop table t1; + +# +# BUG#20339: stored procedure using LAST_INSERT_ID() does not +# replicate statement-based +# +--disable_warnings +DROP PROCEDURE IF EXISTS p1; +DROP TABLE IF EXISTS t1, t2; +--enable_warnings + +# Reset result of LAST_INSERT_ID(). +SELECT LAST_INSERT_ID(0); + +CREATE TABLE t1 ( + id INT NOT NULL DEFAULT 0, + last_id INT, + PRIMARY KEY (id) +); + +CREATE TABLE t2 ( + id INT NOT NULL AUTO_INCREMENT, + last_id INT, + PRIMARY KEY (id) +); + +delimiter |; +CREATE PROCEDURE p1() +BEGIN + INSERT INTO t2 (last_id) VALUES (LAST_INSERT_ID()); + INSERT INTO t1 (last_id) VALUES (LAST_INSERT_ID()); +END| +delimiter ;| + +CALL p1(); +SELECT * FROM t1; +SELECT * FROM t2; + +sync_slave_with_master; +SELECT * FROM t1; +SELECT * FROM t2; + +connection master; + +DROP PROCEDURE p1; +DROP TABLE t1, t2; + + +# +# BUG#21726: Incorrect result with multiple invocations of +# LAST_INSERT_ID +# +--disable_warnings +DROP PROCEDURE IF EXISTS p1; +DROP FUNCTION IF EXISTS f1; +DROP FUNCTION IF EXISTS f2; +DROP TABLE IF EXISTS t1, t2; +--enable_warnings + +CREATE TABLE t1 ( + i INT NOT NULL AUTO_INCREMENT PRIMARY KEY, + j INT DEFAULT 0 +); +CREATE TABLE t2 (i INT); + +delimiter |; +CREATE PROCEDURE p1() +BEGIN + INSERT INTO t1 (i) VALUES (NULL); + INSERT INTO t2 (i) VALUES (LAST_INSERT_ID()); + INSERT INTO t1 (i) VALUES (NULL), (NULL); + INSERT INTO t2 (i) VALUES (LAST_INSERT_ID()); +END | + +CREATE FUNCTION f1() RETURNS INT MODIFIES SQL DATA +BEGIN + INSERT INTO t1 (i) VALUES (NULL); + INSERT INTO t2 (i) VALUES (LAST_INSERT_ID()); + INSERT INTO t1 (i) VALUES (NULL), (NULL); + INSERT INTO t2 (i) VALUES (LAST_INSERT_ID()); + RETURN 0; +END | + +CREATE FUNCTION f2() RETURNS INT NOT DETERMINISTIC + RETURN LAST_INSERT_ID() | +delimiter ;| + +INSERT INTO t1 VALUES (NULL, -1); +CALL p1(); +SELECT f1(); +INSERT INTO t1 VALUES (NULL, f2()), (NULL, LAST_INSERT_ID()), + (NULL, LAST_INSERT_ID()), (NULL, f2()), (NULL, f2()); +INSERT INTO t1 VALUES (NULL, f2()); +INSERT INTO t1 VALUES (NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID(5)), + (NULL, @@LAST_INSERT_ID); +# Test replication of substitution "IS NULL" -> "= LAST_INSERT_ID". +INSERT INTO t1 VALUES (NULL, 0), (NULL, LAST_INSERT_ID()); +UPDATE t1 SET j= -1 WHERE i IS NULL; + +SELECT * FROM t1; +SELECT * FROM t2; + +sync_slave_with_master; +SELECT * FROM t1; +SELECT * FROM t2; + +connection master; +DROP PROCEDURE p1; +DROP FUNCTION f1; +DROP FUNCTION f2; +DROP TABLE t1, t2; + + sync_slave_with_master; --echo diff --git a/sql/item_func.cc b/sql/item_func.cc index 2e594c74031..e395a7a3af5 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3345,6 +3345,34 @@ longlong Item_func_release_lock::val_int() } +bool Item_func_last_insert_id::fix_fields(THD *thd, Item **ref) +{ + DBUG_ASSERT(fixed == 0); + + if (Item_int_func::fix_fields(thd, ref)) + return TRUE; + + if (arg_count == 0) + { + if (!thd->last_insert_id_used) + { + /* + As this statement calls LAST_INSERT_ID(), set + THD::last_insert_id_used and remember first generated insert + id of the previous statement in THD::current_insert_id. + */ + thd->last_insert_id_used= TRUE; + thd->current_insert_id= thd->last_insert_id; + } + null_value= FALSE; + } + + thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + + return FALSE; +} + + longlong Item_func_last_insert_id::val_int() { THD *thd= current_thd; @@ -3354,12 +3382,13 @@ longlong Item_func_last_insert_id::val_int() longlong value= args[0]->val_int(); thd->insert_id(value); null_value= args[0]->null_value; - return value; // Avoid side effect of insert_id() + return value; } - thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - return thd->last_insert_id_used ? thd->current_insert_id : thd->insert_id(); + + return thd->current_insert_id; } + /* This function is just used to test speed of different functions */ longlong Item_func_benchmark::val_int() diff --git a/sql/item_func.h b/sql/item_func.h index 177daf0311f..31adc033034 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -891,6 +891,7 @@ public: if (arg_count) max_length= args[0]->max_length; } + bool fix_fields(THD *thd, Item **ref); }; diff --git a/sql/log_event.cc b/sql/log_event.cc index 219434ab218..271658d8054 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -3365,7 +3365,6 @@ int Intvar_log_event::exec_event(struct st_relay_log_info* rli) { switch (type) { case LAST_INSERT_ID_EVENT: - thd->last_insert_id_used = 1; thd->last_insert_id = val; break; case INSERT_ID_EVENT: diff --git a/sql/set_var.cc b/sql/set_var.cc index c667e2f2bcc..d00857a2bc1 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -2571,8 +2571,17 @@ bool sys_var_last_insert_id::update(THD *thd, set_var *var) byte *sys_var_last_insert_id::value_ptr(THD *thd, enum_var_type type, LEX_STRING *base) { - thd->sys_var_tmp.long_value= (long) thd->insert_id(); - return (byte*) &thd->last_insert_id; + if (!thd->last_insert_id_used) + { + /* + As this statement reads @@LAST_INSERT_ID, set + THD::last_insert_id_used and remember first generated insert id + of the previous statement in THD::current_insert_id. + */ + thd->last_insert_id_used= TRUE; + thd->current_insert_id= thd->last_insert_id; + } + return (byte*) &thd->current_insert_id; } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 093173ab949..4d47ec338c0 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -553,10 +553,24 @@ bool THD::store_globals() } -/* Cleanup after a query */ +/* + Cleanup after query. + SYNOPSIS + THD::cleanup_after_query() + + DESCRIPTION + This function is used to reset thread data to it's default state. + + NOTE + This function is not suitable for setting thread data to some + non-default values, as there is only one replication thread, so + different master threads may overwrite data of each other on + slave. +*/ void THD::cleanup_after_query() { + last_insert_id_used= FALSE; if (clear_next_insert_id) { clear_next_insert_id= 0; diff --git a/sql/sql_class.h b/sql/sql_class.h index 039c133e885..ccc7a661446 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1252,17 +1252,29 @@ public: ulonglong next_insert_id; /* Remember last next_insert_id to reset it if something went wrong */ ulonglong prev_insert_id; + /* - The insert_id used for the last statement or set by SET LAST_INSERT_ID=# - or SELECT LAST_INSERT_ID(#). Used for binary log and returned by - LAST_INSERT_ID() + At the beginning of the statement last_insert_id holds the first + generated value of the previous statement. During statement + execution it is updated to the value just generated, but then + restored to the value that was generated first, so for the next + statement it will again be "the first generated value of the + previous statement". + + It may also be set with "LAST_INSERT_ID(expr)" or + "@@LAST_INSERT_ID= expr", but the effect of such setting will be + seen only in the next statement. */ ulonglong last_insert_id; + /* - Set to the first value that LAST_INSERT_ID() returned for the last - statement. When this is set, last_insert_id_used is set to true. + current_insert_id remembers the first generated value of the + previous statement, and does not change during statement + execution. Its value returned from LAST_INSERT_ID() and + @@LAST_INSERT_ID. */ ulonglong current_insert_id; + ulonglong limit_found_rows; ulonglong options; /* Bitmap of states */ longlong row_count_func; /* For the ROW_COUNT() function */ @@ -1325,7 +1337,22 @@ public: bool last_cuted_field; bool no_errors, password, is_fatal_error; bool query_start_used, rand_used, time_zone_used; - bool last_insert_id_used,insert_id_used, clear_next_insert_id; + + /* + last_insert_id_used is set when current statement calls + LAST_INSERT_ID() or reads @@LAST_INSERT_ID, so that binary log + LAST_INSERT_ID_EVENT be generated. + */ + bool last_insert_id_used; + + /* + insert_id_used is set when current statement updates + THD::last_insert_id, so that binary log INSERT_ID_EVENT be + generated. + */ + bool insert_id_used; + + bool clear_next_insert_id; /* for IS NULL => = last_insert_id() fix in remove_eq_conds() */ bool substitute_null_with_insert_id; bool in_lock_tables; @@ -1461,15 +1488,6 @@ public: insert_id_used=1; substitute_null_with_insert_id= TRUE; } - inline ulonglong insert_id(void) - { - if (!last_insert_id_used) - { - last_insert_id_used=1; - current_insert_id=last_insert_id; - } - return last_insert_id; - } inline ulonglong found_rows(void) { return limit_found_rows; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 3db6eae8695..43d646404f1 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -590,10 +590,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, #endif error=write_record(thd, table ,&info); /* - If auto_increment values are used, save the first one - for LAST_INSERT_ID() and for the update log. - We can't use insert_id() as we don't want to touch the - last_insert_id_used flag. + If auto_increment values are used, save the first one for + LAST_INSERT_ID() and for the update log. */ if (! id && thd->insert_id_used) { // Get auto increment value @@ -2447,7 +2445,7 @@ bool select_insert::send_data(List &values) */ table->next_number_field->reset(); if (!last_insert_id && thd->insert_id_used) - last_insert_id= thd->insert_id(); + last_insert_id= thd->last_insert_id; } } DBUG_RETURN(error); diff --git a/sql/sql_load.cc b/sql/sql_load.cc index d5faf6ee7e9..bdc08b7bd2d 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -616,10 +616,8 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, thd->no_trans_update= no_trans_update; /* - If auto_increment values are used, save the first one - for LAST_INSERT_ID() and for the binary/update log. - We can't use insert_id() as we don't want to touch the - last_insert_id_used flag. + If auto_increment values are used, save the first one for + LAST_INSERT_ID() and for the binary/update log. */ if (!id && thd->insert_id_used) id= thd->last_insert_id; @@ -784,10 +782,8 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, if (write_record(thd, table, &info)) DBUG_RETURN(1); /* - If auto_increment values are used, save the first one - for LAST_INSERT_ID() and for the binary/update log. - We can't use insert_id() as we don't want to touch the - last_insert_id_used flag. + If auto_increment values are used, save the first one for + LAST_INSERT_ID() and for the binary/update log. */ if (!id && thd->insert_id_used) id= thd->last_insert_id; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 18d048df393..1b69e266442 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2421,6 +2421,20 @@ mysql_execute_command(THD *thd) DBUG_ENTER("mysql_execute_command"); thd->net.no_send_error= 0; + /* + Remember first generated insert id value of the previous + statement. We remember it here at the beginning of the statement, + and also in Item_func_last_insert_id::fix_fields() and + sys_var_last_insert_id::value_ptr(). Last two places are required + because LAST_INSERT_ID() and @@LAST_INSERT_ID may also be used in + expression that is not executed with mysql_execute_command(). + + And we remember it here because some statements read + @@LAST_INSERT_ID indirectly, like "SELECT * FROM t1 WHERE id IS + NULL", that may replace "id IS NULL" with "id = ". + */ + thd->current_insert_id= thd->last_insert_id; + /* In many cases first table of main SELECT_LEX have special meaning => check that it is first table in global list and relink it first in @@ -5636,7 +5650,7 @@ void mysql_reset_thd_for_next_command(THD *thd) DBUG_ENTER("mysql_reset_thd_for_next_command"); thd->free_list= 0; thd->select_number= 1; - thd->last_insert_id_used= thd->query_start_used= thd->insert_id_used=0; + thd->query_start_used= thd->insert_id_used=0; thd->is_fatal_error= thd->time_zone_used= 0; thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS | SERVER_QUERY_NO_INDEX_USED | diff --git a/sql/sql_select.cc b/sql/sql_select.cc index b328d9162d5..87cbdef0522 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -8142,7 +8142,7 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) Field *field=((Item_field*) args[0])->field; if (field->flags & AUTO_INCREMENT_FLAG && !field->table->maybe_null && (thd->options & OPTION_AUTO_IS_NULL) && - thd->insert_id() && thd->substitute_null_with_insert_id) + thd->current_insert_id && thd->substitute_null_with_insert_id) { #ifdef HAVE_QUERY_CACHE query_cache_abort(&thd->net); @@ -8150,9 +8150,16 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) COND *new_cond; if ((new_cond= new Item_func_eq(args[0], new Item_int("last_insert_id()", - thd->insert_id(), + thd->current_insert_id, 21)))) { + /* + Set THD::last_insert_id_used manually, as this statement + uses LAST_INSERT_ID() in a sense, and should issue + LAST_INSERT_ID_EVENT. + */ + thd->last_insert_id_used= TRUE; + cond=new_cond; /* Item_func_eq can't be fixed after creation so we do not check diff --git a/sql/sql_update.cc b/sql/sql_update.cc index a5a767b6ebc..50914fd3408 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -568,7 +568,7 @@ int mysql_update(THD *thd, thd->row_count_func= (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated; send_ok(thd, (ulong) thd->row_count_func, - thd->insert_id_used ? thd->insert_id() : 0L,buff); + thd->insert_id_used ? thd->last_insert_id : 0L,buff); DBUG_PRINT("info",("%d records updated",updated)); } thd->count_cuted_fields= CHECK_FIELD_IGNORE; /* calc cuted fields */ @@ -1567,6 +1567,6 @@ bool multi_update::send_eof() thd->row_count_func= (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated; ::send_ok(thd, (ulong) thd->row_count_func, - thd->insert_id_used ? thd->insert_id() : 0L,buff); + thd->insert_id_used ? thd->last_insert_id : 0L,buff); return FALSE; } diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 8a444590301..d3f39296445 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -15237,6 +15237,43 @@ static void test_bug21206() DBUG_VOID_RETURN; } +/* + Bug#21726: Incorrect result with multiple invocations of + LAST_INSERT_ID + + Test that client gets updated value of insert_id on UPDATE that uses + LAST_INSERT_ID(expr). +*/ +static void test_bug21726() +{ + const char *create_table[]= + { + "DROP TABLE IF EXISTS t1", + "CREATE TABLE t1 (i INT)", + "INSERT INTO t1 VALUES (1)", + }; + const char *update_query= "UPDATE t1 SET i= LAST_INSERT_ID(i + 1)"; + int rc; + my_ulonglong insert_id; + + DBUG_ENTER("test_bug21726"); + myheader("test_bug21726"); + + fill_tables(create_table, sizeof(create_table) / sizeof(*create_table)); + + rc= mysql_query(mysql, update_query); + myquery(rc); + insert_id= mysql_insert_id(mysql); + DIE_UNLESS(insert_id == 2); + + rc= mysql_query(mysql, update_query); + myquery(rc); + insert_id= mysql_insert_id(mysql); + DIE_UNLESS(insert_id == 3); + + DBUG_VOID_RETURN; +} + /* Read and parse arguments and MySQL options from my.cnf @@ -15511,7 +15548,8 @@ static struct my_tests_st my_tests[]= { { "test_bug17667", test_bug17667 }, { "test_bug19671", test_bug19671 }, { "test_bug15752", test_bug15752 }, - { "test_bug21206", test_bug21206}, + { "test_bug21206", test_bug21206 }, + { "test_bug21726", test_bug21726 }, { 0, 0 } }; From 84000c374b5e4e2fa61a11481bf65a87f0b123a1 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 2 Oct 2006 15:15:16 +0200 Subject: [PATCH 26/26] Raise version number --- configure.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index 7bc4fe77675..55da1dfb241 100644 --- a/configure.in +++ b/configure.in @@ -7,7 +7,7 @@ AC_INIT(sql/mysqld.cc) AC_CANONICAL_SYSTEM # The Docs Makefile.am parses this line! # remember to also change ndb version below and update version.c in ndb -AM_INIT_AUTOMAKE(mysql, 5.0.26) +AM_INIT_AUTOMAKE(mysql, 5.0.27) AM_CONFIG_HEADER(config.h) PROTOCOL_VERSION=10 @@ -19,7 +19,7 @@ SHARED_LIB_VERSION=$SHARED_LIB_MAJOR_VERSION:0:0 # ndb version NDB_VERSION_MAJOR=5 NDB_VERSION_MINOR=0 -NDB_VERSION_BUILD=26 +NDB_VERSION_BUILD=27 NDB_VERSION_STATUS="" # Set all version vars based on $VERSION. How do we do this more elegant ?