From c583de95f8cf7d685e893ae4298c032cb1077078 Mon Sep 17 00:00:00 2001 From: "ramil@mysql.com" <> Date: Thu, 18 May 2006 17:10:58 +0500 Subject: [PATCH 01/29] Fix for bug #15558: truncate doesn't clear table on archive storage engine tables. --- mysql-test/r/archive.result | 2 ++ mysql-test/t/archive.test | 2 ++ sql/examples/ha_archive.cc | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/archive.result b/mysql-test/r/archive.result index 246e96bf993..2626b439059 100644 --- a/mysql-test/r/archive.result +++ b/mysql-test/r/archive.result @@ -2616,6 +2616,7 @@ select count(*) from t4; count(*) 1203 DELETE FROM t2; +ERROR HY000: Table storage engine for 't2' doesn't have this option SELECT * FROM t2; auto fld1 companynr fld3 fld4 fld5 fld6 1 000001 00 Omaha teethe neat @@ -5033,6 +5034,7 @@ auto fld1 companynr fld3 fld4 fld5 fld6 3 011402 37 Romans scholastics jarring 4 011403 37 intercepted audiology tinily TRUNCATE TABLE t2; +ERROR HY000: Table storage engine for 't2' doesn't have this option SELECT * FROM t2; auto fld1 companynr fld3 fld4 fld5 fld6 1 000001 00 Omaha teethe neat diff --git a/mysql-test/t/archive.test b/mysql-test/t/archive.test index f10ff0f648e..7b3ee45de5f 100644 --- a/mysql-test/t/archive.test +++ b/mysql-test/t/archive.test @@ -1316,12 +1316,14 @@ select count(*) from t4; # # For bug #12836 # Delete was allowing all rows to be removed +--error 1031 DELETE FROM t2; SELECT * FROM t2; INSERT INTO t2 VALUES (2,011401,37,'breaking','dreaded','Steinberg','W'); INSERT INTO t2 VALUES (3,011402,37,'Romans','scholastics','jarring',''); INSERT INTO t2 VALUES (4,011403,37,'intercepted','audiology','tinily',''); SELECT * FROM t2; +--error 1031 TRUNCATE TABLE t2; SELECT * FROM t2; diff --git a/sql/examples/ha_archive.cc b/sql/examples/ha_archive.cc index ee4cad25460..55fc359348f 100644 --- a/sql/examples/ha_archive.cc +++ b/sql/examples/ha_archive.cc @@ -1010,6 +1010,6 @@ ha_rows ha_archive::records_in_range(uint inx, key_range *min_key, int ha_archive::delete_all_rows() { DBUG_ENTER("ha_archive::delete_all_rows"); - DBUG_RETURN(0); + DBUG_RETURN(HA_ERR_WRONG_COMMAND); } #endif /* HAVE_ARCHIVE_DB */ From 40ea30253f2a5a1681d314d73a42a054f6a6a33d Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Fri, 26 May 2006 01:24:14 +0400 Subject: [PATCH 02/29] Fixed bug#16716: subselect in concat() may lead to a wrong result. The Item_func_concat::val_str() function tries to make as less re-allocations as possible. This results in appending strings returned by 2nd and next arguments to the string returned by 1st argument if the buffer for the first argument has enough free space. A constant subselect is evaluated only once and its result is stored in an Item_cache_str. In the case when the first argument of the concat() function is such a subselect Item_cache_str returns the stored value and Item_func_concat::val_str() append values of other arguments to it. But for the next row the value in the Item_cache_str isn't restored because the subselect is a constant one and it isn't evaluated second time. This results in appending string values of 2nd and next arguments to the result of the previous Item_func_concat::val_str() call. The Item_func_concat::val_str() function now checks whether the first argument is a constant one and if so it doesn't append values of 2nd and next arguments to the string value returned by it. --- mysql-test/r/func_concat.result | 7 +++++++ mysql-test/t/func_concat.test | 7 +++++++ sql/item_strfunc.cc | 5 ++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/func_concat.result b/mysql-test/r/func_concat.result index cf6fbf2da4f..94f1f640523 100644 --- a/mysql-test/r/func_concat.result +++ b/mysql-test/r/func_concat.result @@ -64,3 +64,10 @@ select 'a' union select concat('a', -0.0); a a good +select concat((select x from (select 'a' as x) as t1 ), +(select y from (select 'b' as y) as t2 )) from (select 1 union select 2 ) +as t3; +concat((select x from (select 'a' as x) as t1 ), +(select y from (select 'b' as y) as t2 )) +ab +ab diff --git a/mysql-test/t/func_concat.test b/mysql-test/t/func_concat.test index 69ce10c83c9..f546a25f647 100644 --- a/mysql-test/t/func_concat.test +++ b/mysql-test/t/func_concat.test @@ -50,4 +50,11 @@ select 'a' union select concat('a', -0); --replace_result 'a-0.0' good 'a0.0' good select 'a' union select concat('a', -0.0); +# +# Bug#16716: subselect in concat() may lead to a wrong result +# +select concat((select x from (select 'a' as x) as t1 ), + (select y from (select 'b' as y) as t2 )) from (select 1 union select 2 ) + as t3; + # End of 4.1 tests diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index e74d0100b55..0dfbf3b7a1d 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -252,11 +252,13 @@ String *Item_func_concat::val_str(String *str) DBUG_ASSERT(fixed == 1); String *res,*res2,*use_as_buff; uint i; + bool is_const= 0; null_value=0; if (!(res=args[0]->val_str(str))) goto null; use_as_buff= &tmp_value; + is_const= args[0]->const_item(); for (i=1 ; i < arg_count ; i++) { if (res->length() == 0) @@ -279,7 +281,7 @@ String *Item_func_concat::val_str(String *str) current_thd->variables.max_allowed_packet); goto null; } - if (res->alloced_length() >= res->length()+res2->length()) + if (!is_const && res->alloced_length() >= res->length()+res2->length()) { // Use old buffer res->append(*res2); } @@ -334,6 +336,7 @@ String *Item_func_concat::val_str(String *str) res= &tmp_value; use_as_buff=str; } + is_const= 0; } } res->set_charset(collation.collation); From 766b4a8c7ff930d8d1b3a9b361c40984b5469b06 Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Sun, 28 May 2006 22:01:38 +0400 Subject: [PATCH 03/29] Fixed bug#15351: Wrong collation used for comparison of md5() and sha() argument can lead to a wrong result. md5() and sha() functions treat their arguments as case sensitive strings. But when they are compared their arguments were compared as a case insensitive strings which leads to two functions with different arguments and thus different results to being identical. This can lead to a wrong decision made in the range optimizer and thus lead to a wrong result set. Item_func_md5::fix_length_and_dec() and Item_func_sha::fix_length_and_dec() functions now set binary collation on their arguments. --- mysql-test/r/func_str.result | 15 +++++++++++++++ mysql-test/t/func_str.test | 12 ++++++++++++ sql/item_strfunc.cc | 20 ++++++++++++++++++-- 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/func_str.result b/mysql-test/r/func_str.result index 0609624af18..24e6bb6f38a 100644 --- a/mysql-test/r/func_str.result +++ b/mysql-test/r/func_str.result @@ -1006,4 +1006,19 @@ NULL select ifnull(load_file("lkjlkj"),"it's null"); ifnull(load_file("lkjlkj"),"it's null") it's null +create table t1 (f1 varchar(4), f2 varchar(64), unique key k1 (f1,f2)); +insert into t1 values ( 'test',md5('test')), ('test', sha('test')); +select * from t1 where f1='test' and (f2= md5("test") or f2= md5("TEST")); +f1 f2 +test 098f6bcd4621d373cade4e832627b4f6 +select * from t1 where f1='test' and (f2= md5("TEST") or f2= md5("test")); +f1 f2 +test 098f6bcd4621d373cade4e832627b4f6 +select * from t1 where f1='test' and (f2= sha("test") or f2= sha("TEST")); +f1 f2 +test a94a8fe5ccb19ba61c4c0873d391e987982fbbd3 +select * from t1 where f1='test' and (f2= sha("TEST") or f2= sha("test")); +f1 f2 +test a94a8fe5ccb19ba61c4c0873d391e987982fbbd3 +drop table t1; End of 4.1 tests diff --git a/mysql-test/t/func_str.test b/mysql-test/t/func_str.test index c2f76dbac43..c36e15a08b9 100644 --- a/mysql-test/t/func_str.test +++ b/mysql-test/t/func_str.test @@ -669,4 +669,16 @@ drop table t1; select load_file("lkjlkj"); select ifnull(load_file("lkjlkj"),"it's null"); +# +# Bug#15351: Wrong collation used for comparison of md5() and sha() +# parameter can lead to a wrong result. +# +create table t1 (f1 varchar(4), f2 varchar(64), unique key k1 (f1,f2)); +insert into t1 values ( 'test',md5('test')), ('test', sha('test')); +select * from t1 where f1='test' and (f2= md5("test") or f2= md5("TEST")); +select * from t1 where f1='test' and (f2= md5("TEST") or f2= md5("test")); +select * from t1 where f1='test' and (f2= sha("test") or f2= sha("TEST")); +select * from t1 where f1='test' and (f2= sha("TEST") or f2= sha("test")); +drop table t1; + --echo End of 4.1 tests diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index e74d0100b55..e817edac6c0 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -118,7 +118,15 @@ String *Item_func_md5::val_str(String *str) void Item_func_md5::fix_length_and_dec() { - max_length=32; + max_length=32; + /* + The MD5() function treats its parameter as being a case sensitive. Thus + we set binary collation on it so different instances of MD5() will be + compared properly. + */ + args[0]->collation.set( + get_charset_by_csname(args[0]->collation.collation->csname, + MY_CS_BINSORT,MYF(0)), DERIVATION_COERCIBLE); } @@ -159,7 +167,15 @@ String *Item_func_sha::val_str(String *str) void Item_func_sha::fix_length_and_dec() { - max_length=SHA1_HASH_SIZE*2; // size of hex representation of hash + max_length=SHA1_HASH_SIZE*2; // size of hex representation of hash + /* + The SHA() function treats its parameter as being a case sensitive. Thus + we set binary collation on it so different instances of MD5() will be + compared properly. + */ + args[0]->collation.set( + get_charset_by_csname(args[0]->collation.collation->csname, + MY_CS_BINSORT,MYF(0)), DERIVATION_COERCIBLE); } From 1f30bf5a33fa4ace8e101a55f778728152bffa60 Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Mon, 29 May 2006 00:32:59 +0400 Subject: [PATCH 04/29] Fixed bug#19225: unchecked error results in server crash In multi-table delete a table for delete can't be used for selecting in subselects. Appropriate error was raised but wasn't checked which leads to a crash at the execution phase. The mysql_execute_command() now checks for errors before executing select for multi-delete. --- mysql-test/r/multi_update.result | 5 +++++ mysql-test/t/multi_update.test | 8 ++++++++ sql/sql_parse.cc | 4 ++-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/multi_update.result b/mysql-test/r/multi_update.result index dba10296063..ee62bd3bce6 100644 --- a/mysql-test/r/multi_update.result +++ b/mysql-test/r/multi_update.result @@ -475,3 +475,8 @@ aclid bigint, index idx_acl(aclid) insert into t2 values(1,null); delete t2, t1 from t2 left join t1 on (t2.aclid=t1.aclid) where t2.refid='1'; drop table t1, t2; +create table t1(a int); +create table t2(a int); +delete from t1,t2 using t1,t2 where t1.a=(select a from t1); +ERROR HY000: You can't specify target table 't1' for update in FROM clause +drop table t1, t2; diff --git a/mysql-test/t/multi_update.test b/mysql-test/t/multi_update.test index 27bdf4df3d7..e628e900c73 100644 --- a/mysql-test/t/multi_update.test +++ b/mysql-test/t/multi_update.test @@ -448,4 +448,12 @@ insert into t2 values(1,null); delete t2, t1 from t2 left join t1 on (t2.aclid=t1.aclid) where t2.refid='1'; drop table t1, t2; +# +# Bug#19225: unchecked error leads to server crash +# +create table t1(a int); +create table t2(a int); +--error 1093 +delete from t1,t2 using t1,t2 where t1.a=(select a from t1); +drop table t1, t2; # End of 4.1 tests diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 51ef3f31b26..06005f31198 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3047,8 +3047,8 @@ unsent_create_error: } } - if (!thd->is_fatal_error && (result= new multi_delete(thd,aux_tables, - table_count))) + if (!res && !thd->is_fatal_error && + (result= new multi_delete(thd,aux_tables, table_count))) { res= mysql_select(thd, &select_lex->ref_pointer_array, select_lex->get_table_list(), From 641f852de8fe25e66ca6360ca6567c2c5d8bc402 Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Tue, 30 May 2006 00:36:48 +0400 Subject: [PATCH 05/29] Fixed bug#18360: Incorrect type coercion in IN() results in false comparison The IN() function uses agg_cmp_type() to aggregate all types of its arguments to find out some common type for comparisons. In this particular case the char() and the int was aggregated to double because char() can contain values like '1.5'. But all strings which do not start from a digit are converted to 0. thus 'a' and 'z' become equal. This behaviour is reasonable when all function arguments are constants. But when there is a field or an expression this can lead to false comparisons. In this case it makes more sense to coerce constants to the type of the field argument. The agg_cmp_type() function now aggregates types of constant and non-constant items separately. If some non-constant items will be found then their aggregated type will be returned. Thus after the aggregation constants will be coerced to the aggregated type. --- mysql-test/r/func_in.result | 21 +++++++++++++++++++++ mysql-test/t/func_in.test | 12 ++++++++++++ sql/item_cmpfunc.cc | 37 +++++++++++++++++++++++++++++++++++-- 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/func_in.result b/mysql-test/r/func_in.result index 3cf2afc83d1..0632dddb87e 100644 --- a/mysql-test/r/func_in.result +++ b/mysql-test/r/func_in.result @@ -202,3 +202,24 @@ select count(*) from t1 where id not in (1,2); count(*) 1 drop table t1; +create table t1 (f1 char(1), f2 int); +insert into t1 values (1,0),('a',1),('z',2); +select f1 from t1 where f1 in (1,'z'); +f1 +1 +z +select f2 from t1 where f2 in (1,'z'); +f2 +0 +1 +select f1 from t1 where 'z' in (1,f1); +f1 +z +select * from t1 where 'z' in (f2,f1); +f1 f2 +z 2 +select * from t1 where 1 in (f2,f1); +f1 f2 +1 0 +a 1 +drop table t1; diff --git a/mysql-test/t/func_in.test b/mysql-test/t/func_in.test index 2ffe5a2d5f7..3481f2c7b11 100644 --- a/mysql-test/t/func_in.test +++ b/mysql-test/t/func_in.test @@ -109,4 +109,16 @@ select count(*) from t1 where id not in (1); select count(*) from t1 where id not in (1,2); drop table t1; +# +# Bug#18360 Incorrect type coercion in IN() results in false comparison +# +create table t1 (f1 char(1), f2 int); +insert into t1 values (1,0),('a',1),('z',2); +select f1 from t1 where f1 in (1,'z'); +select f2 from t1 where f2 in (1,'z'); +select f1 from t1 where 'z' in (1,f1); +select * from t1 where 'z' in (f2,f1); +select * from t1 where 1 in (f2,f1); +drop table t1; + # End of 4.1 tests diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 3c41fb56d89..bfbb3355004 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -58,12 +58,45 @@ static void agg_result_type(Item_result *type, Item **items, uint nitems) } } + +/* + Aggregates result types from the array of items. + + SYNOPSIS: + agg_cmp_type() + type [out] the aggregated type + items array of items to aggregate the type from + nitems number of items in the array + + DESCRIPTION + This function aggregates result types from the array of items. Found type + supposed to be used later for comparison of values of these items. + Aggregation itself is performed by the item_cmp_type() function. + + NOTES + Aggregation rules: + If all items are constants the type will be aggregated from all items. + If there are some non-constant items then only types of non-constant + items will be used for aggregation. +*/ static void agg_cmp_type(Item_result *type, Item **items, uint nitems) { uint i; type[0]= items[0]->result_type(); - for (i=1 ; i < nitems ; i++) - type[0]= item_cmp_type(type[0], items[i]->result_type()); + /* Reset to 0 on first occurence of non-const item. 1 otherwise */ + bool is_const= items[0]->const_item(); + + for (i= 1 ; i < nitems ; i++) + { + if (!items[i]->const_item()) + { + type[0]= is_const ? items[i]->result_type() : + item_cmp_type(type[0], items[i]->result_type()); + is_const= 0; + } + else if (is_const) + type[0]= item_cmp_type(type[0], items[i]->result_type()); + } } static void my_coll_agg_error(DTCollation &c1, DTCollation &c2, From fdc2b921cc529076de68d099b809cb1cb29dfa67 Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Tue, 30 May 2006 23:05:34 +0400 Subject: [PATCH 06/29] item_cmpfunc.cc, func_in.result, multi_update.result: After merge fix --- mysql-test/r/func_in.result | 8 ++++++++ mysql-test/r/multi_update.result | 1 + sql/item_cmpfunc.cc | 23 +++++++++++++++++++++++ 3 files changed, 32 insertions(+) diff --git a/mysql-test/r/func_in.result b/mysql-test/r/func_in.result index dd355c693d7..f758e2adfd9 100644 --- a/mysql-test/r/func_in.result +++ b/mysql-test/r/func_in.result @@ -212,12 +212,20 @@ select f2 from t1 where f2 in (1,'z'); f2 0 1 +Warnings: +Warning 1292 Truncated incorrect INTEGER value: 'z' select f1 from t1 where 'z' in (1,f1); f1 z select * from t1 where 'z' in (f2,f1); f1 f2 +1 0 +a 1 z 2 +Warnings: +Warning 1292 Truncated incorrect DOUBLE value: 'z' +Warning 1292 Truncated incorrect DOUBLE value: 'z' +Warning 1292 Truncated incorrect DOUBLE value: 'z' select * from t1 where 1 in (f2,f1); f1 f2 1 0 diff --git a/mysql-test/r/multi_update.result b/mysql-test/r/multi_update.result index 8d56cf6f2dc..b4a7aa5cb76 100644 --- a/mysql-test/r/multi_update.result +++ b/mysql-test/r/multi_update.result @@ -480,6 +480,7 @@ create table t1(a int); create table t2(a int); delete from t1,t2 using t1,t2 where t1.a=(select a from t1); ERROR HY000: You can't specify target table 't1' for update in FROM clause +drop table t1, t2; create table t1 ( c char(8) not null ) engine=innodb; insert into t1 values ('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9'); insert into t1 values ('A'),('B'),('C'),('D'),('E'),('F'); diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 1701af2325f..cbf77507b84 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -63,6 +63,27 @@ static void agg_result_type(Item_result *type, Item **items, uint nitems) } +/* + Aggregates result types from the array of items. + + SYNOPSIS: + agg_cmp_type() + type [out] the aggregated type + items array of items to aggregate the type from + nitems number of items in the array + + DESCRIPTION + This function aggregates result types from the array of items. Found type + supposed to be used later for comparison of values of these items. + Aggregation itself is performed by the item_cmp_type() function. + + NOTES + Aggregation rules: + If all items are constants the type will be aggregated from all items. + If there are some non-constant items then only types of non-constant + items will be used for aggregation. +*/ + static void agg_cmp_type(THD *thd, Item_result *type, Item **items, uint nitems) { uint i; @@ -89,6 +110,8 @@ static void agg_cmp_type(THD *thd, Item_result *type, Item **items, uint nitems) } else if (is_const) type[0]= item_cmp_type(type[0], items[i]->result_type()); + else if (field && convert_constant_item(thd, field, &items[i])) + type[0]= INT_RESULT; } } From b519877c9051fdec1ab355d56265d3a4a69f27ad Mon Sep 17 00:00:00 2001 From: "gkodinov@mysql.com" <> Date: Fri, 2 Jun 2006 12:04:03 +0300 Subject: [PATCH 07/29] Bug #4981: 4.x and 5.x produce non-optimal execution path, 3.23 regression test failure The member SEL_ARG::min_flag was not initialized, due to which the condition for no GEOM_FLAG in function key_or did not choose "Range checked for each record" as the correct access method. --- mysql-test/r/select.result | 14 ++++++++++++++ mysql-test/t/select.test | 14 ++++++++++++++ sql/opt_range.cc | 2 +- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index b80ca2b195e..5511a4bb66b 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -2714,3 +2714,17 @@ select * from t1 where f1 in (select f3 from t2 where (f3,f4)= (select f3,f4 fro f1 f2 1 1 drop table t1,t2; +CREATE TABLE t1(a int, b int, c int, KEY b(b), KEY c(c)); +insert into t1 values (1,0,0),(2,0,0); +CREATE TABLE t2 (a int, b varchar(2), c varchar(2), PRIMARY KEY(a)); +insert into t2 values (1,'',''), (2,'',''); +CREATE TABLE t3 (a int, b int, PRIMARY KEY (a,b), KEY a (a), KEY b (b)); +insert into t3 values (1,1),(1,2); +explain select straight_join DISTINCT t2.a,t2.b, t1.c from t1, t3, t2 +where (t1.c=t2.a or (t1.c=t3.a and t2.a=t3.b)) and t1.b=556476786 and +t2.b like '%%' order by t2.b limit 0,1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref b,c b 5 const 1 Using where; Using temporary; Using filesort +1 SIMPLE t3 index PRIMARY,a,b PRIMARY 8 NULL 2 Using index +1 SIMPLE t2 ALL PRIMARY NULL NULL NULL 2 Range checked for each record (index map: 0x1) +DROP TABLE t1,t2,t3; diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test index 996d5854854..3dcc16f1625 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -2248,4 +2248,18 @@ insert into t2 values(1,1); select * from t1 where f1 in (select f3 from t2 where (f3,f4)= (select f3,f4 from t2)); drop table t1,t2; +# +# Bug #4981: 4.x and 5.x produce non-optimal execution path, 3.23 regression test failure +# +CREATE TABLE t1(a int, b int, c int, KEY b(b), KEY c(c)); +insert into t1 values (1,0,0),(2,0,0); +CREATE TABLE t2 (a int, b varchar(2), c varchar(2), PRIMARY KEY(a)); +insert into t2 values (1,'',''), (2,'',''); +CREATE TABLE t3 (a int, b int, PRIMARY KEY (a,b), KEY a (a), KEY b (b)); +insert into t3 values (1,1),(1,2); +# must have "range checked" for t2 +explain select straight_join DISTINCT t2.a,t2.b, t1.c from t1, t3, t2 + where (t1.c=t2.a or (t1.c=t3.a and t2.a=t3.b)) and t1.b=556476786 and + t2.b like '%%' order by t2.b limit 0,1; +DROP TABLE t1,t2,t3; # End of 4.1 tests diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 71f937f90c6..67141aab6ce 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -64,7 +64,7 @@ public: uint8 min_flag, uint8 max_flag, uint8 maybe_flag); SEL_ARG(enum Type type_arg) :elements(1),use_count(1),left(0),next_key_part(0),color(BLACK), - type(type_arg) + type(type_arg),min_flag(0) {} inline bool is_same(SEL_ARG *arg) { From 3dad611cbc2f8dce136f09559930dbaeee656a3c Mon Sep 17 00:00:00 2001 From: "gkodinov@mysql.com" <> Date: Fri, 2 Jun 2006 17:22:21 +0300 Subject: [PATCH 08/29] bad merge fixed for b4981. --- mysql-test/r/select.result | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index ea8ae804ac3..bcf4c9c27fe 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -2716,6 +2716,20 @@ select * from t1 where f1 in (select f3 from t2 where (f3,f4)= (select f3,f4 fro f1 f2 1 1 drop table t1,t2; +CREATE TABLE t1(a int, b int, c int, KEY b(b), KEY c(c)); +insert into t1 values (1,0,0),(2,0,0); +CREATE TABLE t2 (a int, b varchar(2), c varchar(2), PRIMARY KEY(a)); +insert into t2 values (1,'',''), (2,'',''); +CREATE TABLE t3 (a int, b int, PRIMARY KEY (a,b), KEY a (a), KEY b (b)); +insert into t3 values (1,1),(1,2); +explain select straight_join DISTINCT t2.a,t2.b, t1.c from t1, t3, t2 +where (t1.c=t2.a or (t1.c=t3.a and t2.a=t3.b)) and t1.b=556476786 and +t2.b like '%%' order by t2.b limit 0,1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref b,c b 5 const 1 Using where; Using temporary; Using filesort +1 SIMPLE t3 index PRIMARY,a,b PRIMARY 8 NULL 2 Using index +1 SIMPLE t2 ALL PRIMARY NULL NULL NULL 2 Range checked for each record (index map: 0x1) +DROP TABLE t1,t2,t3; CREATE TABLE t1 ( city char(30) ); INSERT INTO t1 VALUES ('London'); INSERT INTO t1 VALUES ('Paris'); @@ -3446,17 +3460,3 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 range PRIMARY,b b 5 NULL 3 Using where 1 SIMPLE t2 ref c c 5 test.t1.a 2 Using where DROP TABLE t1, t2; -CREATE TABLE t1(a int, b int, c int, KEY b(b), KEY c(c)); -insert into t1 values (1,0,0),(2,0,0); -CREATE TABLE t2 (a int, b varchar(2), c varchar(2), PRIMARY KEY(a)); -insert into t2 values (1,'',''), (2,'',''); -CREATE TABLE t3 (a int, b int, PRIMARY KEY (a,b), KEY a (a), KEY b (b)); -insert into t3 values (1,1),(1,2); -explain select straight_join DISTINCT t2.a,t2.b, t1.c from t1, t3, t2 -where (t1.c=t2.a or (t1.c=t3.a and t2.a=t3.b)) and t1.b=556476786 and -t2.b like '%%' order by t2.b limit 0,1; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref b,c b 5 const 1 Using where; Using temporary; Using filesort -1 SIMPLE t3 index PRIMARY,a,b PRIMARY 8 NULL 2 Using index -1 SIMPLE t2 ALL PRIMARY NULL NULL NULL 2 Range checked for each record (index map: 0x1) -DROP TABLE t1,t2,t3; From 20c057cef3df3aff9cf3118cee74c2b433aa4a1a Mon Sep 17 00:00:00 2001 From: "gkodinov@mysql.com" <> Date: Fri, 2 Jun 2006 18:02:22 +0300 Subject: [PATCH 09/29] Removed duplicate tests from select.test --- mysql-test/r/select.result | 101 ------------------------------------- mysql-test/t/select.test | 94 ---------------------------------- 2 files changed, 195 deletions(-) diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index bcf4c9c27fe..4c1e64cc1cb 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -2656,16 +2656,6 @@ t11 MyISAM 10 Dynamic 0 0 X X X X X X X X latin1_swedish_ci NULL select 123 as a from t1 where f1 is null; a drop table t1,t11; -CREATE TABLE t1 (a INT, b INT); -(SELECT a, b AS c FROM t1) ORDER BY c+1; -a c -(SELECT a, b AS c FROM t1) ORDER BY b+1; -a c -SELECT a, b AS c FROM t1 ORDER BY c+1; -a c -SELECT a, b AS c FROM t1 ORDER BY b+1; -a c -drop table t1; CREATE TABLE t1 ( a INT NOT NULL, b INT NOT NULL, UNIQUE idx (a,b) ); INSERT INTO t1 VALUES (1,1),(1,2),(1,3),(1,4); CREATE TABLE t2 ( a INT NOT NULL, b INT NOT NULL, e INT ); @@ -2730,61 +2720,6 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t3 index PRIMARY,a,b PRIMARY 8 NULL 2 Using index 1 SIMPLE t2 ALL PRIMARY NULL NULL NULL 2 Range checked for each record (index map: 0x1) DROP TABLE t1,t2,t3; -CREATE TABLE t1 ( city char(30) ); -INSERT INTO t1 VALUES ('London'); -INSERT INTO t1 VALUES ('Paris'); -SELECT * FROM t1 WHERE city='London'; -city -London -SELECT * FROM t1 WHERE city='london'; -city -London -EXPLAIN SELECT * FROM t1 WHERE city='London' AND city='london'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using where -SELECT * FROM t1 WHERE city='London' AND city='london'; -city -London -EXPLAIN SELECT * FROM t1 WHERE city LIKE '%london%' AND city='London'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using where -SELECT * FROM t1 WHERE city LIKE '%london%' AND city='London'; -city -London -DROP TABLE t1; -create table t1 (a int(11) unsigned, b int(11) unsigned); -insert into t1 values (1,0), (1,1), (1,2); -select a-b from t1 order by 1; -a-b -0 -1 -18446744073709551615 -select a-b , (a-b < 0) from t1 order by 1; -a-b (a-b < 0) -0 0 -1 0 -18446744073709551615 0 -select a-b as d, (a-b >= 0), b from t1 group by b having d >= 0; -d (a-b >= 0) b -1 1 0 -0 1 1 -18446744073709551615 1 2 -select cast((a - b) as unsigned) from t1 order by 1; -cast((a - b) as unsigned) -0 -1 -18446744073709551615 -drop table t1; -create table t1 (a int(11)); -select all all * from t1; -a -select distinct distinct * from t1; -a -select all distinct * from t1; -ERROR HY000: Incorrect usage of ALL and DISTINCT -select distinct all * from t1; -ERROR HY000: Incorrect usage of ALL and DISTINCT -drop table t1; CREATE TABLE t1 ( K2C4 varchar(4) character set latin1 collate latin1_bin NOT NULL default '', K4N4 varchar(4) character set latin1 collate latin1_bin NOT NULL default '0000', @@ -2818,19 +2753,6 @@ WART 0100 1 WART 0200 1 WART 0300 3 DROP TABLE t1; -CREATE TABLE t1 ( a BLOB, INDEX (a(20)) ); -CREATE TABLE t2 ( a BLOB, INDEX (a(20)) ); -INSERT INTO t1 VALUES ('one'),('two'),('three'),('four'),('five'); -INSERT INTO t2 VALUES ('one'),('two'),('three'),('four'),('five'); -EXPLAIN SELECT * FROM t1 LEFT JOIN t2 USE INDEX (a) ON t1.a=t2.a; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 5 -1 SIMPLE t2 ref a a 23 test.t1.a 2 -EXPLAIN SELECT * FROM t1 LEFT JOIN t2 FORCE INDEX (a) ON t1.a=t2.a; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 5 -1 SIMPLE t2 ref a a 23 test.t1.a 2 -DROP TABLE t1, t2; create table t1 (a int, b int); create table t2 like t1; select t1.a from (t1 inner join t2 on t1.a=t2.a) where t2.a=1; @@ -2861,29 +2783,6 @@ x NULL 1.0000 drop table t1; -create table t1 (a int(11)); -select all all * from t1; -a -select distinct distinct * from t1; -a -select all distinct * from t1; -ERROR HY000: Incorrect usage of ALL and DISTINCT -select distinct all * from t1; -ERROR HY000: Incorrect usage of ALL and DISTINCT -drop table t1; -CREATE TABLE t1 ( a BLOB, INDEX (a(20)) ); -CREATE TABLE t2 ( a BLOB, INDEX (a(20)) ); -INSERT INTO t1 VALUES ('one'),('two'),('three'),('four'),('five'); -INSERT INTO t2 VALUES ('one'),('two'),('three'),('four'),('five'); -EXPLAIN SELECT * FROM t1 LEFT JOIN t2 USE INDEX (a) ON t1.a=t2.a; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 5 -1 SIMPLE t2 ref a a 23 test.t1.a 2 -EXPLAIN SELECT * FROM t1 LEFT JOIN t2 FORCE INDEX (a) ON t1.a=t2.a; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 5 -1 SIMPLE t2 ref a a 23 test.t1.a 2 -DROP TABLE t1, t2; CREATE TABLE t1 (a int); CREATE TABLE t2 (a int); INSERT INTO t1 VALUES (1), (2), (3), (4), (5); diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test index 6e7882b1047..4ed7c3d1de9 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -2217,15 +2217,6 @@ show table status like 't1%'; select 123 as a from t1 where f1 is null; drop table t1,t11; -# Bug 7672 Unknown column error in order clause -# -CREATE TABLE t1 (a INT, b INT); -(SELECT a, b AS c FROM t1) ORDER BY c+1; -(SELECT a, b AS c FROM t1) ORDER BY b+1; -SELECT a, b AS c FROM t1 ORDER BY c+1; -SELECT a, b AS c FROM t1 ORDER BY b+1; -drop table t1; - # # Bug #3874 (function in GROUP and LEFT JOIN) # @@ -2280,48 +2271,6 @@ explain select straight_join DISTINCT t2.a,t2.b, t1.c from t1, t3, t2 DROP TABLE t1,t2,t3; # End of 4.1 tests -# -# Test case for bug 7098: substitution of a constant for a string field -# - -CREATE TABLE t1 ( city char(30) ); -INSERT INTO t1 VALUES ('London'); -INSERT INTO t1 VALUES ('Paris'); - -SELECT * FROM t1 WHERE city='London'; -SELECT * FROM t1 WHERE city='london'; -EXPLAIN SELECT * FROM t1 WHERE city='London' AND city='london'; -SELECT * FROM t1 WHERE city='London' AND city='london'; -EXPLAIN SELECT * FROM t1 WHERE city LIKE '%london%' AND city='London'; -SELECT * FROM t1 WHERE city LIKE '%london%' AND city='London'; - -DROP TABLE t1; - -# -# Bug#7425 inconsistent sort order on unsigned columns result of substraction -# - -create table t1 (a int(11) unsigned, b int(11) unsigned); -insert into t1 values (1,0), (1,1), (1,2); -select a-b from t1 order by 1; -select a-b , (a-b < 0) from t1 order by 1; -select a-b as d, (a-b >= 0), b from t1 group by b having d >= 0; -select cast((a - b) as unsigned) from t1 order by 1; -drop table t1; - - -# -# Bug#8733 server accepts malformed query (multiply mentioned distinct) -# -create table t1 (a int(11)); -select all all * from t1; -select distinct distinct * from t1; ---error 1221 -select all distinct * from t1; ---error 1221 -select distinct all * from t1; -drop table t1; - # # Test for bug #6474 # @@ -2356,21 +2305,6 @@ SELECT K2C4, K4N4, F2I4 FROM t1 WHERE K2C4 = 'WART' AND (K2C4 = 'WART' OR K4N4 = '0200'); DROP TABLE t1; -# -# Test case for bug 7520: a wrong cost of the index for a BLOB field -# - -CREATE TABLE t1 ( a BLOB, INDEX (a(20)) ); -CREATE TABLE t2 ( a BLOB, INDEX (a(20)) ); - -INSERT INTO t1 VALUES ('one'),('two'),('three'),('four'),('five'); -INSERT INTO t2 VALUES ('one'),('two'),('three'),('four'),('five'); - -EXPLAIN SELECT * FROM t1 LEFT JOIN t2 USE INDEX (a) ON t1.a=t2.a; -EXPLAIN SELECT * FROM t1 LEFT JOIN t2 FORCE INDEX (a) ON t1.a=t2.a; - -DROP TABLE t1, t2; - # # Bug#8670 # @@ -2409,34 +2343,6 @@ select distinct avg(s1) as x from t1 group by s1 with rollup; drop table t1; -# -# Bug#8733 server accepts malformed query (multiply mentioned distinct) -# -create table t1 (a int(11)); -select all all * from t1; -select distinct distinct * from t1; ---error 1221 -select all distinct * from t1; ---error 1221 -select distinct all * from t1; -drop table t1; - - -# -# Test case for bug 7520: a wrong cost of the index for a BLOB field -# - -CREATE TABLE t1 ( a BLOB, INDEX (a(20)) ); -CREATE TABLE t2 ( a BLOB, INDEX (a(20)) ); - -INSERT INTO t1 VALUES ('one'),('two'),('three'),('four'),('five'); -INSERT INTO t2 VALUES ('one'),('two'),('three'),('four'),('five'); - -EXPLAIN SELECT * FROM t1 LEFT JOIN t2 USE INDEX (a) ON t1.a=t2.a; -EXPLAIN SELECT * FROM t1 LEFT JOIN t2 FORCE INDEX (a) ON t1.a=t2.a; - -DROP TABLE t1, t2; - # # Test for bug #10084: STRAIGHT_JOIN with ON expression # From 37e049db012b8e136dcc826bb2579f8af02201ab Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Fri, 2 Jun 2006 14:14:57 -0700 Subject: [PATCH 10/29] Fixed bug #18206. The bug report revealed two problems related to min/max optimization: 1. If the length of a constant key used in a SARGable condition for for the MIN/MAX fields is greater than the length of the field an unwanted warning on key truncation is issued; 2. If MIN/MAX optimization is applied to a partial index, like INDEX(b(4)) than can lead to returning a wrong result set. --- mysql-test/r/func_group.result | 24 ++++++++++++++++++++++++ mysql-test/t/func_group.test | 19 +++++++++++++++++++ sql/opt_sum.cc | 17 ++++++++++++++--- sql/sql_select.cc | 6 +++--- sql/sql_select.h | 2 +- 5 files changed, 61 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/func_group.result b/mysql-test/r/func_group.result index 67966b999d4..ffa68f279f3 100644 --- a/mysql-test/r/func_group.result +++ b/mysql-test/r/func_group.result @@ -916,3 +916,27 @@ select count(*), min(7), max(7) from t2m, t1i; count(*) min(7) max(7) 0 NULL NULL drop table t1m, t1i, t2m, t2i; +CREATE TABLE t1 (id int PRIMARY KEY, b char(3), INDEX(b)); +INSERT INTO t1 VALUES (1,'xx'), (2,'aa'); +SELECT * FROM t1; +id b +1 xx +2 aa +SELECT MAX(b) FROM t1 WHERE b < 'ppppp'; +MAX(b) +aa +SHOW WARNINGS; +Level Code Message +SELECT MAX(b) FROM t1 WHERE b < 'pp'; +MAX(b) +aa +DROP TABLE t1; +CREATE TABLE t1 (id int PRIMARY KEY, b char(16), INDEX(b(4))); +INSERT INTO t1 VALUES (1, 'xxxxbbbb'), (2, 'xxxxaaaa'); +SELECT MAX(b) FROM t1; +MAX(b) +xxxxbbbb +EXPLAIN SELECT MAX(b) FROM t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 +DROP TABLE t1; diff --git a/mysql-test/t/func_group.test b/mysql-test/t/func_group.test index dc647dbcfeb..f8a3ed0f25e 100644 --- a/mysql-test/t/func_group.test +++ b/mysql-test/t/func_group.test @@ -598,4 +598,23 @@ select count(*), min(7), max(7) from t2m, t1i; drop table t1m, t1i, t2m, t2i; +# +# Bug #18206: min/max optimization cannot be applied to partial index +# + +CREATE TABLE t1 (id int PRIMARY KEY, b char(3), INDEX(b)); +INSERT INTO t1 VALUES (1,'xx'), (2,'aa'); +SELECT * FROM t1; + +SELECT MAX(b) FROM t1 WHERE b < 'ppppp'; +SHOW WARNINGS; +SELECT MAX(b) FROM t1 WHERE b < 'pp'; +DROP TABLE t1; + +CREATE TABLE t1 (id int PRIMARY KEY, b char(16), INDEX(b(4))); +INSERT INTO t1 VALUES (1, 'xxxxbbbb'), (2, 'xxxxaaaa'); +SELECT MAX(b) FROM t1; +EXPLAIN SELECT MAX(b) FROM t1; +DROP TABLE t1; + # End of 4.1 tests diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index cfb5b3695a3..82211120c57 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -543,6 +543,10 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo, break; // Found a part od the key for the field } +#if 0 + if (part->length != (((Item_field*) args[0])->field)->field_length) + return 0; +#endif bool is_field_part= part == field_part; if (!(is_field_part || eq_type)) return 0; @@ -582,7 +586,8 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo, } else { - store_val_in_field(part->field, args[between && max_fl ? 2 : 1]); + store_val_in_field(part->field, args[between && max_fl ? 2 : 1], + CHECK_FIELD_IGNORE); if (part->null_bit) *key_ptr++= (byte) test(part->field->is_null()); part->field->get_key_image((char*) key_ptr, part->length, @@ -638,6 +643,8 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo, field BETWEEN const1 AND const2 3. all references to the columns from the same table as column field occur only in conjucts mentioned above. + 4. each of k first components the index is not partial, i.e. is not + defined on a fixed length proper prefix of the field. If such an index exists the function through the ref parameter returns the key value to find max/min for the field using the index, @@ -647,8 +654,8 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo, of the whole search key) NOTE - This function may set table->key_read to 1, which must be reset after - index is used! (This can only happen when function returns 1) + This function may set table->key_read to 1, which must be reset after + index is used! (This can only happen when function returns 1) RETURN 0 Index can not be used to optimize MIN(field)/MAX(field) @@ -682,6 +689,10 @@ static bool find_key_for_maxmin(bool max_fl, TABLE_REF *ref, if (!(table->file->index_flags(idx, jdx, 0) & HA_READ_ORDER)) return 0; + /* Check whether the index component is partial */ + if (part->length < table->field[part->fieldnr-1]->pack_length()) + break; + if (field->eq(part->field)) { ref->key= idx; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 57fb9738612..5a7e9e52aed 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -3434,7 +3434,7 @@ get_store_key(THD *thd, KEYUSE *keyuse, table_map used_tables, */ bool -store_val_in_field(Field *field,Item *item) +store_val_in_field(Field *field, Item *item, enum_check_fields check_flag) { bool error; THD *thd=current_thd; @@ -3445,7 +3445,7 @@ store_val_in_field(Field *field,Item *item) with select_insert, which make count_cuted_fields= 1 */ enum_check_fields old_count_cuted_fields= thd->count_cuted_fields; - thd->count_cuted_fields= CHECK_FIELD_WARN; + thd->count_cuted_fields= check_flag; error= item->save_in_field(field, 1); thd->count_cuted_fields= old_count_cuted_fields; return error || cuted_fields != thd->cuted_fields; @@ -7097,7 +7097,7 @@ static bool test_if_ref(Item_field *left_item,Item *right_item) field->real_type() != FIELD_TYPE_VAR_STRING && (field->type() != FIELD_TYPE_FLOAT || field->decimals() == 0)) { - return !store_val_in_field(field,right_item); + return !store_val_in_field(field, right_item, CHECK_FIELD_WARN); } } } diff --git a/sql/sql_select.h b/sql/sql_select.h index 94cc8839466..75cd0b4d797 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -335,7 +335,7 @@ extern const char *join_type_str[]; void TEST_join(JOIN *join); /* Extern functions in sql_select.cc */ -bool store_val_in_field(Field *field,Item *val); +bool store_val_in_field(Field *field, Item *val, enum_check_fields check_flag); TABLE *create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, ORDER *group, bool distinct, bool save_sum_fields, ulong select_options, ha_rows rows_limit, From f1afd17821be166fbcc0f33759ae43b0f43a949c Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Fri, 2 Jun 2006 19:15:32 -0700 Subject: [PATCH 11/29] Cleanup for the fix of bug 18206. --- sql/opt_sum.cc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 82211120c57..97e271121d3 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -543,10 +543,6 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo, break; // Found a part od the key for the field } -#if 0 - if (part->length != (((Item_field*) args[0])->field)->field_length) - return 0; -#endif bool is_field_part= part == field_part; if (!(is_field_part || eq_type)) return 0; From d027dd7c0d9d2d105e712e6ff89a4483a7dfc177 Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Wed, 7 Jun 2006 01:10:23 +0400 Subject: [PATCH 12/29] Fixed bug#15962: CONCAT() in UNION may lead to a data trucation. To calculate its max_length the CONCAT() function is simply sums max_lengths of its arguments but when the collation of an argument differs from the collation of the CONCAT() max_length will be wrong. This may lead to a data truncation when a tmp table is used, in UNIONS for example. The Item_func_concat::fix_length_and_dec() function now recalculates the max_length of an argument when the mbmaxlen of the argument differs from the mbmaxlen of the CONCAT(). --- mysql-test/r/func_concat.result | 7 +++++++ mysql-test/t/func_concat.test | 8 ++++++++ sql/item_strfunc.cc | 9 ++++++++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/func_concat.result b/mysql-test/r/func_concat.result index 0bd53b32dd7..712f0c0495e 100644 --- a/mysql-test/r/func_concat.result +++ b/mysql-test/r/func_concat.result @@ -68,3 +68,10 @@ select 'a' union select concat('a', -0.0000); a a a0.0000 +create table t1(f1 varchar(6)) charset=utf8; +insert into t1 values ("123456"); +select concat(f1, 2) a from t1 union select 'x' a from t1; +a +1234562 +x +drop table t1; diff --git a/mysql-test/t/func_concat.test b/mysql-test/t/func_concat.test index 37fc0e105b8..42201961222 100644 --- a/mysql-test/t/func_concat.test +++ b/mysql-test/t/func_concat.test @@ -53,3 +53,11 @@ select 'a' union select concat('a', -0.0); select 'a' union select concat('a', -0.0000); # End of 4.1 tests + +# +# Bug#15962: CONCAT() in UNION may lead to a data trucation. +# +create table t1(f1 varchar(6)) charset=utf8; +insert into t1 values ("123456"); +select concat(f1, 2) a from t1 union select 'x' a from t1; +drop table t1; diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index ce9897afeed..d8082440f1a 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -389,7 +389,14 @@ void Item_func_concat::fix_length_and_dec() return; for (uint i=0 ; i < arg_count ; i++) - max_result_length+= args[i]->max_length; + { + if (args[i]->collation.collation->mbmaxlen != collation.collation->mbmaxlen) + max_result_length+= (args[i]->max_length / + args[i]->collation.collation->mbmaxlen) * + collation.collation->mbmaxlen; + else + max_result_length+= args[i]->max_length; + } if (max_result_length >= MAX_BLOB_WIDTH) { From 0bdae38efbf4696739e116c8fcc1f876b3ecc7fc Mon Sep 17 00:00:00 2001 From: "ramil@mysql.com" <> Date: Wed, 7 Jun 2006 14:01:10 +0500 Subject: [PATCH 13/29] Fix for bug #6880: LAST_INSERT_ID() within a statement --- mysql-test/r/auto_increment.result | 22 ++++++++++++++++++++++ mysql-test/r/rpl_log.result | 17 +++++++++++++++++ mysql-test/t/auto_increment.test | 17 +++++++++++++++++ mysql-test/t/rpl_log.test | 14 ++++++++++++++ sql/item_func.cc | 7 ++++--- 5 files changed, 74 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/auto_increment.result b/mysql-test/r/auto_increment.result index 426f20212c3..c1b7b753bd4 100644 --- a/mysql-test/r/auto_increment.result +++ b/mysql-test/r/auto_increment.result @@ -378,4 +378,26 @@ t1 CREATE TABLE `t1` ( KEY `t1_name` (`t1_name`) ) ENGINE=MyISAM AUTO_INCREMENT=1003 DEFAULT CHARSET=latin1 DROP TABLE `t1`; +create table t1(a int not null auto_increment primary key); +create table t2(a int not null auto_increment primary key, t1a int); +insert into t1 values(NULL); +insert into t2 values (NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID()); +insert into t1 values (NULL); +insert into t2 values (NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID()), +(NULL, LAST_INSERT_ID()); +insert into t1 values (NULL); +insert into t2 values (NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID()), +(NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID()); +select * from t2; +a t1a +1 1 +2 1 +3 2 +4 2 +5 2 +6 3 +7 3 +8 3 +9 3 +drop table t1, t2; End of 4.1 tests diff --git a/mysql-test/r/rpl_log.result b/mysql-test/r/rpl_log.result index 9fcab2a7cbe..3833800bfeb 100644 --- a/mysql-test/r/rpl_log.result +++ b/mysql-test/r/rpl_log.result @@ -99,3 +99,20 @@ Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File # 127.0.0.1 root MASTER_PORT 1 master-bin.000002 276 slave-relay-bin.000003 214 master-bin.000002 Yes Yes 0 0 276 214 None 0 No # show binlog events in 'slave-bin.000005' from 4; ERROR HY000: Error when executing command SHOW BINLOG EVENTS: Could not find target log +create table t1(a int auto_increment primary key, b int); +insert into t1 values (NULL, 1); +reset master; +set insert_id=5; +insert into t1 values (NULL, last_insert_id()), (NULL, last_insert_id()); +show binlog events; +Log_name Pos Event_type Server_id Orig_log_pos Info +slave-bin.000001 4 Start 2 4 Server ver: VERSION, Binlog ver: 3 +slave-bin.000001 79 Intvar 2 79 LAST_INSERT_ID=1 +slave-bin.000001 107 Intvar 2 107 INSERT_ID=5 +slave-bin.000001 135 Query 2 135 use `test`; insert into t1 values (NULL, last_insert_id()), (NULL, last_insert_id()) +select * from t1; +a b +1 1 +5 1 +6 1 +drop table t1; diff --git a/mysql-test/t/auto_increment.test b/mysql-test/t/auto_increment.test index eed2ea44d05..37b92b32bfb 100644 --- a/mysql-test/t/auto_increment.test +++ b/mysql-test/t/auto_increment.test @@ -238,4 +238,21 @@ SHOW CREATE TABLE `t1`; DROP TABLE `t1`; +# +# Bug #6880: LAST_INSERT_ID() within a statement +# + +create table t1(a int not null auto_increment primary key); +create table t2(a int not null auto_increment primary key, t1a int); +insert into t1 values(NULL); +insert into t2 values (NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID()); +insert into t1 values (NULL); +insert into t2 values (NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID()), +(NULL, LAST_INSERT_ID()); +insert into t1 values (NULL); +insert into t2 values (NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID()), +(NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID()); +select * from t2; +drop table t1, t2; + --echo End of 4.1 tests diff --git a/mysql-test/t/rpl_log.test b/mysql-test/t/rpl_log.test index 7a67ab2311a..08aa3b28850 100644 --- a/mysql-test/t/rpl_log.test +++ b/mysql-test/t/rpl_log.test @@ -108,4 +108,18 @@ show slave status; --error 1220 show binlog events in 'slave-bin.000005' from 4; +# +# Bug #6880: LAST_INSERT_ID() within a statement +# + +create table t1(a int auto_increment primary key, b int); +insert into t1 values (NULL, 1); +reset master; +set insert_id=5; +insert into t1 values (NULL, last_insert_id()), (NULL, last_insert_id()); +--replace_result $VERSION VERSION +show binlog events; +select * from t1; +drop table t1; + # End of 4.1 tests diff --git a/sql/item_func.cc b/sql/item_func.cc index 66300d129d4..91ccef6511f 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2233,15 +2233,16 @@ longlong Item_func_release_lock::val_int() longlong Item_func_last_insert_id::val_int() { DBUG_ASSERT(fixed == 1); + THD* thd= current_thd; if (arg_count) { longlong value=args[0]->val_int(); - current_thd->insert_id(value); + thd->insert_id(value); null_value=args[0]->null_value; } else - current_thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - return current_thd->insert_id(); + thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + return thd->last_insert_id_used ? thd->current_insert_id : thd->insert_id(); } /* This function is just used to test speed of different functions */ From 974362875880ca324063cd37a0a532033c46d617 Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Wed, 7 Jun 2006 16:17:56 +0400 Subject: [PATCH 14/29] Fixed bug#19789: REPLACE was allowed for a VIEW with CHECK OPTION enabled. The st_lex::which_check_option_applicable() function controls for which statements WITH CHECK OPTION clause should be taken into account. REPLACE and REPLACE_SELECT wasn't in the list which results in allowing REPLACE to insert wrong rows in a such view. The st_lex::which_check_option_applicable() now includes REPLACE and REPLACE_SELECT in the list of statements for which WITH CHECK OPTION clause is applicable. --- mysql-test/r/replace.result | 6 ++++++ mysql-test/t/replace.test | 10 ++++++++++ sql/sql_lex.h | 2 ++ 3 files changed, 18 insertions(+) diff --git a/mysql-test/r/replace.result b/mysql-test/r/replace.result index a7d59fcfa62..5a5e4571ba9 100644 --- a/mysql-test/r/replace.result +++ b/mysql-test/r/replace.result @@ -24,3 +24,9 @@ a b 63 default_value 127 last drop table t1; +CREATE TABLE t1 (f1 INT); +CREATE VIEW v1 AS SELECT f1 FROM t1 WHERE f1 = 0 WITH CHECK OPTION; +REPLACE INTO v1 (f1) VALUES (1); +ERROR HY000: CHECK OPTION failed 'test.v1' +DROP TABLE t1; +DROP VIEW v1; diff --git a/mysql-test/t/replace.test b/mysql-test/t/replace.test index 10703eaafb8..269854fb180 100644 --- a/mysql-test/t/replace.test +++ b/mysql-test/t/replace.test @@ -35,3 +35,13 @@ select * from t1; drop table t1; # End of 4.1 tests + +# +# Bug#19789: REPLACE was allowed for a VIEW with CHECK OPTION enabled. +# +CREATE TABLE t1 (f1 INT); +CREATE VIEW v1 AS SELECT f1 FROM t1 WHERE f1 = 0 WITH CHECK OPTION; +--error 1369 +REPLACE INTO v1 (f1) VALUES (1); +DROP TABLE t1; +DROP VIEW v1; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 34e7ee969b6..306a726cab9 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1000,6 +1000,8 @@ typedef struct st_lex case SQLCOM_UPDATE_MULTI: case SQLCOM_INSERT: case SQLCOM_INSERT_SELECT: + case SQLCOM_REPLACE: + case SQLCOM_REPLACE_SELECT: case SQLCOM_LOAD: return TRUE; default: From 395affb8e95c873208d49310f5b46e65b98f0d6a Mon Sep 17 00:00:00 2001 From: "gkodinov@mysql.com" <> Date: Thu, 8 Jun 2006 13:34:03 +0300 Subject: [PATCH 15/29] Bug #15355: Common natural join column not resolved in prepared statement nested query Problem: There was a wrong context assigned to the columns that were added in insert_fields() when expanding a '*'. When this is done in a prepared statement it causes fix_fields() to fail to find the table that these columns reference. Actually the right context is set in setup_natural_join_row_types() called at the end of setup_tables(). However when executed in a context of a prepared statement setup_tables() resets the context, but setup_natural_join_row_types() was not setting it to the correct value assuming it has already done so. Solution: The top-most, left-most NATURAL/USING join must be set as a first_name_resolution_table in context even when operating on prepared statements. --- mysql-test/r/join.result | 20 ++++++++++++++++++++ mysql-test/t/join.test | 25 +++++++++++++++++++++++++ sql/sql_base.cc | 22 +++++++++++----------- 3 files changed, 56 insertions(+), 11 deletions(-) diff --git a/mysql-test/r/join.result b/mysql-test/r/join.result index 86288caf398..48b7730481f 100644 --- a/mysql-test/r/join.result +++ b/mysql-test/r/join.result @@ -744,3 +744,23 @@ select a2 from ((t1 natural join t2) join t3 on b=c1) natural join t4; a2 1 drop table t1,t2,t3,t4; +create table t1 (c int, b int); +create table t2 (a int, b int); +create table t3 (b int, c int); +create table t4 (y int, c int); +create table t5 (y int, z int); +insert into t1 values (3,2); +insert into t2 values (1,2); +insert into t3 values (2,3); +insert into t4 values (1,3); +insert into t5 values (1,4); +prepare stmt1 from "select * from ((t3 natural join (t1 natural join t2)) +natural join t4) natural join t5"; +execute stmt1; +y c b a z +1 3 2 1 4 +select * from ((t3 natural join (t1 natural join t2)) natural join t4) +natural join t5; +y c b a z +1 3 2 1 4 +drop table t1, t2, t3, t4, t5; diff --git a/mysql-test/t/join.test b/mysql-test/t/join.test index f6a57d5e230..27558a31d68 100644 --- a/mysql-test/t/join.test +++ b/mysql-test/t/join.test @@ -563,4 +563,29 @@ select a2 from ((t1 natural join t2) join t3 on b=c1) natural join t4; drop table t1,t2,t3,t4; +# +# BUG#15355: Common natural join column not resolved in prepared statement nested query +# +create table t1 (c int, b int); +create table t2 (a int, b int); +create table t3 (b int, c int); +create table t4 (y int, c int); +create table t5 (y int, z int); + +insert into t1 values (3,2); +insert into t2 values (1,2); +insert into t3 values (2,3); +insert into t4 values (1,3); +insert into t5 values (1,4); + +-- this fails +prepare stmt1 from "select * from ((t3 natural join (t1 natural join t2)) +natural join t4) natural join t5"; +execute stmt1; + +-- this works +select * from ((t3 natural join (t1 natural join t2)) natural join t4) + natural join t5; +drop table t1, t2, t3, t4, t5; + # End of tests for WL#2486 - natural/using join diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 75b69c91846..7fe626c8f2d 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -4184,10 +4184,6 @@ static bool setup_natural_join_row_types(THD *thd, if (from_clause->elements == 0) return FALSE; /* We come here in the case of UNIONs. */ - /* For stored procedures do not redo work if already done. */ - if (!context->select_lex->first_execution) - return FALSE; - List_iterator_fast table_ref_it(*from_clause); TABLE_LIST *table_ref; /* Current table reference. */ /* Table reference to the left of the current. */ @@ -4200,14 +4196,18 @@ static bool setup_natural_join_row_types(THD *thd, { table_ref= left_neighbor; left_neighbor= table_ref_it++; - if (store_top_level_join_columns(thd, table_ref, - left_neighbor, right_neighbor)) - return TRUE; - if (left_neighbor) + /* For stored procedures do not redo work if already done. */ + if (context->select_lex->first_execution) { - TABLE_LIST *first_leaf_on_the_right; - first_leaf_on_the_right= table_ref->first_leaf_for_name_resolution(); - left_neighbor->next_name_resolution_table= first_leaf_on_the_right; + if (store_top_level_join_columns(thd, table_ref, + left_neighbor, right_neighbor)) + return TRUE; + if (left_neighbor) + { + TABLE_LIST *first_leaf_on_the_right; + first_leaf_on_the_right= table_ref->first_leaf_for_name_resolution(); + left_neighbor->next_name_resolution_table= first_leaf_on_the_right; + } } right_neighbor= table_ref; } From 47bb56997910d9c580e5b49c8db92489380fd4bf Mon Sep 17 00:00:00 2001 From: "mskold@mysql.com" <> Date: Mon, 12 Jun 2006 15:35:47 +0200 Subject: [PATCH 16/29] Added order by --- mysql-test/r/ndb_lock.result | 8 ++++---- mysql-test/t/ndb_lock.test | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/ndb_lock.result b/mysql-test/r/ndb_lock.result index 281b02b8228..875dcd775e6 100644 --- a/mysql-test/r/ndb_lock.result +++ b/mysql-test/r/ndb_lock.result @@ -78,10 +78,10 @@ ERROR HY000: Lock wait timeout exceeded; try restarting transaction rollback; commit; begin; -select * from t1 where y = 'one' or y = 'three' for update; +select * from t1 where y = 'one' or y = 'three' order by x for update; x y z -3 three 3 1 one 1 +3 three 3 begin; select * from t1 where x = 2 for update; x y z @@ -118,10 +118,10 @@ ERROR HY000: Lock wait timeout exceeded; try restarting transaction rollback; commit; begin; -select * from t1 where y = 'one' or y = 'three' lock in share mode; +select * from t1 where y = 'one' or y = 'three' order by x lock in share mode; x y z -3 three 3 1 one 1 +3 three 3 begin; select * from t1 where y = 'one' lock in share mode; x y z diff --git a/mysql-test/t/ndb_lock.test b/mysql-test/t/ndb_lock.test index db42b7ec2dd..e8f7c57ac96 100644 --- a/mysql-test/t/ndb_lock.test +++ b/mysql-test/t/ndb_lock.test @@ -93,7 +93,7 @@ commit; # table scan connection con1; begin; -select * from t1 where y = 'one' or y = 'three' for update; +select * from t1 where y = 'one' or y = 'three' order by x for update; connection con2; begin; @@ -145,7 +145,7 @@ commit; # table scan connection con1; begin; -select * from t1 where y = 'one' or y = 'three' lock in share mode; +select * from t1 where y = 'one' or y = 'three' order by x lock in share mode; connection con2; begin; From 67de8c46a5ddbc13ef32a339cb8c3c3e5dcb77bd Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Tue, 13 Jun 2006 19:09:24 +0400 Subject: [PATCH 17/29] Fixed bug#16377: result of DATE/TIME functions were compared as strings which can lead to a wrong result. All date/time functions has the STRING result type thus their results are compared as strings. The string date representation allows a user to skip some of leading zeros. This can lead to wrong comparison result if a date/time function result is compared to such a string constant. The idea behind this bug fix is to compare results of date/time functions and data/time constants as ints, because that date/time representation is more exact. To achieve this the agg_cmp_type() is changed to take in the account that a date/time field or an date/time item should be compared as ints. This bug fix is partially back ported from 5.0. The agg_cmp_type() function now accepts THD as one of parameters. In addition, it now checks if a date/time field/function is present in the list. If so, it tries to coerce all constants to INT to make date/time comparison return correct result. The field for the constant coercion is taken from the Item_field or constructed from the Item_func. In latter case the constructed field will be freed after conversion of all constant items. Otherwise the result is same as before - aggregated with help of the item_cmp_type() function. From the Item_func_between::fix_length_and_dec() function removed the part which was converting date/time constants to int if possible. Now this is done by the agg_cmp_type() function. The new function result_as_longlong() is added to the Item class. It indicates that the item is a date/time item and result of it can be compared as int. Such items are date/time fields/functions. Correct val_int() methods are implemented for classes Item_date_typecast, Item_func_makedate, Item_time_typecast, Item_datetime_typecast. All these classes are derived from Item_str_func and Item_str_func::val_int() converts its string value to int without regard to the date/time type of these items. Arg_comparator::set_compare_func() and Arg_comparator::set_cmp_func() functions are changed to substitute result type of an item with the INT_RESULT if the item is a date/time item and another item is a constant. This is done to get a correct result of comparisons like date_time_function() = string_constant. --- mysql-test/r/cast.result | 2 +- mysql-test/r/func_time.result | 41 +++++++++ mysql-test/t/func_time.test | 20 +++++ sql/field.cc | 16 ++-- sql/item.h | 16 ++++ sql/item_cmpfunc.cc | 154 +++++++++++++++++++++++++++------- sql/item_cmpfunc.h | 14 +++- sql/item_timefunc.cc | 60 +++++++++++++ sql/item_timefunc.h | 12 +++ 9 files changed, 293 insertions(+), 42 deletions(-) diff --git a/mysql-test/r/cast.result b/mysql-test/r/cast.result index 68687670e17..2538fbfd61d 100644 --- a/mysql-test/r/cast.result +++ b/mysql-test/r/cast.result @@ -192,7 +192,7 @@ cast("2001-1-1" as datetime) = "2001-01-01 00:00:00" 1 select cast("1:2:3" as TIME) = "1:02:03"; cast("1:2:3" as TIME) = "1:02:03" -0 +1 select cast(NULL as DATE); cast(NULL as DATE) NULL diff --git a/mysql-test/r/func_time.result b/mysql-test/r/func_time.result index 02f3d2f7273..93d4542bf5b 100644 --- a/mysql-test/r/func_time.result +++ b/mysql-test/r/func_time.result @@ -630,3 +630,44 @@ select monthname(str_to_date(null, '%m')), monthname(str_to_date(null, '%m')), monthname(str_to_date(1, '%m')), monthname(str_to_date(0, '%m')); monthname(str_to_date(null, '%m')) monthname(str_to_date(null, '%m')) monthname(str_to_date(1, '%m')) monthname(str_to_date(0, '%m')) NULL NULL January NULL +create table t1(f1 date, f2 time, f3 datetime); +insert into t1 values ("2006-01-01", "12:01:01", "2006-01-01 12:01:01"); +insert into t1 values ("2006-01-02", "12:01:02", "2006-01-02 12:01:02"); +select f1 from t1 where f1 between "2006-1-1" and 20060101; +f1 +2006-01-01 +select f1 from t1 where f1 between "2006-1-1" and "2006.1.1"; +f1 +2006-01-01 +select f1 from t1 where date(f1) between "2006-1-1" and "2006.1.1"; +f1 +2006-01-01 +select f2 from t1 where f2 between "12:1:2" and "12:2:2"; +f2 +12:01:02 +select f2 from t1 where time(f2) between "12:1:2" and "12:2:2"; +f2 +12:01:02 +select f3 from t1 where f3 between "2006-1-1 12:1:1" and "2006-1-1 12:1:2"; +f3 +2006-01-01 12:01:01 +select f3 from t1 where timestamp(f3) between "2006-1-1 12:1:1" and "2006-1-1 12:1:2"; +f3 +2006-01-01 12:01:01 +select f1 from t1 where "2006-1-1" between f1 and f3; +f1 +2006-01-01 +select f1 from t1 where "2006-1-1" between date(f1) and date(f3); +f1 +2006-01-01 +select f1 from t1 where "2006-1-1" between f1 and 'zzz'; +f1 +Warnings: +Warning 1292 Truncated incorrect date value: 'zzz' +select f1 from t1 where makedate(2006,1) between date(f1) and date(f3); +f1 +2006-01-01 +select f1 from t1 where makedate(2006,2) between date(f1) and date(f3); +f1 +2006-01-02 +drop table t1; diff --git a/mysql-test/t/func_time.test b/mysql-test/t/func_time.test index 01e4e47d318..5e0b35522cf 100644 --- a/mysql-test/t/func_time.test +++ b/mysql-test/t/func_time.test @@ -322,4 +322,24 @@ select last_day('2005-01-00'); select monthname(str_to_date(null, '%m')), monthname(str_to_date(null, '%m')), monthname(str_to_date(1, '%m')), monthname(str_to_date(0, '%m')); +# +# Bug#16377 result of DATE/TIME functions were compared as strings which +# can lead to a wrong result. +# +create table t1(f1 date, f2 time, f3 datetime); +insert into t1 values ("2006-01-01", "12:01:01", "2006-01-01 12:01:01"); +insert into t1 values ("2006-01-02", "12:01:02", "2006-01-02 12:01:02"); +select f1 from t1 where f1 between "2006-1-1" and 20060101; +select f1 from t1 where f1 between "2006-1-1" and "2006.1.1"; +select f1 from t1 where date(f1) between "2006-1-1" and "2006.1.1"; +select f2 from t1 where f2 between "12:1:2" and "12:2:2"; +select f2 from t1 where time(f2) between "12:1:2" and "12:2:2"; +select f3 from t1 where f3 between "2006-1-1 12:1:1" and "2006-1-1 12:1:2"; +select f3 from t1 where timestamp(f3) between "2006-1-1 12:1:1" and "2006-1-1 12:1:2"; +select f1 from t1 where "2006-1-1" between f1 and f3; +select f1 from t1 where "2006-1-1" between date(f1) and date(f3); +select f1 from t1 where "2006-1-1" between f1 and 'zzz'; +select f1 from t1 where makedate(2006,1) between date(f1) and date(f3); +select f1 from t1 where makedate(2006,2) between date(f1) and date(f3); +drop table t1; # End of 4.1 tests diff --git a/sql/field.cc b/sql/field.cc index a64eaad7308..ec4d4b4e4f5 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -6841,7 +6841,11 @@ create_field::create_field(Field *old_field,Field *orig_field) bool Field::set_warning(const uint level, const uint code, int cuted_increment) { - THD *thd= table->in_use; + /* + If this field was created only for type conversion purposes it + will have table == NULL. + */ + THD *thd= table ? table->in_use : current_thd; if (thd->count_cuted_fields) { thd->cuted_fields+= cuted_increment; @@ -6876,7 +6880,8 @@ Field::set_datetime_warning(const uint level, const uint code, timestamp_type ts_type, int cuted_increment) { if (set_warning(level, code, cuted_increment)) - make_truncated_value_warning(table->in_use, str, str_length, ts_type); + make_truncated_value_warning(table ? table->in_use : current_thd, + str, str_length, ts_type); } @@ -6905,8 +6910,8 @@ Field::set_datetime_warning(const uint level, const uint code, { char str_nr[22]; char *str_end= longlong10_to_str(nr, str_nr, -10); - make_truncated_value_warning(table->in_use, str_nr, str_end - str_nr, - ts_type); + make_truncated_value_warning(table ? table->in_use : current_thd, + str_nr, str_end - str_nr, ts_type); } } @@ -6935,7 +6940,8 @@ Field::set_datetime_warning(const uint level, const uint code, /* DBL_DIG is enough to print '-[digits].E+###' */ char str_nr[DBL_DIG + 8]; uint str_len= my_sprintf(str_nr, (str_nr, "%g", nr)); - make_truncated_value_warning(table->in_use, str_nr, str_len, ts_type); + make_truncated_value_warning(table ? table->in_use : current_thd, + str_nr, str_len, ts_type); } } diff --git a/sql/item.h b/sql/item.h index 66da7ad4e74..eca8ee184a1 100644 --- a/sql/item.h +++ b/sql/item.h @@ -327,6 +327,14 @@ public: cleanup(); delete this; } + /* + result_as_longlong() must return TRUE for Items representing DATE/TIME + functions and DATE/TIME table fields. + Those Items have result_type()==STRING_RESULT (and not INT_RESULT), but + their values should be compared as integers (because the integer + representation is more precise than the string one). + */ + virtual bool result_as_longlong() { return FALSE; } }; @@ -450,6 +458,10 @@ public: Item *get_tmp_table_item(THD *thd); void cleanup(); inline uint32 max_disp_length() { return field->max_length(); } + bool result_as_longlong() + { + return field->can_be_compared_as_longlong(); + } friend class Item_default_value; friend class Item_insert_value; friend class st_select_lex_unit; @@ -973,6 +985,10 @@ public: } Item *real_item() { return *ref; } void print(String *str); + bool result_as_longlong() + { + return (*ref)->result_as_longlong(); + } }; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index bfbb3355004..e92e1d30ca4 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -25,6 +25,8 @@ #include #include "sql_select.h" +static bool convert_constant_item(THD *thd, Field *field, Item **item); + static Item_result item_store_type(Item_result a,Item_result b) { if (a == STRING_RESULT || b == STRING_RESULT) @@ -64,6 +66,7 @@ static void agg_result_type(Item_result *type, Item **items, uint nitems) SYNOPSIS: agg_cmp_type() + thd thread handle type [out] the aggregated type items array of items to aggregate the type from nitems number of items in the array @@ -78,24 +81,133 @@ static void agg_result_type(Item_result *type, Item **items, uint nitems) If all items are constants the type will be aggregated from all items. If there are some non-constant items then only types of non-constant items will be used for aggregation. + If there are DATE/TIME fields/functions in the list and no string + fields/functions in the list then: + The INT_RESULT type will be used for aggregation instead of orginal + result type of any DATE/TIME field/function in the list + All constant items in the list will be converted to a DATE/TIME using + found field or result field of found function. + + Implementation notes: + The code is equvalent to: + 1. Check the list for presense of a STRING field/function. + Collect the is_const flag. + 2. Get a Field* object to use for type coercion + 3. Perform type conversion. + 1 and 2 are implemented in 2 loops. The first searches for a DATE/TIME + field/function and checks presense of a STRING field/function. + The second loop works only if a DATE/TIME field/function is found. + It checks presense of a STRING field/function in the rest of the list. + + TODO + 1) The current implementation can produce false comparison results for + expressions like: + date_time_field BETWEEN string_field_with_dates AND string_constant + if the string_constant will omit some of leading zeroes. + In order to fully implement correct comparison of DATE/TIME the new + DATETIME_RESULT result type should be introduced and agg_cmp_type() + should return the DATE/TIME field used for the conversion. Later + this field can be used by comparison functions like Item_func_between to + convert string values to ints on the fly and thus return correct results. + This modification will affect functions BETWEEN, IN and CASE. + + 2) If in the list a DATE field/function and a DATETIME field/function + are present in the list then the first found field/function will be + used for conversion. This may lead to wrong results and probably should + be fixed. */ -static void agg_cmp_type(Item_result *type, Item **items, uint nitems) + +static void agg_cmp_type(THD *thd, Item_result *type, Item **items, uint nitems) { uint i; - type[0]= items[0]->result_type(); + Item::Type res; + char *buff= NULL; + uchar null_byte; + Field *field= NULL; + + /* Search for date/time fields/functions */ + for (i= 0; i < nitems; i++) + { + if (!items[i]->result_as_longlong()) + { + /* Do not convert anything if a string field/function is present */ + if (!items[i]->const_item() && items[i]->result_type() == STRING_RESULT) + { + i= nitems; + break; + } + continue; + } + if ((res= items[i]->real_item()->type()) == Item::FIELD_ITEM) + { + field= ((Item_field *)items[i]->real_item())->field; + break; + } + else if (res == Item::FUNC_ITEM) + { + field= items[i]->tmp_table_field_from_field_type(0); + if (field) + buff= alloc_root(thd->mem_root, field->max_length()); + if (!buff || !field) + { + if (field) + delete field; + if (buff) + my_free(buff, MYF(MY_WME)); + field= 0; + } + else + field->move_field(buff, &null_byte, 0); + break; + } + } + if (field) + { + /* Check the rest of the list for presense of a string field/function. */ + for (i++ ; i < nitems; i++) + { + if (!items[i]->const_item() && items[i]->result_type() == STRING_RESULT && + !items[i]->result_as_longlong()) + { + field= 0; + break; + } + } + } /* Reset to 0 on first occurence of non-const item. 1 otherwise */ bool is_const= items[0]->const_item(); + /* + If the first item is a date/time function then its result should be + compared as int + */ + if (field) + { + /* Suppose we are comparing dates and some non-constant items are present. */ + type[0]= INT_RESULT; + is_const= 0; + } + else + type[0]= items[0]->result_type(); - for (i= 1 ; i < nitems ; i++) + for (i= 0; i < nitems ; i++) { if (!items[i]->const_item()) { - type[0]= is_const ? items[i]->result_type() : - item_cmp_type(type[0], items[i]->result_type()); + Item_result result= field && items[i]->result_as_longlong() ? + INT_RESULT : items[i]->result_type(); + type[0]= is_const ? result : item_cmp_type(type[0], result); is_const= 0; } else if (is_const) type[0]= item_cmp_type(type[0], items[i]->result_type()); + else if (field) + convert_constant_item(thd, field, &items[i]); + } + + if (res == Item::FUNC_ITEM && field) + { + delete field; + my_free(buff, MYF(MY_WME)); } } @@ -929,31 +1041,9 @@ void Item_func_between::fix_length_and_dec() */ if (!args[0] || !args[1] || !args[2]) return; - agg_cmp_type(&cmp_type, args, 3); - if (cmp_type == STRING_RESULT && - agg_arg_charsets(cmp_collation, args, 3, MY_COLL_CMP_CONV)) - return; - - /* - Make a special case of compare with date/time and longlong fields. - They are compared as integers, so for const item this time-consuming - conversion can be done only once, not for every single comparison - */ - if (args[0]->type() == FIELD_ITEM) - { - Field *field=((Item_field*) args[0])->field; - if (field->can_be_compared_as_longlong()) - { - /* - The following can't be recoded with || as convert_constant_item - changes the argument - */ - if (convert_constant_item(thd, field,&args[1])) - cmp_type=INT_RESULT; // Works for all types. - if (convert_constant_item(thd, field,&args[2])) - cmp_type=INT_RESULT; // Works for all types. - } - } + agg_cmp_type(thd, &cmp_type, args, 3); + if (cmp_type == STRING_RESULT) + agg_arg_charsets(cmp_collation, args, 3, MY_COLL_CMP_CONV); } @@ -1477,7 +1567,7 @@ void Item_func_case::fix_length_and_dec() for (nagg= 0; nagg < ncases/2 ; nagg++) agg[nagg+1]= args[nagg*2]; nagg++; - agg_cmp_type(&cmp_type, agg, nagg); + agg_cmp_type(current_thd, &cmp_type, agg, nagg); if ((cmp_type == STRING_RESULT) && agg_arg_charsets(cmp_collation, agg, nagg, MY_COLL_CMP_CONV)) return; @@ -1958,7 +2048,7 @@ void Item_func_in::fix_length_and_dec() uint const_itm= 1; THD *thd= current_thd; - agg_cmp_type(&cmp_type, args, arg_count); + agg_cmp_type(thd, &cmp_type, args, arg_count); if (cmp_type == STRING_RESULT && agg_arg_charsets(cmp_collation, args, arg_count, MY_COLL_CMP_CONV)) diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index ade09113c63..08d9de6ffd6 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -43,8 +43,11 @@ public: int set_compare_func(Item_bool_func2 *owner, Item_result type); inline int set_compare_func(Item_bool_func2 *owner_arg) { - return set_compare_func(owner_arg, item_cmp_type((*a)->result_type(), - (*b)->result_type())); + Item_result ar= (*a)->result_as_longlong() && (*b)->const_item() ? + INT_RESULT : (*a)->result_type(); + Item_result br= (*b)->result_as_longlong() && (*a)->const_item() ? + INT_RESULT : (*b)->result_type(); + return set_compare_func(owner_arg, item_cmp_type(ar, br)); } inline int set_cmp_func(Item_bool_func2 *owner_arg, Item **a1, Item **a2, @@ -57,8 +60,11 @@ public: inline int set_cmp_func(Item_bool_func2 *owner_arg, Item **a1, Item **a2) { - return set_cmp_func(owner_arg, a1, a2, item_cmp_type((*a1)->result_type(), - (*a2)->result_type())); + Item_result ar= (*a1)->result_as_longlong() && (*a2)->const_item() ? + INT_RESULT : (*a1)->result_type(); + Item_result br= (*a2)->result_as_longlong() && (*a1)->const_item() ? + INT_RESULT : (*a2)->result_type(); + return set_cmp_func(owner_arg, a1, a2, item_cmp_type(ar, br)); } inline int compare() { return (this->*func)(); } diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 5fdbd968df1..f7b4c9dd630 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -2306,6 +2306,20 @@ String *Item_datetime_typecast::val_str(String *str) } +longlong Item_datetime_typecast::val_int() +{ + DBUG_ASSERT(fixed == 1); + TIME ltime; + if (get_arg0_date(<ime,1)) + { + null_value= 1; + return 0; + } + + return TIME_to_ulonglong_datetime(<ime); +} + + bool Item_time_typecast::get_time(TIME *ltime) { bool res= get_arg0_time(ltime); @@ -2320,6 +2334,17 @@ bool Item_time_typecast::get_time(TIME *ltime) } +longlong Item_time_typecast::val_int() +{ + TIME ltime; + if (get_time(<ime)) + { + null_value= 1; + return 0; + } + return ltime.hour * 10000L + ltime.minute * 100 + ltime.second; +} + String *Item_time_typecast::val_str(String *str) { DBUG_ASSERT(fixed == 1); @@ -2359,6 +2384,14 @@ String *Item_date_typecast::val_str(String *str) return 0; } +longlong Item_date_typecast::val_int() +{ + DBUG_ASSERT(fixed == 1); + TIME ltime; + if (args[0]->get_date(<ime, TIME_FUZZY_DATE)) + return 0; + return (longlong) (ltime.year * 10000L + ltime.month * 100 + ltime.day); +} /* MAKEDATE(a,b) is a date function that creates a date value @@ -2395,6 +2428,33 @@ err: } +longlong Item_func_makedate::val_int() +{ + DBUG_ASSERT(fixed == 1); + TIME l_time; + long daynr= (long) args[1]->val_int(); + long yearnr= (long) args[0]->val_int(); + long days; + + if (args[0]->null_value || args[1]->null_value || + yearnr < 0 || daynr <= 0) + goto err; + + days= calc_daynr(yearnr,1,1) + daynr - 1; + /* Day number from year 0 to 9999-12-31 */ + if (days >= 0 && days < MAX_DAY_NUMBER) + { + null_value=0; + get_date_from_daynr(days,&l_time.year,&l_time.month,&l_time.day); + return (longlong) (l_time.year * 10000L + l_time.month * 100 + l_time.day); + } + +err: + null_value= 1; + return 0; +} + + void Item_func_add_time::fix_length_and_dec() { enum_field_types arg0_field_type; diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 163b1591e52..95f1dfa75d1 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -339,6 +339,7 @@ public: { return (new Field_date(maybe_null, name, t_arg, &my_charset_bin)); } + bool result_as_longlong() { return TRUE; } }; @@ -354,6 +355,7 @@ public: { return (new Field_datetime(maybe_null, name, t_arg, &my_charset_bin)); } + bool result_as_longlong() { return TRUE; } }; @@ -383,6 +385,7 @@ public: TIME representation using UTC-SYSTEM or per-thread time zone. */ virtual void store_now_in_TIME(TIME *now_time)=0; + bool result_as_longlong() { return TRUE; } }; @@ -588,6 +591,7 @@ public: { return (new Field_time(maybe_null, name, t_arg, &my_charset_bin)); } + bool result_as_longlong() { return TRUE; } }; /* @@ -715,6 +719,8 @@ public: max_length= 10; maybe_null= 1; } + bool result_as_longlong() { return TRUE; } + longlong val_int(); }; @@ -731,6 +737,8 @@ public: { return (new Field_time(maybe_null, name, t_arg, &my_charset_bin)); } + bool result_as_longlong() { return TRUE; } + longlong val_int(); }; @@ -746,6 +754,8 @@ public: { return (new Field_datetime(maybe_null, name, t_arg, &my_charset_bin)); } + bool result_as_longlong() { return TRUE; } + longlong val_int(); }; class Item_func_makedate :public Item_str_func @@ -764,6 +774,8 @@ public: { return (new Field_date(maybe_null, name, t_arg, &my_charset_bin)); } + bool result_as_longlong() { return TRUE; } + longlong val_int(); }; From 8d94b8cacf10eb89f407ff4c2311794431ad39d5 Mon Sep 17 00:00:00 2001 From: "gkodinov@mysql.com" <> Date: Tue, 13 Jun 2006 18:18:32 +0300 Subject: [PATCH 18/29] Bug #20195: INSERT DELAYED with auto_increment is assigned wrong values The INSERT DELAYED should not maintain its own private auto-increment counter, because this is assuming that other threads cannot insert into the table while the INSERT DELAYED thread is inserting, which is a wrong assumption. So the start of processing of a batch of INSERT rows in the INSERT DELAYED thread must be treated as a start of a new statement and cached next_insert_id must be cleared. --- mysql-test/r/delayed.result | 30 +++++++++++++++++++++++ mysql-test/t/delayed.test | 49 +++++++++++++++++++++++++++++++++++++ sql/sql_insert.cc | 8 ++++++ 3 files changed, 87 insertions(+) diff --git a/mysql-test/r/delayed.result b/mysql-test/r/delayed.result index f8ae61b03fb..a336f3b4108 100644 --- a/mysql-test/r/delayed.result +++ b/mysql-test/r/delayed.result @@ -39,3 +39,33 @@ select * from t1; a 1 drop table t1; +CREATE TABLE t1 ( a int(10) NOT NULL auto_increment, PRIMARY KEY (a)); +insert delayed into t1 values(null); +insert into t1 values(null); +insert into t1 values(null); +insert delayed into t1 values(null); +insert delayed into t1 values(null); +insert delayed into t1 values(null); +insert into t1 values(null); +insert into t1 values(null); +insert into t1 values(null); +delete from t1 where a=6; +insert delayed into t1 values(null); +insert delayed into t1 values(null); +insert delayed into t1 values(null); +insert delayed into t1 values(null); +select * from t1 order by a; +a +1 +2 +3 +4 +5 +7 +8 +9 +10 +11 +12 +13 +DROP TABLE t1; diff --git a/mysql-test/t/delayed.test b/mysql-test/t/delayed.test index 5ae757b1fde..55e8f81f763 100644 --- a/mysql-test/t/delayed.test +++ b/mysql-test/t/delayed.test @@ -50,3 +50,52 @@ insert into t1 values (1); insert delayed into t1 values (1); select * from t1; drop table t1; + +# +# Bug #20195: INSERT DELAYED with auto_increment is assigned wrong values +# +CREATE TABLE t1 ( a int(10) NOT NULL auto_increment, PRIMARY KEY (a)); + +# Make one delayed insert to start the separate thread +insert delayed into t1 values(null); + +# Do some normal inserts +insert into t1 values(null); +insert into t1 values(null); + +# Discarded, since the delayed-counter is 2, which is already used +insert delayed into t1 values(null); + +# Discarded, since the delayed-counter is 3, which is already used +insert delayed into t1 values(null); + +# Works, since the delayed-counter is 4, which is unused +insert delayed into t1 values(null); + +# Do some more inserts +insert into t1 values(null); +insert into t1 values(null); +insert into t1 values(null); + +# Delete one of the above to make a hole +delete from t1 where a=6; + +# Discarded, since the delayed-counter is 5, which is already used +insert delayed into t1 values(null); + +# Works, since the delayed-counter is 6, which is unused (the row we deleted) +insert delayed into t1 values(null); + +# Discarded, since the delayed-counter is 7, which is already used +insert delayed into t1 values(null); + +# Works, since the delayed-counter is 8, which is unused +insert delayed into t1 values(null); + +# 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; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 26f3b6f5faa..5630702fe52 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1938,6 +1938,14 @@ bool delayed_insert::handle_inserts(void) if (!using_bin_log) table->file->extra(HA_EXTRA_WRITE_CACHE); pthread_mutex_lock(&mutex); + + /* Reset auto-increment cacheing */ + if (thd.clear_next_insert_id) + { + thd.next_insert_id= 0; + thd.clear_next_insert_id= 0; + } + while ((row=rows.get())) { stacked_inserts--; From 355753baa465a7507234158e3a99d1805d01e5a9 Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Tue, 13 Jun 2006 19:57:50 -0700 Subject: [PATCH 19/29] Fixed bug #14896. This bug in Field_string::cmp resulted in a wrong comparison with keys in partial indexes over multi-byte character fields. Given field a is declared as a varchar(16) collate utf8_unicode_ci INDEX(a(4)) gives us an example of such an index. Wrong key comparisons could lead to wrong result sets if the selected query execution plan used a range scan by a partial index over a utf8 character field. This also caused wrong results in many other cases. --- mysql-test/r/ctype_utf8.result | 40 ++++++++++++++++++++++++++++++++++ mysql-test/t/ctype_utf8.test | 26 ++++++++++++++++++++++ sql/field.cc | 20 ++++++----------- 3 files changed, 73 insertions(+), 13 deletions(-) diff --git a/mysql-test/r/ctype_utf8.result b/mysql-test/r/ctype_utf8.result index 69d7577ee77..cc271176dfc 100644 --- a/mysql-test/r/ctype_utf8.result +++ b/mysql-test/r/ctype_utf8.result @@ -1124,3 +1124,43 @@ check table t1; Table Op Msg_type Msg_text test.t1 check status OK drop table t1; +SET NAMES utf8; +CREATE TABLE t1 (id int PRIMARY KEY, +a varchar(16) collate utf8_unicode_ci NOT NULL default '', +b int, +f varchar(128) default 'XXX', +INDEX (a(4)) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +INSERT INTO t1(id, a, b) VALUES +(1, 'cccc', 50), (2, 'cccc', 70), (3, 'cccc', 30), +(4, 'cccc', 30), (5, 'cccc', 20), (6, 'bbbbbb', 40), +(7, 'dddd', 30), (8, 'aaaa', 10), (9, 'aaaa', 50), +(10, 'eeeee', 40), (11, 'bbbbbb', 60); +SELECT id, a, b FROM t1; +id a b +1 cccc 50 +2 cccc 70 +3 cccc 30 +4 cccc 30 +5 cccc 20 +6 bbbbbb 40 +7 dddd 30 +8 aaaa 10 +9 aaaa 50 +10 eeeee 40 +11 bbbbbb 60 +SELECT id, a, b FROM t1 WHERE a BETWEEN 'aaaa' AND 'bbbbbb'; +id a b +8 aaaa 10 +9 aaaa 50 +6 bbbbbb 40 +11 bbbbbb 60 +SELECT id, a FROM t1 WHERE a='bbbbbb'; +id a +6 bbbbbb +11 bbbbbb +SELECT id, a FROM t1 WHERE a='bbbbbb' ORDER BY b; +id a +6 bbbbbb +11 bbbbbb +DROP TABLE t1; diff --git a/mysql-test/t/ctype_utf8.test b/mysql-test/t/ctype_utf8.test index 5044f7979f1..9b8e6590999 100644 --- a/mysql-test/t/ctype_utf8.test +++ b/mysql-test/t/ctype_utf8.test @@ -926,4 +926,30 @@ INSERT INTO t1 VALUES('uUABCDEFGHIGKLMNOPRSTUVWXYZ̈bbbbbbbbbbbbbbbbbbbbbbbbbbbb check table t1; drop table t1; +# +# Bug#14896: Comparison with a key in a partial index over mb chararacter field +# + +SET NAMES utf8; +CREATE TABLE t1 (id int PRIMARY KEY, + a varchar(16) collate utf8_unicode_ci NOT NULL default '', + b int, + f varchar(128) default 'XXX', + INDEX (a(4)) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +INSERT INTO t1(id, a, b) VALUES + (1, 'cccc', 50), (2, 'cccc', 70), (3, 'cccc', 30), + (4, 'cccc', 30), (5, 'cccc', 20), (6, 'bbbbbb', 40), + (7, 'dddd', 30), (8, 'aaaa', 10), (9, 'aaaa', 50), + (10, 'eeeee', 40), (11, 'bbbbbb', 60); + +SELECT id, a, b FROM t1; + +SELECT id, a, b FROM t1 WHERE a BETWEEN 'aaaa' AND 'bbbbbb'; + +SELECT id, a FROM t1 WHERE a='bbbbbb'; +SELECT id, a FROM t1 WHERE a='bbbbbb' ORDER BY b; + +DROP TABLE t1; + # End of 4.1 tests diff --git a/sql/field.cc b/sql/field.cc index a64eaad7308..6eea006fcc8 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -5072,17 +5072,6 @@ int Field_string::cmp(const char *a_ptr, const char *b_ptr) { uint a_len, b_len; - if (field_charset->strxfrm_multiply > 1) - { - /* - We have to remove end space to be able to compare multi-byte-characters - like in latin_de 'ae' and 0xe4 - */ - return field_charset->coll->strnncollsp(field_charset, - (const uchar*) a_ptr, field_length, - (const uchar*) b_ptr, - field_length); - } if (field_charset->mbmaxlen != 1) { uint char_len= field_length/field_charset->mbmaxlen; @@ -5091,8 +5080,13 @@ int Field_string::cmp(const char *a_ptr, const char *b_ptr) } else a_len= b_len= field_length; - return my_strnncoll(field_charset,(const uchar*) a_ptr, a_len, - (const uchar*) b_ptr, b_len); + /* + We have to remove end space to be able to compare multi-byte-characters + like in latin_de 'ae' and 0xe4 + */ + return field_charset->coll->strnncollsp(field_charset, + (const uchar*) a_ptr, a_len, + (const uchar*) b_ptr, b_len); } From 5cdd1eb62dd7ab1466a6405e0821ad74a387f92a Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Tue, 13 Jun 2006 22:38:00 -0700 Subject: [PATCH 20/29] Post-review corrections of the fix for bug #18206. --- mysql-test/r/func_group.result | 13 +++++++++++++ mysql-test/t/func_group.test | 9 +++++++++ sql/opt_sum.cc | 6 ++++-- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/func_group.result b/mysql-test/r/func_group.result index ffa68f279f3..f16fb01d0df 100644 --- a/mysql-test/r/func_group.result +++ b/mysql-test/r/func_group.result @@ -940,3 +940,16 @@ EXPLAIN SELECT MAX(b) FROM t1; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 2 DROP TABLE t1; +CREATE TABLE t1 (id int , b varchar(512), INDEX(b(250))) COLLATE latin1_bin; +Warnings: +Warning 1246 Converting column 'b' from CHAR to TEXT +INSERT INTO t1 VALUES +(1,CONCAT(REPEAT('_', 250), "qq")), (1,CONCAT(REPEAT('_', 250), "zz")), +(1,CONCAT(REPEAT('_', 250), "aa")), (1,CONCAT(REPEAT('_', 250), "ff")); +SELECT MAX(b) FROM t1; +MAX(b) +__________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________zz +EXPLAIN SELECT MAX(b) FROM t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 +DROP TABLE t1; diff --git a/mysql-test/t/func_group.test b/mysql-test/t/func_group.test index f8a3ed0f25e..7133b96f868 100644 --- a/mysql-test/t/func_group.test +++ b/mysql-test/t/func_group.test @@ -617,4 +617,13 @@ SELECT MAX(b) FROM t1; EXPLAIN SELECT MAX(b) FROM t1; DROP TABLE t1; +CREATE TABLE t1 (id int , b varchar(512), INDEX(b(250))) COLLATE latin1_bin; +INSERT INTO t1 VALUES + (1,CONCAT(REPEAT('_', 250), "qq")), (1,CONCAT(REPEAT('_', 250), "zz")), + (1,CONCAT(REPEAT('_', 250), "aa")), (1,CONCAT(REPEAT('_', 250), "ff")); + +SELECT MAX(b) FROM t1; +EXPLAIN SELECT MAX(b) FROM t1; +DROP TABLE t1; + # End of 4.1 tests diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 97e271121d3..21c637f4faf 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -685,8 +685,10 @@ static bool find_key_for_maxmin(bool max_fl, TABLE_REF *ref, if (!(table->file->index_flags(idx, jdx, 0) & HA_READ_ORDER)) return 0; - /* Check whether the index component is partial */ - if (part->length < table->field[part->fieldnr-1]->pack_length()) + /* Check whether the index component is partial */ + Field *part_field= table->field[part->fieldnr-1]; + if ((part_field->flags & BLOB_FLAG) || + part->length < part_field->key_length()) break; if (field->eq(part->field)) From 476d728a8da07ebece6b945c922864dcd27724c9 Mon Sep 17 00:00:00 2001 From: "gkodinov@mysql.com" <> Date: Wed, 14 Jun 2006 15:57:23 +0300 Subject: [PATCH 21/29] Bug #18895: BIT values cause joins to fail The Field::eq() considered instances of Field_bit that differ only in bit_ptr/bit_ofs equal. This caused equality conditions optimization (build_equal_items_for_cond()) to make bad field substitutions that result in wrong predicates. Field_bit requires an overloaded eq() function that checks the bit_ptr/bit_ofs in addition to Field::eq(). --- mysql-test/r/select.result | 20 ++++++++++++++++++++ mysql-test/t/select.test | 26 ++++++++++++++++++++++++++ sql/field.h | 9 ++++++++- 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index 4c1e64cc1cb..e80e8f9ec41 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -3359,3 +3359,23 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 range PRIMARY,b b 5 NULL 3 Using where 1 SIMPLE t2 ref c c 5 test.t1.a 2 Using where DROP TABLE t1, t2; +create table t1 ( +a int unsigned not null auto_increment primary key, +b bit not null, +c bit not null +); +create table t2 ( +a int unsigned not null auto_increment primary key, +b bit not null, +c int unsigned not null, +d varchar(50) +); +insert into t1 (b,c) values (0,1), (0,1); +insert into t2 (b,c) values (0,1); +select t1.a, t1.b + 0, t1.c + 0, t2.a, t2.b + 0, t2.c, t2.d +from t1 left outer join t2 on t1.a = t2.c and t2.b <> 1 +where t1.b <> 1 order by t1.a; +a t1.b + 0 t1.c + 0 a t2.b + 0 c d +1 0 1 1 0 1 NULL +2 0 1 NULL NULL NULL NULL +drop table t1,t2; diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test index 4ed7c3d1de9..ef7f9ca9d89 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -2840,3 +2840,29 @@ EXPLAIN SELECT a, c, d, f FROM t1,t2 WHERE a=c AND b BETWEEN 4 AND 6 AND a > 0; DROP TABLE t1, t2; + +# +# Bug #18895: BIT values cause joins to fail +# +create table t1 ( + a int unsigned not null auto_increment primary key, + b bit not null, + c bit not null +); + +create table t2 ( + a int unsigned not null auto_increment primary key, + b bit not null, + c int unsigned not null, + d varchar(50) +); + +insert into t1 (b,c) values (0,1), (0,1); +insert into t2 (b,c) values (0,1); + +-- Row 1 should succeed. Row 2 should fail. Both fail. +select t1.a, t1.b + 0, t1.c + 0, t2.a, t2.b + 0, t2.c, t2.d +from t1 left outer join t2 on t1.a = t2.c and t2.b <> 1 +where t1.b <> 1 order by t1.a; + +drop table t1,t2; diff --git a/sql/field.h b/sql/field.h index f4d27e46877..e7b7aa45c27 100644 --- a/sql/field.h +++ b/sql/field.h @@ -124,7 +124,7 @@ public: static bool type_can_have_key_part(enum_field_types); static enum_field_types field_type_merge(enum_field_types, enum_field_types); static Item_result result_merge_type(enum_field_types); - bool eq(Field *field) + virtual bool eq(Field *field) { return (ptr == field->ptr && null_ptr == field->null_ptr && null_bit == field->null_bit); @@ -1358,6 +1358,13 @@ public: bit_ptr= bit_ptr_arg; bit_ofs= bit_ofs_arg; } + bool eq(Field *field) + { + return (Field::eq(field) && + field->type() == type() && + bit_ptr == ((Field_bit *)field)->bit_ptr && + bit_ofs == ((Field_bit *)field)->bit_ofs); + } }; From 6e5f578a46dcea8760551f72c4d6a77d986f8c4c Mon Sep 17 00:00:00 2001 From: "sergefp@mysql.com" <> Date: Wed, 14 Jun 2006 21:06:02 +0400 Subject: [PATCH 22/29] Better comments in KEY_PART_INFO struct --- sql/structs.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sql/structs.h b/sql/structs.h index 081ada88bf7..2037496635a 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -70,7 +70,13 @@ typedef struct st_key_part_info { /* Info about a key part */ Field *field; uint offset; /* offset in record (from 0) */ uint null_offset; /* Offset to null_bit in record */ - uint16 length; /* Length of key_part */ + uint16 length; /* Length of keypart value in bytes */ + /* + Number of bytes required to store the keypart value. This may be + different from the "length" field as it also counts + - possible NULL-flag byte (see HA_KEY_NULL_LENGTH) + - possible HA_KEY_BLOB_LENGTH bytes needed to store actual value length. + */ uint16 store_length; uint16 key_type; uint16 fieldnr; /* Fieldnum in UNIREG */ From a2261d57be6dac312b0ae599031210341f5fcf70 Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Thu, 15 Jun 2006 01:48:41 +0400 Subject: [PATCH 23/29] Many files: After merge fix --- mysql-test/r/cast.result | 2 +- mysql-test/r/func_concat.result | 14 +++--- mysql-test/r/func_time.result | 4 +- sql/field.cc | 82 +++++++++++++-------------------- sql/item_cmpfunc.cc | 8 ++-- sql/item_cmpfunc.h | 15 ++---- 6 files changed, 52 insertions(+), 73 deletions(-) diff --git a/mysql-test/r/cast.result b/mysql-test/r/cast.result index 8f5a5eea99f..d60efa083e0 100644 --- a/mysql-test/r/cast.result +++ b/mysql-test/r/cast.result @@ -254,7 +254,7 @@ cast("2001-1-1" as datetime) = "2001-01-01 00:00:00" 1 select cast("1:2:3" as TIME) = "1:02:03"; cast("1:2:3" as TIME) = "1:02:03" -1 +0 select cast(NULL as DATE); cast(NULL as DATE) NULL diff --git a/mysql-test/r/func_concat.result b/mysql-test/r/func_concat.result index 7fb489f2a14..66808afd4e9 100644 --- a/mysql-test/r/func_concat.result +++ b/mysql-test/r/func_concat.result @@ -68,13 +68,6 @@ select 'a' union select concat('a', -0.0000); a a a0.0000 -create table t1(f1 varchar(6)) charset=utf8; -insert into t1 values ("123456"); -select concat(f1, 2) a from t1 union select 'x' a from t1; -a -1234562 -x -drop table t1; select concat((select x from (select 'a' as x) as t1 ), (select y from (select 'b' as y) as t2 )) from (select 1 union select 2 ) as t3; @@ -82,3 +75,10 @@ concat((select x from (select 'a' as x) as t1 ), (select y from (select 'b' as y) as t2 )) ab ab +create table t1(f1 varchar(6)) charset=utf8; +insert into t1 values ("123456"); +select concat(f1, 2) a from t1 union select 'x' a from t1; +a +1234562 +x +drop table t1; diff --git a/mysql-test/r/func_time.result b/mysql-test/r/func_time.result index a0faa05f988..cee5dc70c32 100644 --- a/mysql-test/r/func_time.result +++ b/mysql-test/r/func_time.result @@ -784,7 +784,9 @@ f1 select f1 from t1 where "2006-1-1" between f1 and 'zzz'; f1 Warnings: -Warning 1292 Truncated incorrect date value: 'zzz' +Warning 1292 Incorrect date value: 'zzz' for column 'f1' at row 1 +Warning 1292 Truncated incorrect INTEGER value: 'zzz' +Warning 1292 Truncated incorrect INTEGER value: 'zzz' select f1 from t1 where makedate(2006,1) between date(f1) and date(f3); f1 2006-01-01 diff --git a/sql/field.cc b/sql/field.cc index 64f888eb24d..b0a0633b6ce 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -4530,11 +4530,11 @@ int Field_timestamp::store(const char *from,uint len,CHARSET_INFO *cs) int error; bool have_smth_to_conv; my_bool in_dst_time_gap; - THD *thd= table->in_use; + THD *thd= table ? table->in_use : current_thd; /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */ have_smth_to_conv= (str_to_datetime(from, len, &l_time, - (table->in_use->variables.sql_mode & + (thd->variables.sql_mode & MODE_NO_ZERO_DATE) | MODE_NO_ZERO_IN_DATE, &error) > MYSQL_TIMESTAMP_ERROR); @@ -4599,7 +4599,7 @@ int Field_timestamp::store(longlong nr, bool unsigned_val) my_time_t timestamp= 0; int error; my_bool in_dst_time_gap; - THD *thd= table->in_use; + THD *thd= table ? table->in_use : current_thd; /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */ longlong tmp= number_to_datetime(nr, &l_time, (thd->variables.sql_mode & @@ -4653,7 +4653,7 @@ longlong Field_timestamp::val_int(void) { uint32 temp; TIME time_tmp; - THD *thd= table->in_use; + THD *thd= table ? table->in_use : current_thd; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) @@ -4678,7 +4678,7 @@ String *Field_timestamp::val_str(String *val_buffer, String *val_ptr) { uint32 temp, temp2; TIME time_tmp; - THD *thd= table->in_use; + THD *thd= table ? table->in_use : current_thd; char *to; val_buffer->alloc(field_length+1); @@ -4749,7 +4749,7 @@ String *Field_timestamp::val_str(String *val_buffer, String *val_ptr) bool Field_timestamp::get_date(TIME *ltime, uint fuzzydate) { long temp; - THD *thd= table->in_use; + THD *thd= table ? table->in_use : current_thd; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) temp=uint4korr(ptr); @@ -4832,7 +4832,8 @@ void Field_timestamp::sql_type(String &res) const void Field_timestamp::set_time() { - long tmp= (long) table->in_use->query_start(); + THD *thd= table ? table->in_use : current_thd; + long tmp= (long) thd->query_start(); set_notnull(); #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) @@ -5024,12 +5025,13 @@ String *Field_time::val_str(String *val_buffer, bool Field_time::get_date(TIME *ltime, uint fuzzydate) { long tmp; + THD *thd= table ? table->in_use : current_thd; if (!(fuzzydate & TIME_FUZZY_DATE)) { - push_warning_printf(table->in_use, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, ER(ER_WARN_DATA_OUT_OF_RANGE), field_name, - table->in_use->row_count); + thd->row_count); return 1; } tmp=(long) sint3korr(ptr); @@ -5217,9 +5219,10 @@ int Field_date::store(const char *from, uint len,CHARSET_INFO *cs) TIME l_time; uint32 tmp; int error; + THD *thd= table ? table->in_use : current_thd; if (str_to_datetime(from, len, &l_time, TIME_FUZZY_DATE | - (table->in_use->variables.sql_mode & + (thd->variables.sql_mode & (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES)), &error) <= MYSQL_TIMESTAMP_ERROR) @@ -5272,9 +5275,10 @@ int Field_date::store(longlong nr, bool unsigned_val) TIME not_used; int error; longlong initial_nr= nr; + THD *thd= table ? table->in_use : current_thd; nr= number_to_datetime(nr, ¬_used, (TIME_FUZZY_DATE | - (table->in_use->variables.sql_mode & + (thd->variables.sql_mode & (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES))), &error); @@ -5420,9 +5424,10 @@ int Field_newdate::store(const char *from,uint len,CHARSET_INFO *cs) TIME l_time; long tmp; int error; + THD *thd= table ? table->in_use : current_thd; if (str_to_datetime(from, len, &l_time, (TIME_FUZZY_DATE | - (table->in_use->variables.sql_mode & + (thd->variables.sql_mode & (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES))), &error) <= MYSQL_TIMESTAMP_ERROR) @@ -5460,9 +5465,10 @@ int Field_newdate::store(longlong nr, bool unsigned_val) TIME l_time; longlong tmp; int error; + THD *thd= table ? table->in_use : current_thd; if (number_to_datetime(nr, &l_time, (TIME_FUZZY_DATE | - (table->in_use->variables.sql_mode & + (thd->variables.sql_mode & (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES))), &error) == LL(-1)) @@ -5605,10 +5611,11 @@ int Field_datetime::store(const char *from,uint len,CHARSET_INFO *cs) int error; ulonglong tmp= 0; enum enum_mysql_timestamp_type func_res; + THD *thd= table ? table->in_use : current_thd; func_res= str_to_datetime(from, len, &time_tmp, (TIME_FUZZY_DATE | - (table->in_use->variables.sql_mode & + (thd->variables.sql_mode & (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES))), &error); @@ -5655,9 +5662,10 @@ int Field_datetime::store(longlong nr, bool unsigned_val) TIME not_used; int error; longlong initial_nr= nr; + THD *thd= table ? table->in_use : current_thd; nr= number_to_datetime(nr, ¬_used, (TIME_FUZZY_DATE | - (table->in_use->variables.sql_mode & + (thd->variables.sql_mode & (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES))), &error); @@ -9076,10 +9084,10 @@ Field::set_datetime_warning(MYSQL_ERROR::enum_warning_level level, uint code, const char *str, uint str_length, timestamp_type ts_type, int cuted_increment) { - if (table->in_use->really_abort_on_warning() || + THD *thd= table ? table->in_use : current_thd; + if (thd->really_abort_on_warning() || set_warning(level, code, cuted_increment)) - make_truncated_value_warning(table ? table->in_use : current_thd, - str, str_length, ts_type, + make_truncated_value_warning(thd, str, str_length, ts_type, field_name); } @@ -9106,13 +9114,13 @@ Field::set_datetime_warning(MYSQL_ERROR::enum_warning_level level, uint code, longlong nr, timestamp_type ts_type, int cuted_increment) { - if (table->in_use->really_abort_on_warning() || + THD *thd= table ? table->in_use : current_thd; + if (thd->really_abort_on_warning() || set_warning(level, code, cuted_increment)) { char str_nr[22]; char *str_end= longlong10_to_str(nr, str_nr, -10); - make_truncated_value_warning(table ? table->in_use : current_thd, - str_nr, (uint) (str_end - str_nr), + make_truncated_value_warning(thd, str_nr, (uint) (str_end - str_nr), ts_type, field_name); } } @@ -9138,41 +9146,15 @@ void Field::set_datetime_warning(MYSQL_ERROR::enum_warning_level level, uint code, double nr, timestamp_type ts_type) { - if (table->in_use->really_abort_on_warning() || + THD *thd= table ? table->in_use : current_thd; + if (thd->really_abort_on_warning() || set_warning(level, code, 1)) { /* DBL_DIG is enough to print '-[digits].E+###' */ char str_nr[DBL_DIG + 8]; uint str_len= my_sprintf(str_nr, (str_nr, "%g", nr)); - make_truncated_value_warning(table ? table->in_use : current_thd, - str_nr, str_len, ts_type, + make_truncated_value_warning(thd, str_nr, str_len, ts_type, field_name); } } -/* - maximum possible display length for blob - - SYNOPSIS - Field_blob::max_length() - - RETURN - length -*/ -uint32 Field_blob::max_length() -{ - switch (packlength) - { - case 1: - return 255 * field_charset->mbmaxlen; - case 2: - return 65535 * field_charset->mbmaxlen; - case 3: - return 16777215 * field_charset->mbmaxlen; - case 4: - return (uint32) 4294967295U; - default: - DBUG_ASSERT(0); // we should never go here - return 0; - } -} diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 6a1ec1de7e7..db1af9de0a2 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -366,18 +366,18 @@ static bool convert_constant_item(THD *thd, Field *field, Item **item) if (!(*item)->with_subselect && (*item)->const_item()) { /* For comparison purposes allow invalid dates like 2000-01-32 */ - ulong orig_sql_mode= field->table->in_use->variables.sql_mode; - field->table->in_use->variables.sql_mode|= MODE_INVALID_DATES; + ulong orig_sql_mode= thd->variables.sql_mode; + thd->variables.sql_mode|= MODE_INVALID_DATES; if (!(*item)->save_in_field(field, 1) && !((*item)->null_value)) { Item *tmp=new Item_int_with_ref(field->val_int(), *item, test(field->flags & UNSIGNED_FLAG)); - field->table->in_use->variables.sql_mode= orig_sql_mode; + thd->variables.sql_mode= orig_sql_mode; if (tmp) thd->change_item_tree(item, tmp); return 1; // Item was replaced } - field->table->in_use->variables.sql_mode= orig_sql_mode; + thd->variables.sql_mode= orig_sql_mode; } return 0; } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 340bf2bb2bf..a2b10eacc79 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -45,11 +45,8 @@ public: int set_compare_func(Item_bool_func2 *owner, Item_result type); inline int set_compare_func(Item_bool_func2 *owner_arg) { - Item_result ar= (*a)->result_as_longlong() && (*b)->const_item() ? - INT_RESULT : (*a)->result_type(); - Item_result br= (*b)->result_as_longlong() && (*a)->const_item() ? - INT_RESULT : (*b)->result_type(); - return set_compare_func(owner_arg, item_cmp_type(ar, br)); + return set_compare_func(owner_arg, item_cmp_type((*a)->result_type(), + (*b)->result_type())); } inline int set_cmp_func(Item_bool_func2 *owner_arg, Item **a1, Item **a2, @@ -62,11 +59,9 @@ public: inline int set_cmp_func(Item_bool_func2 *owner_arg, Item **a1, Item **a2) { - Item_result ar= (*a1)->result_as_longlong() && (*a2)->const_item() ? - INT_RESULT : (*a1)->result_type(); - Item_result br= (*a2)->result_as_longlong() && (*a1)->const_item() ? - INT_RESULT : (*a2)->result_type(); - return set_cmp_func(owner_arg, a1, a2, item_cmp_type(ar, br)); + return set_cmp_func(owner_arg, a1, a2, + item_cmp_type((*a1)->result_type(), + (*a2)->result_type())); } inline int compare() { return (this->*func)(); } From 2877b5ef64ed40ccfdaed3f743753d4348a7e814 Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Thu, 15 Jun 2006 16:39:18 +0400 Subject: [PATCH 24/29] item_cmpfunc.h, cast.result: Post fix for bug#16377 --- mysql-test/r/cast.result | 2 +- sql/item_cmpfunc.h | 15 +++++---------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/mysql-test/r/cast.result b/mysql-test/r/cast.result index 2538fbfd61d..68687670e17 100644 --- a/mysql-test/r/cast.result +++ b/mysql-test/r/cast.result @@ -192,7 +192,7 @@ cast("2001-1-1" as datetime) = "2001-01-01 00:00:00" 1 select cast("1:2:3" as TIME) = "1:02:03"; cast("1:2:3" as TIME) = "1:02:03" -1 +0 select cast(NULL as DATE); cast(NULL as DATE) NULL diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 08d9de6ffd6..68852b5a5f6 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -43,11 +43,8 @@ public: int set_compare_func(Item_bool_func2 *owner, Item_result type); inline int set_compare_func(Item_bool_func2 *owner_arg) { - Item_result ar= (*a)->result_as_longlong() && (*b)->const_item() ? - INT_RESULT : (*a)->result_type(); - Item_result br= (*b)->result_as_longlong() && (*a)->const_item() ? - INT_RESULT : (*b)->result_type(); - return set_compare_func(owner_arg, item_cmp_type(ar, br)); + return set_compare_func(owner_arg, item_cmp_type((*a)->result_type(), + (*b)->result_type())); } inline int set_cmp_func(Item_bool_func2 *owner_arg, Item **a1, Item **a2, @@ -60,11 +57,9 @@ public: inline int set_cmp_func(Item_bool_func2 *owner_arg, Item **a1, Item **a2) { - Item_result ar= (*a1)->result_as_longlong() && (*a2)->const_item() ? - INT_RESULT : (*a1)->result_type(); - Item_result br= (*a2)->result_as_longlong() && (*a1)->const_item() ? - INT_RESULT : (*a2)->result_type(); - return set_cmp_func(owner_arg, a1, a2, item_cmp_type(ar, br)); + return set_cmp_func(owner_arg, a1, a2, + item_cmp_type((*a1)->result_type(), + (*a2)->result_type())); } inline int compare() { return (this->*func)(); } From 0fb6b044e08790be9d42d8b16100a439c181e39d Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Thu, 15 Jun 2006 22:09:58 +0400 Subject: [PATCH 25/29] Fixed bug#18175: The nest_level counter wasn't decremented for union parts which resulted in a wrong error message. The nest_level counter indicates the depth of nesting for a subselect. It is needed to properly resolve aggregate functions in nested subselects. Obviously it shouldn't be incremented for UNION parts because they have the same level of nesting. This counter was incremented by 1 in the mysql_new_select() function for any new select and wasn't decremented for UNION parts. This resulted in wrongly reported error messages. Now the nest_level counter is decremented by 1 for any union part. --- mysql-test/r/union.result | 45 ++++++++++++++++++++++++++++++++++++ mysql-test/t/union.test | 48 +++++++++++++++++++++++++++++++++++++++ sql/sql_yacc.yy | 2 ++ 3 files changed, 95 insertions(+) diff --git a/mysql-test/r/union.result b/mysql-test/r/union.result index eb9e10aa58d..426387e04f5 100644 --- a/mysql-test/r/union.result +++ b/mysql-test/r/union.result @@ -1306,3 +1306,48 @@ id 5 99 drop table t1; +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)); +avg(1) +NULL diff --git a/mysql-test/t/union.test b/mysql-test/t/union.test index 692f1f509fa..7dfe4ac482f 100644 --- a/mysql-test/t/union.test +++ b/mysql-test/t/union.test @@ -793,3 +793,51 @@ select id from t1 union all select 99 order by 1; drop table t1; # End of 4.1 tests + +# +# Bug#18175: Union select over 129 tables with a sum function fails. +# +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)); + diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index b2dbc517fa4..4f3cf4d8554 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -8752,6 +8752,8 @@ union_list: yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; } + /* This counter shouldn't be incremented for UNION parts */ + Lex->nest_level--; if (mysql_new_select(lex, 0)) YYABORT; mysql_init_select(lex); From ee2e2d0c6d46d0d58e4bcff0b4c8289882e254b4 Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Thu, 15 Jun 2006 20:29:05 -0700 Subject: [PATCH 26/29] Post_merges fixes. --- mysql-test/r/func_group.result | 2 -- 1 file changed, 2 deletions(-) diff --git a/mysql-test/r/func_group.result b/mysql-test/r/func_group.result index 9f6189b558f..f693c6190d5 100644 --- a/mysql-test/r/func_group.result +++ b/mysql-test/r/func_group.result @@ -846,8 +846,6 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 2 DROP TABLE t1; CREATE TABLE t1 (id int , b varchar(512), INDEX(b(250))) COLLATE latin1_bin; -Warnings: -Warning 1246 Converting column 'b' from CHAR to TEXT INSERT INTO t1 VALUES (1,CONCAT(REPEAT('_', 250), "qq")), (1,CONCAT(REPEAT('_', 250), "zz")), (1,CONCAT(REPEAT('_', 250), "aa")), (1,CONCAT(REPEAT('_', 250), "ff")); From f2899063eebf2cdec1d01a057b1d1dccf9770d89 Mon Sep 17 00:00:00 2001 From: "ramil@mysql.com" <> Date: Fri, 16 Jun 2006 14:05:58 +0500 Subject: [PATCH 27/29] after-merge fixes. --- mysql-test/r/rpl_log.result | 10 +++++----- sql/ha_archive.cc | 2 +- sql/item_func.cc | 2 +- sql/sql_class.cc | 4 ++++ sql/sql_class.h | 3 ++- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/mysql-test/r/rpl_log.result b/mysql-test/r/rpl_log.result index 6a41a9b9e02..6ee0eb283b5 100644 --- a/mysql-test/r/rpl_log.result +++ b/mysql-test/r/rpl_log.result @@ -107,11 +107,11 @@ reset master; set insert_id=5; insert into t1 values (NULL, last_insert_id()), (NULL, last_insert_id()); show binlog events; -Log_name Pos Event_type Server_id Orig_log_pos Info -slave-bin.000001 4 Start 2 4 Server ver: VERSION, Binlog ver: 3 -slave-bin.000001 79 Intvar 2 79 LAST_INSERT_ID=1 -slave-bin.000001 107 Intvar 2 107 INSERT_ID=5 -slave-bin.000001 135 Query 2 135 use `test`; insert into t1 values (NULL, last_insert_id()), (NULL, last_insert_id()) +Log_name Pos Event_type Server_id End_log_pos Info +slave-bin.000001 4 Format_desc 2 98 Server ver: VERSION, Binlog ver: 4 +slave-bin.000001 98 Intvar 2 126 LAST_INSERT_ID=1 +slave-bin.000001 126 Intvar 2 154 INSERT_ID=5 +slave-bin.000001 154 Query 2 289 use `test`; insert into t1 values (NULL, last_insert_id()), (NULL, last_insert_id()) select * from t1; a b 1 1 diff --git a/sql/ha_archive.cc b/sql/ha_archive.cc index bfb90b607ee..3885defb4d5 100644 --- a/sql/ha_archive.cc +++ b/sql/ha_archive.cc @@ -1131,7 +1131,7 @@ int ha_archive::end_bulk_insert() int ha_archive::delete_all_rows() { DBUG_ENTER("ha_archive::delete_all_rows"); - DBUG_RETURN(0); + DBUG_RETURN(HA_ERR_WRONG_COMMAND); } /* diff --git a/sql/item_func.cc b/sql/item_func.cc index 2c770bec1ad..c75480aaeae 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3296,7 +3296,7 @@ longlong Item_func_last_insert_id::val_int() return value; // Avoid side effect of insert_id() } thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - return thd->insert_id(); + return thd->last_insert_id_used ? thd->current_insert_id : thd->insert_id(); } /* This function is just used to test speed of different functions */ diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 65fd4d3ac19..678048226af 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -2050,7 +2050,9 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup, backup->enable_slow_log= enable_slow_log; backup->last_insert_id= last_insert_id; backup->next_insert_id= next_insert_id; + backup->current_insert_id= current_insert_id; backup->insert_id_used= insert_id_used; + backup->last_insert_id_used= last_insert_id_used; backup->clear_next_insert_id= clear_next_insert_id; backup->limit_found_rows= limit_found_rows; backup->examined_row_count= examined_row_count; @@ -2099,7 +2101,9 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup) enable_slow_log= backup->enable_slow_log; last_insert_id= backup->last_insert_id; next_insert_id= backup->next_insert_id; + current_insert_id= backup->current_insert_id; insert_id_used= backup->insert_id_used; + last_insert_id_used= backup->last_insert_id_used; clear_next_insert_id= backup->clear_next_insert_id; limit_found_rows= backup->limit_found_rows; sent_row_count= backup->sent_row_count; diff --git a/sql/sql_class.h b/sql/sql_class.h index 0ddba0e6f05..2cfc9142453 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1051,12 +1051,13 @@ class Sub_statement_state { public: ulonglong options; - ulonglong last_insert_id, next_insert_id; + ulonglong last_insert_id, next_insert_id, current_insert_id; ulonglong limit_found_rows; ha_rows cuted_fields, sent_row_count, examined_row_count; ulong client_capabilities; uint in_sub_stmt; bool enable_slow_log, insert_id_used, clear_next_insert_id; + bool last_insert_id_used; my_bool no_send_ok; SAVEPOINT *savepoints; }; From b8b9738e0baabc5c299eb0363b9f1d61d2985021 Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Fri, 16 Jun 2006 23:46:37 +0400 Subject: [PATCH 28/29] item_strfunc.cc: Fix for bug#16716 for --ps-protocol mode. item_cmpfunc.cc: Fix for a memory allocation/freeing problem in agg_cmp_type() after fix for bug#16377. Few language corrections. --- sql/item_cmpfunc.cc | 32 +++++++++++--------------------- sql/item_strfunc.cc | 3 ++- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index e92e1d30ca4..126037a24d8 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -83,21 +83,21 @@ static void agg_result_type(Item_result *type, Item **items, uint nitems) items will be used for aggregation. If there are DATE/TIME fields/functions in the list and no string fields/functions in the list then: - The INT_RESULT type will be used for aggregation instead of orginal + The INT_RESULT type will be used for aggregation instead of original result type of any DATE/TIME field/function in the list All constant items in the list will be converted to a DATE/TIME using found field or result field of found function. Implementation notes: - The code is equvalent to: - 1. Check the list for presense of a STRING field/function. + The code is equivalent to: + 1. Check the list for presence of a STRING field/function. Collect the is_const flag. 2. Get a Field* object to use for type coercion 3. Perform type conversion. 1 and 2 are implemented in 2 loops. The first searches for a DATE/TIME - field/function and checks presense of a STRING field/function. + field/function and checks presence of a STRING field/function. The second loop works only if a DATE/TIME field/function is found. - It checks presense of a STRING field/function in the rest of the list. + It checks presence of a STRING field/function in the rest of the list. TODO 1) The current implementation can produce false comparison results for @@ -120,8 +120,9 @@ static void agg_result_type(Item_result *type, Item **items, uint nitems) static void agg_cmp_type(THD *thd, Item_result *type, Item **items, uint nitems) { uint i; - Item::Type res; - char *buff= NULL; + Item::Type res= (Item::Type)0; + /* Used only for date/time fields, max_length = 19 */ + char buff[20]; uchar null_byte; Field *field= NULL; @@ -147,28 +148,20 @@ static void agg_cmp_type(THD *thd, Item_result *type, Item **items, uint nitems) { field= items[i]->tmp_table_field_from_field_type(0); if (field) - buff= alloc_root(thd->mem_root, field->max_length()); - if (!buff || !field) - { - if (field) - delete field; - if (buff) - my_free(buff, MYF(MY_WME)); - field= 0; - } - else field->move_field(buff, &null_byte, 0); break; } } if (field) { - /* Check the rest of the list for presense of a string field/function. */ + /* Check the rest of the list for presence of a string field/function. */ for (i++ ; i < nitems; i++) { if (!items[i]->const_item() && items[i]->result_type() == STRING_RESULT && !items[i]->result_as_longlong()) { + if (res == Item::FUNC_ITEM) + delete field; field= 0; break; } @@ -205,10 +198,7 @@ static void agg_cmp_type(THD *thd, Item_result *type, Item **items, uint nitems) } if (res == Item::FUNC_ITEM && field) - { delete field; - my_free(buff, MYF(MY_WME)); - } } static void my_coll_agg_error(DTCollation &c1, DTCollation &c2, diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index deb3542f4a5..ee585649f8c 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -274,7 +274,8 @@ String *Item_func_concat::val_str(String *str) if (!(res=args[0]->val_str(str))) goto null; use_as_buff= &tmp_value; - is_const= args[0]->const_item(); + /* Item_subselect in --ps-protocol mode will state it as a non-const */ + is_const= args[0]->const_item() || !args[0]->used_tables(); for (i=1 ; i < arg_count ; i++) { if (res->length() == 0) From 59b204fe08be9c6f0a79497dcd1f8d36f635b068 Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Sat, 17 Jun 2006 02:52:14 +0400 Subject: [PATCH 29/29] select.result: After merge fix --- mysql-test/r/select.result | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index dceb0efe199..b385c576f2e 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -2720,6 +2720,16 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t3 index PRIMARY,a,b PRIMARY 8 NULL 2 Using index 1 SIMPLE t2 ALL PRIMARY NULL NULL NULL 2 Range checked for each record (index map: 0x1) DROP TABLE t1,t2,t3; +CREATE TABLE t1 (a int, INDEX idx(a)); +INSERT INTO t1 VALUES (2), (3), (1); +EXPLAIN SELECT * FROM t1 IGNORE INDEX (idx); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 +EXPLAIN SELECT * FROM t1 IGNORE INDEX (a); +ERROR HY000: Key 'a' doesn't exist in table 't1' +EXPLAIN SELECT * FROM t1 FORCE INDEX (a); +ERROR HY000: Key 'a' doesn't exist in table 't1' +DROP TABLE t1; CREATE TABLE t1 ( K2C4 varchar(4) character set latin1 collate latin1_bin NOT NULL default '', K4N4 varchar(4) character set latin1 collate latin1_bin NOT NULL default '0000',