From f372641876e7a560ce661693bfc5d8e62cc5a19d Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 6 Mar 2006 16:38:35 +0400 Subject: [PATCH 01/20] Fix for bug #17896: MIN of CASE WHEN returns non-minimum value! sql/item_cmpfunc.cc: Fix for bug #17896: MIN of CASE WHEN returns non-minimum value! - NULL items should not affect the result type. --- mysql-test/r/case.result | 8 ++++++++ mysql-test/t/case.test | 11 +++++++++++ sql/item_cmpfunc.cc | 19 +++++++++++++++---- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/case.result b/mysql-test/r/case.result index fb0bc19a67e..a5495d0fc3e 100644 --- a/mysql-test/r/case.result +++ b/mysql-test/r/case.result @@ -169,3 +169,11 @@ SELECT CASE '1' WHEN '2' THEN 'BUG' ELSE 'nobug' END; case+union+test case+union+test nobug +create table t1(a float, b int default 3); +insert into t1 (a) values (2), (11), (8); +select min(a), min(case when 1=1 then a else NULL end), +min(case when 1!=1 then NULL else a end) +from t1 where b=3 group by b; +min(a) min(case when 1=1 then a else NULL end) min(case when 1!=1 then NULL else a end) +2 2 2 +drop table t1; diff --git a/mysql-test/t/case.test b/mysql-test/t/case.test index fbbbce15576..fd1b6e5247f 100644 --- a/mysql-test/t/case.test +++ b/mysql-test/t/case.test @@ -119,4 +119,15 @@ SELECT 'case+union+test' UNION SELECT CASE '1' WHEN '2' THEN 'BUG' ELSE 'nobug' END; +# +# Bug #17896: problem with MIN(CASE...) +# + +create table t1(a float, b int default 3); +insert into t1 (a) values (2), (11), (8); +select min(a), min(case when 1=1 then a else NULL end), + min(case when 1!=1 then NULL else a end) +from t1 where b=3 group by b; +drop table t1; + # End of 4.1 tests diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index f9aa220c181..57a3c9e5623 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -37,10 +37,21 @@ static Item_result item_store_type(Item_result a,Item_result b) static void agg_result_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_store_type(type[0], items[i]->result_type()); + Item **item, **item_end; + + /* Note: NULL items don't affect the result type */ + *type= STRING_RESULT; + /* Skip beginning NULL items */ + for (item= items, item_end= item + nitems; item < item_end; item++) + if ((*item)->type() != Item::NULL_ITEM) + { + *type= (*item)->result_type(); + item++; + break; + } + for (; item < item_end; item++) + if ((*item)->type() != Item::NULL_ITEM) + *type= item_store_type(type[0], (*item)->result_type()); } static void agg_cmp_type(Item_result *type, Item **items, uint nitems) From 9cc12d65cd6d57789678ce57f6ac64806d3ca56c Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 6 Apr 2006 21:59:13 +1000 Subject: [PATCH 02/20] BUG#18843 valgrind: cond jump on uninit in Qmgr::execAPI_REGREQ ndb/src/kernel/blocks/ndbcntr/NdbcntrInit.cpp: Also init a few internals before start phase 1 as they can be accessed before that --- ndb/src/kernel/blocks/ndbcntr/NdbcntrInit.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ndb/src/kernel/blocks/ndbcntr/NdbcntrInit.cpp b/ndb/src/kernel/blocks/ndbcntr/NdbcntrInit.cpp index 97ca3f44b3a..4859ca22221 100644 --- a/ndb/src/kernel/blocks/ndbcntr/NdbcntrInit.cpp +++ b/ndb/src/kernel/blocks/ndbcntr/NdbcntrInit.cpp @@ -25,7 +25,10 @@ void Ndbcntr::initData() { - + c_start.reset(); + cmasterNodeId = 0; + cnoStartNodes = 0; + cnoWaitrep = 0; // Records with constant sizes ndbBlocksRec = new NdbBlocksRec[ZSIZE_NDB_BLOCKS_REC]; }//Ndbcntr::initData() From 3af116f18795e8446c0baa460989be3af4512b53 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 6 Apr 2006 21:59:20 +1000 Subject: [PATCH 03/20] BUG#18833 valgrind: Mismatched free()/delete/delete[] in SimBlockList ndb/src/kernel/SimBlockList.cpp: NEW_BLOCK allocates the elements with new, we should free them with delete but only if we're not building with VM_TRACE --- ndb/src/kernel/SimBlockList.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ndb/src/kernel/SimBlockList.cpp b/ndb/src/kernel/SimBlockList.cpp index 6029fc7e225..271d515dc92 100644 --- a/ndb/src/kernel/SimBlockList.cpp +++ b/ndb/src/kernel/SimBlockList.cpp @@ -111,8 +111,12 @@ SimBlockList::unload(){ if(theList != 0){ for(int i = 0; i~SimulatedBlock(); free(theList[i]); +#else + delete(theList[i]); +#endif theList[i] = 0; } } From a2066982f1983e2675533d2f2db28fbaf77d2c24 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 12 Apr 2006 23:05:38 +0400 Subject: [PATCH 04/20] Fixed bug#14169: type of group_concat() result changed to blob if tmp_table was used In a simple queries a result of the GROUP_CONCAT() function was always of varchar type. But if length of GROUP_CONCAT() result is greater than 512 chars and temporary table is used during select then the result is converted to blob, due to policy to not to store fields longer than 512 chars in tmp table as varchar fields. In order to provide consistent behaviour, result of GROUP_CONCAT() now will always be converted to blob if it is longer than 512 chars. Item_func_group_concat::field_type() is modified accordingly. mysql-test/t/func_gconcat.test: Added test case for bug#14169: type of group_concat() result changed to blob if tmp_table was used mysql-test/r/func_gconcat.result: Added test case for bug#14169: type of group_concat() result changed to blob if tmp_table was used sql/unireg.h: Added the CONVERT_IF_BIGGER_TO_BLOB constant sql/sql_select.cc: Fixed bug#14169: type of group_concat() result changed to blob if tmp_table was used The unnamed constant 255 in the create_tmp_field() and create_tmp_field_from_item() functions now defined as the CONVERT_IF_BIGGER_TO_BLOB constant. The create_tmp_field() function now converts the Item_sum string result to a blob field based on its char length. sql/item_sum.h: Fixed bug#14169: type of group_concat() result changed to blob if tmp_table was used To the Item_func_group_concat calls added the member function field_type() which returns the BLOB or VAR_STRING type based on the items length. sql/item_func.cc: Fixed bug#14169: type of group_concat() result changed to blob if tmp_table was used In the Item_func::tmp_table_field() function the unnamed constant 255 is changed to the CONVERT_IF_BIGGER_TO_BLOB constant. The Item_func::tmp_table_field() function now measures the result length in chars rather than bytes when converting string result to a blob. --- mysql-test/r/func_gconcat.result | 16 ++++++++++++++++ mysql-test/t/func_gconcat.test | 10 ++++++++++ sql/item_func.cc | 2 +- sql/item_sum.h | 7 +++++++ sql/sql_select.cc | 6 ++++-- sql/unireg.h | 1 + 6 files changed, 39 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/func_gconcat.result b/mysql-test/r/func_gconcat.result index 3cb8da7f63f..ef46d170bda 100644 --- a/mysql-test/r/func_gconcat.result +++ b/mysql-test/r/func_gconcat.result @@ -604,3 +604,19 @@ count(distinct (f1+1)) 1 3 drop table t1; +create table t1 (f1 int unsigned, f2 varchar(255)); +insert into t1 values (1,repeat('a',255)),(2,repeat('b',255)); +select f2,group_concat(f1) from t1 group by f2; +Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr +def test t1 t1 f2 f2 253 255 255 Y 0 0 8 +def group_concat(f1) 252 400 1 Y 128 0 63 +f2 group_concat(f1) +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 1 +bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 2 +select f2,group_concat(f1) from t1 group by f2 order by 2; +Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr +def test t1 t1 f2 f2 253 255 255 Y 0 0 8 +def group_concat(f1) group_concat(f1) 252 400 1 Y 144 0 63 +f2 group_concat(f1) +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 1 +bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 2 diff --git a/mysql-test/t/func_gconcat.test b/mysql-test/t/func_gconcat.test index f116aa6f164..1bb49a312b8 100644 --- a/mysql-test/t/func_gconcat.test +++ b/mysql-test/t/func_gconcat.test @@ -390,4 +390,14 @@ insert into t1 values(1),(2),(3); select f1, group_concat(f1+1) from t1 group by f1 with rollup; select count(distinct (f1+1)) from t1 group by f1 with rollup; drop table t1; + +# +# Bug#14169 type of group_concat() result changed to blob if tmp_table was used +# +create table t1 (f1 int unsigned, f2 varchar(255)); +insert into t1 values (1,repeat('a',255)),(2,repeat('b',255)); +--enable_metadata +select f2,group_concat(f1) from t1 group by f2; +select f2,group_concat(f1) from t1 group by f2 order by 2; +--disable_metadata # End of 4.1 tests diff --git a/sql/item_func.cc b/sql/item_func.cc index d0d2eca7233..174a8c55d01 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -307,7 +307,7 @@ Field *Item_func::tmp_table_field(TABLE *t_arg) res= new Field_double(max_length, maybe_null, name, t_arg, decimals); break; case STRING_RESULT: - if (max_length > 255) + if (max_length/collation.collation->mbmaxlen > CONVERT_IF_BIGGER_TO_BLOB) res= new Field_blob(max_length, maybe_null, name, t_arg, collation.collation); else res= new Field_string(max_length, maybe_null, name, t_arg, collation.collation); diff --git a/sql/item_sum.h b/sql/item_sum.h index c972240fcba..0cc2a20faa3 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -729,6 +729,13 @@ class Item_func_group_concat : public Item_sum enum Sumfunctype sum_func () const {return GROUP_CONCAT_FUNC;} const char *func_name() const { return "group_concat"; } virtual Item_result result_type () const { return STRING_RESULT; } + enum_field_types field_type() const + { + if (max_length/collation.collation->mbmaxlen > CONVERT_IF_BIGGER_TO_BLOB) + return FIELD_TYPE_BLOB; + else + return MYSQL_TYPE_VAR_STRING; + } void clear(); bool add(); void reset_field(); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index d7aa617cffd..91fc808058f 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -4925,7 +4925,8 @@ static Field* create_tmp_field_from_item(THD *thd, Item *item, TABLE *table, if ((type= item->field_type()) == MYSQL_TYPE_DATETIME || type == MYSQL_TYPE_TIME || type == MYSQL_TYPE_DATE) new_field= item->tmp_table_field_from_field_type(table); - else if (item->max_length/item->collation.collation->mbmaxlen > 255) + else if (item->max_length/item->collation.collation->mbmaxlen > + CONVERT_IF_BIGGER_TO_BLOB) { if (convert_blob_length) new_field= new Field_varstring(convert_blob_length, maybe_null, @@ -5028,7 +5029,8 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, return new Field_longlong(item_sum->max_length,maybe_null, item->name,table,item->unsigned_flag); case STRING_RESULT: - if (item_sum->max_length > 255) + if (item_sum->max_length/item_sum->collation.collation->mbmaxlen > + CONVERT_IF_BIGGER_TO_BLOB) { if (convert_blob_length) return new Field_varstring(convert_blob_length, maybe_null, diff --git a/sql/unireg.h b/sql/unireg.h index 70df9a89c8f..3fb30315c81 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -60,6 +60,7 @@ #define MAX_MBWIDTH 3 /* Max multibyte sequence */ #define MAX_FIELD_CHARLENGTH 255 +#define CONVERT_IF_BIGGER_TO_BLOB 255 /* Max column width +1 */ #define MAX_FIELD_WIDTH (MAX_FIELD_CHARLENGTH*MAX_MBWIDTH+1) From 5c0c1dcc3d7f46f30d144e80fc88a3ef1a4bcd1e Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 13 Apr 2006 10:55:48 +0500 Subject: [PATCH 05/20] Bug#18691: Converting number to UNICODE string returns invalid result. Conversion from int and real numbers to UCS2 didn't work fine: CONVERT(100, CHAR(50) UNICODE) CONVERT(103.9, CHAR(50) UNICODE) The problem appeared because numbers have binary charset, so, simple charset recast binary->ucs2 was performed instead of real conversion. Fixed to make numbers pretend to be non-binary. mysql-test/r/ctype_ucs.result: Adding test case mysql-test/t/ctype_ucs.test: Adding test case sql/item_timefunc.cc: Adding new member from_cs, to replace my_charset_bin to a non-binary charset when converting from numbers to UCS2 sql/item_timefunc.h: Adding new member from_cs, to replace my_charset_bin to a non-binary charset when converting from numbers to UCS2 --- mysql-test/r/ctype_ucs.result | 12 ++++++++++++ mysql-test/t/ctype_ucs.test | 8 ++++++++ sql/item_timefunc.cc | 28 +++++++++++++++++++++++----- sql/item_timefunc.h | 2 +- 4 files changed, 44 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/ctype_ucs.result b/mysql-test/r/ctype_ucs.result index 0e12ec88662..8869604b128 100644 --- a/mysql-test/r/ctype_ucs.result +++ b/mysql-test/r/ctype_ucs.result @@ -666,6 +666,18 @@ Warnings: Warning 1265 Data truncated for column 'Field1' at row 1 DROP TABLE t1; SET NAMES latin1; +SELECT CONVERT(103, CHAR(50) UNICODE); +CONVERT(103, CHAR(50) UNICODE) +103 +SELECT CONVERT(103.0, CHAR(50) UNICODE); +CONVERT(103.0, CHAR(50) UNICODE) +103.0 +SELECT CONVERT(-103, CHAR(50) UNICODE); +CONVERT(-103, CHAR(50) UNICODE) +-103 +SELECT CONVERT(-103.0, CHAR(50) UNICODE); +CONVERT(-103.0, CHAR(50) UNICODE) +-103.0 CREATE TABLE t1 ( a varchar(255) NOT NULL default '', KEY a (a) diff --git a/mysql-test/t/ctype_ucs.test b/mysql-test/t/ctype_ucs.test index bd37d008173..2cbabf88ee0 100644 --- a/mysql-test/t/ctype_ucs.test +++ b/mysql-test/t/ctype_ucs.test @@ -407,6 +407,14 @@ INSERT INTO t1 VALUES ('-1'); DROP TABLE t1; SET NAMES latin1; +# +# Bug#18691 Converting number to UNICODE string returns invalid result +# +SELECT CONVERT(103, CHAR(50) UNICODE); +SELECT CONVERT(103.0, CHAR(50) UNICODE); +SELECT CONVERT(-103, CHAR(50) UNICODE); +SELECT CONVERT(-103.0, CHAR(50) UNICODE); + # # Bug#9557 MyISAM utf8 table crash # diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index d6b57464d59..f3d6858755c 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -2221,8 +2221,8 @@ String *Item_char_typecast::val_str(String *str) // Convert character set if differ uint dummy_errors; if (!(res= args[0]->val_str(&tmp_value)) || - str->copy(res->ptr(), res->length(), res->charset(), - cast_cs, &dummy_errors)) + str->copy(res->ptr(), res->length(), from_cs, + cast_cs, &dummy_errors)) { null_value= 1; return 0; @@ -2261,14 +2261,32 @@ void Item_char_typecast::fix_length_and_dec() For single-byte character sets we allow just to copy from the argument. A single-byte character sets string is always well-formed. + + There is a special trick to convert form a number to ucs2. + As numbers have my_charset_bin as their character set, + it wouldn't do conversion to ucs2 without an additional action. + To force conversion, we should pretend to be non-binary. + Let's choose from_cs this way: + - If the argument in a number and cast_cs is ucs2 (i.e. mbminlen > 1), + then from_cs is set to latin1, to perform latin1 -> ucs2 conversion. + - If the argument is a number and cast_cs is ASCII-compatible + (i.e. mbminlen == 1), then from_cs is set to cast_cs, + which allows just to take over the args[0]->val_str() result + and thus avoid unnecessary character set conversion. + - If the argument is not a number, then from_cs is set to + the argument's charset. */ + from_cs= (args[0]->result_type() == INT_RESULT || + args[0]->result_type() == REAL_RESULT) ? + (cast_cs->mbminlen == 1 ? cast_cs : &my_charset_latin1) : + args[0]->collation.collation; charset_conversion= (cast_cs->mbmaxlen > 1) || - !my_charset_same(args[0]->collation.collation, cast_cs) && - args[0]->collation.collation != &my_charset_bin && + !my_charset_same(from_cs, cast_cs) && + from_cs != &my_charset_bin && cast_cs != &my_charset_bin; collation.set(cast_cs, DERIVATION_IMPLICIT); char_length= (cast_length >= 0) ? cast_length : - args[0]->max_length/args[0]->collation.collation->mbmaxlen; + args[0]->max_length/from_cs->mbmaxlen; max_length= char_length * cast_cs->mbmaxlen; } diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index ce9d6b0a7aa..163b1591e52 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -681,7 +681,7 @@ public: class Item_char_typecast :public Item_typecast { int cast_length; - CHARSET_INFO *cast_cs; + CHARSET_INFO *cast_cs, *from_cs; bool charset_conversion; String tmp_value; public: From d316b8b10b3b430efef3d429b1588c93e1667e67 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 13 Apr 2006 14:37:03 +0500 Subject: [PATCH 06/20] BUG#17917 - SELECT from compressed MyISAM table crashes MySQL server Retrieving data from compressed MyISAM table which is bigger than 4G on 32-bit box with mmap() support results in server crash. mmap() accepts length of bytes to be mapped in second param, which is 32-bit size_t. But we pass data_file_length, which is 64-bit my_off_t. As a result only first data_file_length % 4G were mapped. This fix adds additional condition for mmap() usage, that is use mmap() for compressed table which size is no more than 4G on 32-bit platform. myisam/mi_packrec.c: Use mmap() for compressed table which size is no more than 4G on 32-bit platform. --- myisam/mi_packrec.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/myisam/mi_packrec.c b/myisam/mi_packrec.c index 322420b71db..bd2d162d100 100644 --- a/myisam/mi_packrec.c +++ b/myisam/mi_packrec.c @@ -1158,16 +1158,22 @@ my_bool _mi_memmap_file(MI_INFO *info) MYISAM_SHARE *share=info->s; DBUG_ENTER("mi_memmap_file"); - if (!info->s->file_map) + if (!share->file_map) { + my_off_t data_file_length= share->state.state.data_file_length; + if (data_file_length > (my_off_t) (~((size_t) 0)) - MEMMAP_EXTRA_MARGIN) + { + DBUG_PRINT("warning", ("File is too large for mmap")); + DBUG_RETURN(0); + } if (my_seek(info->dfile,0L,MY_SEEK_END,MYF(0)) < - share->state.state.data_file_length+MEMMAP_EXTRA_MARGIN) + data_file_length + MEMMAP_EXTRA_MARGIN) { DBUG_PRINT("warning",("File isn't extended for memmap")); DBUG_RETURN(0); } file_map=(byte*) - mmap(0,share->state.state.data_file_length+MEMMAP_EXTRA_MARGIN,PROT_READ, + mmap(0, data_file_length + MEMMAP_EXTRA_MARGIN, PROT_READ, MAP_SHARED | MAP_NORESERVE,info->dfile,0L); if (file_map == (byte*) MAP_FAILED) { @@ -1175,7 +1181,7 @@ my_bool _mi_memmap_file(MI_INFO *info) my_errno=errno; DBUG_RETURN(0); } - info->s->file_map=file_map; + share->file_map= file_map; } info->opt_flag|= MEMMAP_USED; info->read_record=share->read_record=_mi_read_mempack_record; From 403e6783f5c3f57f9dd56af0f5f7d583e91d417e Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 13 Apr 2006 14:06:46 +0400 Subject: [PATCH 07/20] Fix a typo in the help message. sql/mysqld.cc: Fix a typo. --- sql/mysqld.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index e68762868a4..740e1a419c7 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -5253,7 +5253,7 @@ The minimum value for this variable is 4096.", (gptr*) &max_system_variables.max_length_for_sort_data, 0, GET_ULONG, REQUIRED_ARG, 1024, 4, 8192*1024L, 0, 1, 0}, {"max_prepared_stmt_count", OPT_MAX_PREPARED_STMT_COUNT, - "Maximum numbrer of prepared statements in the server.", + "Maximum number of prepared statements in the server.", (gptr*) &max_prepared_stmt_count, (gptr*) &max_prepared_stmt_count, 0, GET_ULONG, REQUIRED_ARG, 16382, 0, 1*1024*1024, 0, 1, 0}, {"max_relay_log_size", OPT_MAX_RELAY_LOG_SIZE, From 5c87b55f7e48780d59f32b2867b119ae732c6680 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 13 Apr 2006 16:19:21 +0500 Subject: [PATCH 08/20] Fix for bug #18643: crazy UNCOMPRESS(). mysql-test/r/func_compress.result: Fix for bug #18643: crazy UNCOMPRESS(). - test-case mysql-test/t/func_compress.test: Fix for bug #18643: crazy UNCOMPRESS(). - test-case sql/item_strfunc.cc: Fix for bug #18643: crazy UNCOMPRESS(). - set null_value. --- mysql-test/r/func_compress.result | 7 +++++++ mysql-test/t/func_compress.test | 9 +++++++++ sql/item_strfunc.cc | 1 + 3 files changed, 17 insertions(+) diff --git a/mysql-test/r/func_compress.result b/mysql-test/r/func_compress.result index 9bc8e417e19..8d6fa9927ce 100644 --- a/mysql-test/r/func_compress.result +++ b/mysql-test/r/func_compress.result @@ -72,3 +72,10 @@ set @@max_allowed_packet=1048576*100; select compress(repeat('aaaaaaaaaa', IF(XXX, 10, 10000000))) is null; compress(repeat('aaaaaaaaaa', IF(XXX, 10, 10000000))) is null 0 +create table t1(a blob); +insert into t1 values(NULL), (compress('a')); +select uncompress(a), uncompressed_length(a) from t1; +uncompress(a) uncompressed_length(a) +NULL NULL +a 1 +drop table t1; diff --git a/mysql-test/t/func_compress.test b/mysql-test/t/func_compress.test index 6b85062b9fa..0f3c3cab307 100644 --- a/mysql-test/t/func_compress.test +++ b/mysql-test/t/func_compress.test @@ -45,4 +45,13 @@ set @@max_allowed_packet=1048576*100; --replace_result "''" XXX "'1'" XXX eval select compress(repeat('aaaaaaaaaa', IF('$LOW_MEMORY', 10, 10000000))) is null; +# +# Bug #18643: problem with null values +# + +create table t1(a blob); +insert into t1 values(NULL), (compress('a')); +select uncompress(a), uncompressed_length(a) from t1; +drop table t1; + # End of 4.1 tests diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 04765e18191..f0127ed2a5d 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -2850,6 +2850,7 @@ String *Item_func_uncompress::val_str(String *str) if (!res) goto err; + null_value= 0; if (res->is_empty()) return res; From f1a5003548b3f119b6ef00f87f60cab6efafc4ec Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 13 Apr 2006 20:42:48 +0200 Subject: [PATCH 09/20] Fixes for Bug#12429: Replication tests fail: "Slave_IO_Running" (?) differs related to MySQL 4.1 and Bug#16920 rpl_deadlock_innodb fails in show slave status (reported for MySQL 5.1) - backport of several fixes done in MySQL 5.0 to 4.1 - fix for new discovered instability (see comment on Bug#12429 + Bug#16920) - reenabling of testcases mysql-test/r/rpl_deadlock.result: Updated results mysql-test/r/rpl_relayrotate.result: Updated results mysql-test/t/disabled.def: Reenabling of tests mysql-test/t/rpl_deadlock.test: - replace sleep with real_sleep (backport fix for Bug#15624 MySQL 5.0) - egalized value for Slave_IO_Running - line 105 (backport fix for Bug#12429 MySQL 5.0) - line 85 (see comment in Bug#12429 + Bug#16920 rpl_deadlock_innodb fails in show slave status) - improve readability of show slave status output (--vertical_results) mysql-test/t/rpl_relayrotate.test: - Replace select ... with select max(a) - add sync_with_master (backport fix done by Kristian in MySQL 5.0 because of timing problems similar to Bug#15624) mysql-test/t/rpl_until.test: Add wait_for_slave_to_stop like in the current MySQL 5.0 version of this test. I assume this makes the test results more predictable. --- mysql-test/r/rpl_deadlock.result | 105 ++++++++++++++++++++++++++-- mysql-test/r/rpl_relayrotate.result | 7 +- mysql-test/t/disabled.def | 3 - mysql-test/t/rpl_deadlock.test | 16 +++-- mysql-test/t/rpl_relayrotate.test | 8 +-- mysql-test/t/rpl_until.test | 4 ++ 6 files changed, 119 insertions(+), 24 deletions(-) diff --git a/mysql-test/r/rpl_deadlock.result b/mysql-test/r/rpl_deadlock.result index 8eb9d64fcae..db88cf73c03 100644 --- a/mysql-test/r/rpl_deadlock.result +++ b/mysql-test/r/rpl_deadlock.result @@ -44,8 +44,39 @@ select * from t2; a 22 show slave status; -Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master -# 127.0.0.1 root MASTER_MYPORT 1 master-bin.000001 13110 # # master-bin.000001 Yes Yes 0 0 13110 # None 0 No # +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_MYPORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos 13110 +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running Yes +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 0 +Last_Error +Skip_Counter 0 +Exec_Master_Log_Pos 13110 +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # stop slave; change master to master_log_pos=401; begin; @@ -62,8 +93,39 @@ select * from t2; a 22 show slave status; -Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master -# 127.0.0.1 root MASTER_MYPORT 1 master-bin.000001 13110 # # master-bin.000001 Yes Yes 0 0 13110 # None 0 No # +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_MYPORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos 13110 +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running # +Slave_SQL_Running Yes +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 0 +Last_Error +Skip_Counter 0 +Exec_Master_Log_Pos 13110 +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # set global max_relay_log_size=0; stop slave; change master to master_log_pos=401; @@ -82,6 +144,37 @@ select * from t2; a 22 show slave status; -Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master -# 127.0.0.1 root MASTER_MYPORT 1 master-bin.000001 13110 # # master-bin.000001 Yes Yes 0 0 13110 # None 0 No # +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_MYPORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos 13110 +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running # +Slave_SQL_Running Yes +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 0 +Last_Error +Skip_Counter 0 +Exec_Master_Log_Pos 13110 +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # drop table t1,t2,t3,t4; diff --git a/mysql-test/r/rpl_relayrotate.result b/mysql-test/r/rpl_relayrotate.result index bf9bbbb9b93..20f19973d83 100644 --- a/mysql-test/r/rpl_relayrotate.result +++ b/mysql-test/r/rpl_relayrotate.result @@ -10,10 +10,7 @@ reset slave; start slave; stop slave; start slave; -select master_pos_wait('master-bin.001',3000)>=0; -master_pos_wait('master-bin.001',3000)>=0 -1 -select * from t1 where a=8000; -a +select max(a) from t1; +max(a) 8000 drop table t1; diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index 3eae8d230d8..9bfe9567d83 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -10,6 +10,3 @@ # ############################################################################## -rpl_relayrotate : Unstable test case, bug#12429 -rpl_until : Unstable test case, bug#12429 -rpl_deadlock : Unstable test case, bug#12429 diff --git a/mysql-test/t/rpl_deadlock.test b/mysql-test/t/rpl_deadlock.test index d3bc31addff..e8ba6d6faec 100644 --- a/mysql-test/t/rpl_deadlock.test +++ b/mysql-test/t/rpl_deadlock.test @@ -58,7 +58,7 @@ while ($1) enable_query_log; select * from t1 for update; start slave; ---sleep 3 # hope that slave is blocked now +--real_sleep 3 # hope that slave is blocked now insert into t2 values(22); # provoke deadlock, slave should be victim commit; sync_with_master; @@ -67,7 +67,9 @@ select * from t2; # check that no error is reported --replace_column 1 # 8 # 9 # 23 # 33 # --replace_result $MASTER_MYPORT MASTER_MYPORT +--vertical_results show slave status; +--horizontal_results # 2) Test lock wait timeout @@ -76,15 +78,17 @@ change master to master_log_pos=401; # the BEGIN log event begin; select * from t2 for update; # hold lock start slave; ---sleep 10 # slave should have blocked, and be retrying +--real_sleep 10 # slave should have blocked, and be retrying commit; sync_with_master; select * from t1; # check that slave succeeded finally select * from t2; # check that no error is reported ---replace_column 1 # 8 # 9 # 23 # 33 # +--replace_column 1 # 8 # 9 # 11 # 23 # 33 # --replace_result $MASTER_MYPORT MASTER_MYPORT +--vertical_results show slave status; +--horizontal_results # Now we repeat 2), but with BEGIN in the same relay log as # COMMIT (to see if seeking into hot log is ok). @@ -97,14 +101,16 @@ change master to master_log_pos=401; begin; select * from t2 for update; start slave; ---sleep 10 +--real_sleep 10 commit; sync_with_master; select * from t1; select * from t2; ---replace_column 1 # 8 # 9 # 23 # 33 # +--replace_column 1 # 8 # 9 # 11 # 23 # 33 # --replace_result $MASTER_MYPORT MASTER_MYPORT +--vertical_results show slave status; +--horizontal_results connection master; drop table t1,t2,t3,t4; diff --git a/mysql-test/t/rpl_relayrotate.test b/mysql-test/t/rpl_relayrotate.test index 1dab8bf4ed5..04f03367e20 100644 --- a/mysql-test/t/rpl_relayrotate.test +++ b/mysql-test/t/rpl_relayrotate.test @@ -52,11 +52,9 @@ start slave; # which proves that the transaction restarted at # the right place. # We must wait for the transaction to commit before -# reading, MASTER_POS_WAIT() will do it for sure -# (the only statement with position>=3000 is COMMIT). -select master_pos_wait('master-bin.001',3000)>=0; -select * from t1 where a=8000; - +# reading: +sync_with_master; +select max(a) from t1; connection master; # The following DROP is a very important cleaning task: diff --git a/mysql-test/t/rpl_until.test b/mysql-test/t/rpl_until.test index 57ebc67db1d..990f00fdc63 100644 --- a/mysql-test/t/rpl_until.test +++ b/mysql-test/t/rpl_until.test @@ -26,6 +26,7 @@ show binlog events; connection slave; start slave until master_log_file='master-bin.000001', master_log_pos=244; sleep 2; +wait_for_slave_to_stop; # here table should be still not deleted select * from t1; --replace_result $MASTER_MYPORT MASTER_MYPORT @@ -37,6 +38,7 @@ start slave until master_log_file='master-no-such-bin.000001', master_log_pos=29 # again this table should be still not deleted select * from t1; sleep 2; +wait_for_slave_to_stop; --replace_result $MASTER_MYPORT MASTER_MYPORT --replace_column 1 # 9 # 11 # 23 # 33 # show slave status; @@ -44,6 +46,7 @@ show slave status; # try replicate all until second insert to t2; start slave until relay_log_file='slave-relay-bin.000002', relay_log_pos=537; sleep 4; +wait_for_slave_to_stop; select * from t2; --replace_result $MASTER_MYPORT MASTER_MYPORT --replace_column 1 # 9 # 11 # 23 # 33 # @@ -61,6 +64,7 @@ stop slave; start slave until master_log_file='master-bin.000001', master_log_pos=561; # 2 is not enough when running with valgrind --real_sleep 4 +wait_for_slave_to_stop; # here the sql slave thread should be stopped --replace_result $MASTER_MYPORT MASTER_MYPORT bin.000005 bin.000004 bin.000006 bin.000004 bin.000007 bin.000004 --replace_column 1 # 9 # 23 # 33 # From b97e48a95b91385a4729e767ee7aae1eece5c67f Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 18 Apr 2006 20:57:31 -0700 Subject: [PATCH 10/20] Fixed bug #19079. The bug caused a reported index corruption in the cases when key_cache_block_size was not a multiple of myisam_block_size, e.g. when key_cache_block_size=1536 while myisam_block_size=1024. mysql-test/r/key_cache.result: Added a test case for bug #19079. mysql-test/t/key_cache.test: Added a test case for bug #19079. --- mysql-test/r/key_cache.result | 42 +++++++++++++++++++++++++++++++++++ mysql-test/t/key_cache.test | 41 +++++++++++++++++++++++++++++++++- mysys/mf_keycache.c | 12 +++++----- 3 files changed, 89 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/key_cache.result b/mysql-test/r/key_cache.result index 41aaa21eea3..15dc244693d 100644 --- a/mysql-test/r/key_cache.result +++ b/mysql-test/r/key_cache.result @@ -287,3 +287,45 @@ check table t1; Table Op Msg_type Msg_text test.t1 check status OK drop table t1; +CREATE TABLE t1(a int NOT NULL AUTO_INCREMENT PRIMARY KEY); +SET GLOBAL key_cache_block_size=1536; +INSERT INTO t1 VALUES (1); +SELECT @@key_cache_block_size; +@@key_cache_block_size +1536 +CHECK TABLE t1; +Table Op Msg_type Msg_text +test.t1 check status OK +DROP TABLE t1; +CREATE TABLE t1(a int NOT NULL AUTO_INCREMENT PRIMARY KEY, b int); +CREATE TABLE t2(a int NOT NULL AUTO_INCREMENT PRIMARY KEY, b int); +SET GLOBAL key_cache_block_size=1536; +INSERT INTO t1 VALUES (1,0); +INSERT INTO t2(b) SELECT b FROM t1; +INSERT INTO t1(b) SELECT b FROM t2; +INSERT INTO t2(b) SELECT b FROM t1; +INSERT INTO t1(b) SELECT b FROM t2; +INSERT INTO t2(b) SELECT b FROM t1; +INSERT INTO t1(b) SELECT b FROM t2; +INSERT INTO t2(b) SELECT b FROM t1; +INSERT INTO t1(b) SELECT b FROM t2; +INSERT INTO t2(b) SELECT b FROM t1; +INSERT INTO t1(b) SELECT b FROM t2; +INSERT INTO t2(b) SELECT b FROM t1; +INSERT INTO t1(b) SELECT b FROM t2; +INSERT INTO t2(b) SELECT b FROM t1; +INSERT INTO t1(b) SELECT b FROM t2; +INSERT INTO t2(b) SELECT b FROM t1; +INSERT INTO t1(b) SELECT b FROM t2; +INSERT INTO t2(b) SELECT b FROM t1; +INSERT INTO t1(b) SELECT b FROM t2; +SELECT COUNT(*) FROM t1; +COUNT(*) +4181 +SELECT @@key_cache_block_size; +@@key_cache_block_size +1536 +CHECK TABLE t1; +Table Op Msg_type Msg_text +test.t1 check status OK +DROP TABLE t1,t2; diff --git a/mysql-test/t/key_cache.test b/mysql-test/t/key_cache.test index 5d0f904a716..6e772a7a9ad 100644 --- a/mysql-test/t/key_cache.test +++ b/mysql-test/t/key_cache.test @@ -149,6 +149,7 @@ show status like 'key_blocks_used'; --replace_result 1812 KEY_BLOCKS_UNUSED 1793 KEY_BLOCKS_UNUSED 1674 KEY_BLOCKS_UNUSED 1818 KEY_BLOCKS_UNUSED 1824 KEY_BLOCKS_UNUSED show status like 'key_blocks_unused'; + # Cleanup # We don't reset keycache2 as we want to ensure that mysqld will reset it set global keycache2.key_buffer_size=0; @@ -157,7 +158,7 @@ set global keycache2.key_buffer_size=0; set global keycache3.key_buffer_size=100; set global keycache3.key_buffer_size=0; -# Test case for buf 6447 +# Test case for bug 6447 create table t1 (mytext text, FULLTEXT (mytext)); insert t1 values ('aaabbb'); @@ -168,4 +169,42 @@ check table t1; drop table t1; +# +# Bug #19079: corrupted index when key_cache_block_size is not multiple of +# myisam_block_size + +CREATE TABLE t1(a int NOT NULL AUTO_INCREMENT PRIMARY KEY); +SET GLOBAL key_cache_block_size=1536; +INSERT INTO t1 VALUES (1); +SELECT @@key_cache_block_size; +CHECK TABLE t1; +DROP TABLE t1; + +CREATE TABLE t1(a int NOT NULL AUTO_INCREMENT PRIMARY KEY, b int); +CREATE TABLE t2(a int NOT NULL AUTO_INCREMENT PRIMARY KEY, b int); +SET GLOBAL key_cache_block_size=1536; +INSERT INTO t1 VALUES (1,0); +INSERT INTO t2(b) SELECT b FROM t1; +INSERT INTO t1(b) SELECT b FROM t2; +INSERT INTO t2(b) SELECT b FROM t1; +INSERT INTO t1(b) SELECT b FROM t2; +INSERT INTO t2(b) SELECT b FROM t1; +INSERT INTO t1(b) SELECT b FROM t2; +INSERT INTO t2(b) SELECT b FROM t1; +INSERT INTO t1(b) SELECT b FROM t2; +INSERT INTO t2(b) SELECT b FROM t1; +INSERT INTO t1(b) SELECT b FROM t2; +INSERT INTO t2(b) SELECT b FROM t1; +INSERT INTO t1(b) SELECT b FROM t2; +INSERT INTO t2(b) SELECT b FROM t1; +INSERT INTO t1(b) SELECT b FROM t2; +INSERT INTO t2(b) SELECT b FROM t1; +INSERT INTO t1(b) SELECT b FROM t2; +INSERT INTO t2(b) SELECT b FROM t1; +INSERT INTO t1(b) SELECT b FROM t2; +SELECT COUNT(*) FROM t1; +SELECT @@key_cache_block_size; +CHECK TABLE t1; +DROP TABLE t1,t2; + # End of 4.1 tests diff --git a/mysys/mf_keycache.c b/mysys/mf_keycache.c index ca683f16415..88b5051c52b 100644 --- a/mysys/mf_keycache.c +++ b/mysys/mf_keycache.c @@ -1741,6 +1741,7 @@ byte *key_cache_read(KEY_CACHE *keycache, uint status; int page_st; + offset= (uint) (filepos & (keycache->key_cache_block_size-1)); /* Read data in key_cache_block_size increments */ do { @@ -1750,7 +1751,6 @@ byte *key_cache_read(KEY_CACHE *keycache, keycache_pthread_mutex_unlock(&keycache->cache_lock); goto no_key_cache; } - offset= (uint) (filepos & (keycache->key_cache_block_size-1)); filepos-= offset; read_length= length; set_if_smaller(read_length, keycache->key_cache_block_size-offset); @@ -1826,6 +1826,7 @@ byte *key_cache_read(KEY_CACHE *keycache, #endif buff+= read_length; filepos+= read_length+offset; + offset= 0; } while ((length-= read_length)); DBUG_RETURN(start); @@ -1877,17 +1878,17 @@ int key_cache_insert(KEY_CACHE *keycache, uint read_length; int page_st; int error; + uint offset; + offset= (uint) (filepos & (keycache->key_cache_block_size-1)); do { - uint offset; keycache_pthread_mutex_lock(&keycache->cache_lock); if (!keycache->can_be_used) { keycache_pthread_mutex_unlock(&keycache->cache_lock); DBUG_RETURN(0); } - offset= (uint) (filepos & (keycache->key_cache_block_size-1)); /* Read data into key cache from buff in key_cache_block_size incr. */ filepos-= offset; read_length= length; @@ -1945,6 +1946,7 @@ int key_cache_insert(KEY_CACHE *keycache, buff+= read_length; filepos+= read_length+offset; + offset= 0; } while ((length-= read_length)); } @@ -2011,17 +2013,17 @@ int key_cache_write(KEY_CACHE *keycache, /* Key cache is used */ uint read_length; int page_st; + uint offset; + offset= (uint) (filepos & (keycache->key_cache_block_size-1)); do { - uint offset; keycache_pthread_mutex_lock(&keycache->cache_lock); if (!keycache->can_be_used) { keycache_pthread_mutex_unlock(&keycache->cache_lock); goto no_key_cache; } - offset= (uint) (filepos & (keycache->key_cache_block_size-1)); /* Write data in key_cache_block_size increments */ filepos-= offset; read_length= length; From c0887e546140ce8989dd8b06a87d3944956bb1cf Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 19 Apr 2006 22:54:25 +0500 Subject: [PATCH 11/20] Bug#18544 - LOCK TABLES timeout causes MyISAM table corruption After a locking error the open table(s) were not fully cleaned up for reuse. But they were put into the open table cache even before the lock was tried. The next statement reused the table(s) with a wrong lock type set up. This tricked MyISAM into believing that it don't need to update the table statistics. Hence CHECK TABLE reported a mismatch of record count and table size. Fortunately nothing worse has been detected yet. The effect of the test case was that the insert worked on a read locked table. (!) I added a new function that clears the lock type from all tables that were prepared for a lock. I call this function when a lock failes. No test case. One test would add 50 seconds to the test suite. Another test requires file mode modifications. I added a test script to the bug report. It contains three cases for failing locks. All could reproduce a table corruption. All are fixed by this patch. This bug was not lock timeout specific. sql/lock.cc: Bug#18544 - LOCK TABLES timeout causes MyISAM table corruption Resetting the lock type in the open table(s) lock data after a locking error. --- sql/lock.cc | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/sql/lock.cc b/sql/lock.cc index fe59039dde3..ab4a81034ba 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -78,6 +78,7 @@ extern HASH open_cache; static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table,uint count, uint flags, TABLE **write_locked); +static void reset_lock_data(MYSQL_LOCK *sql_lock); static int lock_external(THD *thd, TABLE **table,uint count); static int unlock_external(THD *thd, TABLE **table,uint count); static void print_lock_error(int error); @@ -120,12 +121,16 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags) */ if (wait_if_global_read_lock(thd, 1, 1)) { + /* Clear the lock type of all lock data to avoid reusage. */ + reset_lock_data(sql_lock); my_free((gptr) sql_lock,MYF(0)); sql_lock=0; break; - } + } if (thd->version != refresh_version) { + /* Clear the lock type of all lock data to avoid reusage. */ + reset_lock_data(sql_lock); my_free((gptr) sql_lock,MYF(0)); goto retry; } @@ -134,6 +139,8 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags) thd->proc_info="System lock"; if (lock_external(thd, tables, count)) { + /* Clear the lock type of all lock data to avoid reusage. */ + reset_lock_data(sql_lock); my_free((gptr) sql_lock,MYF(0)); sql_lock=0; thd->proc_info=0; @@ -639,6 +646,9 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, if (table->db_stat & HA_READ_ONLY) { my_error(ER_OPEN_AS_READONLY,MYF(0),table->table_name); + /* Clear the lock type of the lock data that are stored already. */ + sql_lock->lock_count= locks - sql_lock->locks; + reset_lock_data(sql_lock); my_free((gptr) sql_lock,MYF(0)); DBUG_RETURN(0); } @@ -663,6 +673,48 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, } +/* + Reset lock type in lock data. + + SYNOPSIS + reset_lock_data() + sql_lock The MySQL lock. + + DESCRIPTION + + After a locking error we want to quit the locking of the table(s). + The test case in the bug report for Bug #18544 has the following + cases: 1. Locking error in lock_external() due to InnoDB timeout. + 2. Locking error in get_lock_data() due to missing write permission. + 3. Locking error in wait_if_global_read_lock() due to lock conflict. + + In all these cases we have already set the lock type into the lock + data of the open table(s). If the table(s) are in the open table + cache, they could be reused with the non-zero lock type set. This + could lead to ignoring a different lock type with the next lock. + + Clear the lock type of all lock data. This ensures that the next + lock request will set its lock type properly. + + RETURN + void +*/ + +static void reset_lock_data(MYSQL_LOCK *sql_lock) +{ + THR_LOCK_DATA **ldata; + THR_LOCK_DATA **ldata_end; + + for (ldata= sql_lock->locks, ldata_end= ldata + sql_lock->lock_count; + ldata < ldata_end; + ldata++) + { + /* Reset lock type. */ + (*ldata)->type= TL_UNLOCK; + } +} + + /***************************************************************************** Lock table based on the name. This is used when we need total access to a closed, not open table From 913c8fa18574c70dd8c7240ff4923fad906edab8 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 20 Apr 2006 00:27:43 +0400 Subject: [PATCH 12/20] func_gconcat.result: Corrected test case for the bug#14169 to make it pass in --ps-protocol mode. mysql-test/r/func_gconcat.result: Corrected test case for the bug#14169 to make it pass in --ps-protocol mode. --- mysql-test/r/func_gconcat.result | 7 ------- 1 file changed, 7 deletions(-) diff --git a/mysql-test/r/func_gconcat.result b/mysql-test/r/func_gconcat.result index ef46d170bda..bf3e566b7f7 100644 --- a/mysql-test/r/func_gconcat.result +++ b/mysql-test/r/func_gconcat.result @@ -613,10 +613,3 @@ def group_concat(f1) 252 400 1 Y 128 0 63 f2 group_concat(f1) aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 1 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 2 -select f2,group_concat(f1) from t1 group by f2 order by 2; -Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr -def test t1 t1 f2 f2 253 255 255 Y 0 0 8 -def group_concat(f1) group_concat(f1) 252 400 1 Y 144 0 63 -f2 group_concat(f1) -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 1 -bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 2 From e4d653abb8a2b25ca932c75e61e75a97f49bd34c Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 19 Apr 2006 16:08:37 -0700 Subject: [PATCH 13/20] Temporarily commented out a query from the test case for bug 14169 to make it pass with --ps-protocol. mysql-test/r/func_gconcat.result: Added DROP TABLE command to the test case for bug 14169. --- mysql-test/r/func_gconcat.result | 1 + mysql-test/t/func_gconcat.test | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/func_gconcat.result b/mysql-test/r/func_gconcat.result index bf3e566b7f7..2c79b8f8ab1 100644 --- a/mysql-test/r/func_gconcat.result +++ b/mysql-test/r/func_gconcat.result @@ -613,3 +613,4 @@ def group_concat(f1) 252 400 1 Y 128 0 63 f2 group_concat(f1) aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 1 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 2 +drop table t1; diff --git a/mysql-test/t/func_gconcat.test b/mysql-test/t/func_gconcat.test index 1bb49a312b8..ed35c7534f6 100644 --- a/mysql-test/t/func_gconcat.test +++ b/mysql-test/t/func_gconcat.test @@ -398,6 +398,8 @@ create table t1 (f1 int unsigned, f2 varchar(255)); insert into t1 values (1,repeat('a',255)),(2,repeat('b',255)); --enable_metadata select f2,group_concat(f1) from t1 group by f2; -select f2,group_concat(f1) from t1 group by f2 order by 2; +# select f2,group_concat(f1) from t1 group by f2 order by 2; --disable_metadata +drop table t1; + # End of 4.1 tests From fffe7c4d1e6be607a449cfe26016e31fe708ab9b Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 20 Apr 2006 12:35:33 +0400 Subject: [PATCH 14/20] func_gconcat.test: Clean up test case for bug#14169 mysql-test/t/func_gconcat.test: Clean up test case for bug#14169 --- mysql-test/t/func_gconcat.test | 1 - 1 file changed, 1 deletion(-) diff --git a/mysql-test/t/func_gconcat.test b/mysql-test/t/func_gconcat.test index ed35c7534f6..8f50690dd8b 100644 --- a/mysql-test/t/func_gconcat.test +++ b/mysql-test/t/func_gconcat.test @@ -398,7 +398,6 @@ create table t1 (f1 int unsigned, f2 varchar(255)); insert into t1 values (1,repeat('a',255)),(2,repeat('b',255)); --enable_metadata select f2,group_concat(f1) from t1 group by f2; -# select f2,group_concat(f1) from t1 group by f2 order by 2; --disable_metadata drop table t1; From 66ee876ba3b50fba4ed159eb23587bf02fc2d44b Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 20 Apr 2006 23:09:49 +0400 Subject: [PATCH 15/20] Applied innodb-4.1-ss22 snapshot. Fix BUG#16814: "SHOW INNODB STATUS format error in LATEST FOREIGN KEY ERROR section" Add a missing newline to the LAST FOREIGN KEY ERROR section in SHOW INNODB STATUS output. Fix BUG#18934: "InnoDB crashes when table uses column names like DB_ROW_ID". Refuse tables that use reserved column names. innobase/dict/dict0dict.c: Applied innodb-4.1-ss22 snapshot. dict_foreign_error_report(): Always print a newline after invoking dict_print_info_on_foreign_key_in_create_format() (Bug#16814). Refuse tables that use reserved column names (Bug#18934). innobase/dict/dict0mem.c: Applied innodb-4.1-ss22 snapshot. Refuse tables that use reserved column names (Bug#18934). innobase/include/dict0dict.h: Applied innodb-4.1-ss22 snapshot. Refuse tables that use reserved column names (Bug#18934). innobase/include/dict0mem.h: Applied innodb-4.1-ss22 snapshot. Refuse tables that use reserved column names (Bug#18934). innobase/include/univ.i: Applied innodb-4.1-ss22 snapshot. innobase/row/row0mysql.c: Applied innodb-4.1-ss22 snapshot. Refuse tables that use reserved column names (Bug#18934). --- innobase/dict/dict0dict.c | 35 ++++++++++++++++++++++++++++++++++- innobase/dict/dict0mem.c | 15 +++++++++++++++ innobase/include/dict0dict.h | 9 +++++++++ innobase/include/dict0mem.h | 7 +++++++ innobase/include/univ.i | 3 +++ innobase/row/row0mysql.c | 14 ++++++++++++++ 6 files changed, 82 insertions(+), 1 deletion(-) diff --git a/innobase/dict/dict0dict.c b/innobase/dict/dict0dict.c index 093df5118af..4b23ce047b2 100644 --- a/innobase/dict/dict0dict.c +++ b/innobase/dict/dict0dict.c @@ -1377,6 +1377,38 @@ dict_col_reposition_in_cache( HASH_INSERT(dict_col_t, hash, dict_sys->col_hash, fold, col); } +/******************************************************************** +If the given column name is reserved for InnoDB system columns, return +TRUE.*/ + +ibool +dict_col_name_is_reserved( +/*======================*/ + /* out: TRUE if name is reserved */ + const char* name) /* in: column name */ +{ + /* This check reminds that if a new system column is added to + the program, it should be dealt with here. */ +#if DATA_N_SYS_COLS != 4 +#error "DATA_N_SYS_COLS != 4" +#endif + + static const char* reserved_names[] = { + "DB_ROW_ID", "DB_TRX_ID", "DB_ROLL_PTR", "DB_MIX_ID" + }; + + ulint i; + + for (i = 0; i < UT_ARR_SIZE(reserved_names); i++) { + if (strcmp(name, reserved_names[i]) == 0) { + + return(TRUE); + } + } + + return(FALSE); +} + /************************************************************************** Adds an index to the dictionary cache. */ @@ -2160,8 +2192,9 @@ dict_foreign_error_report( fputs(msg, file); fputs(" Constraint:\n", file); dict_print_info_on_foreign_key_in_create_format(file, NULL, fk); + putc('\n', file); if (fk->foreign_index) { - fputs("\nThe index in the foreign key in table is ", file); + fputs("The index in the foreign key in table is ", file); ut_print_name(file, NULL, fk->foreign_index->name); fputs( "\nSee http://dev.mysql.com/doc/mysql/en/InnoDB_foreign_key_constraints.html\n" diff --git a/innobase/dict/dict0mem.c b/innobase/dict/dict0mem.c index 1d45585aac1..5a2b2e005d0 100644 --- a/innobase/dict/dict0mem.c +++ b/innobase/dict/dict0mem.c @@ -94,6 +94,21 @@ dict_mem_table_create( return(table); } +/******************************************************************** +Free a table memory object. */ + +void +dict_mem_table_free( +/*================*/ + dict_table_t* table) /* in: table */ +{ + ut_ad(table); + ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); + + mutex_free(&(table->autoinc_mutex)); + mem_heap_free(table->heap); +} + /************************************************************************** Creates a cluster memory object. */ diff --git a/innobase/include/dict0dict.h b/innobase/include/dict0dict.h index bf1382e8bb2..1226055e135 100644 --- a/innobase/include/dict0dict.h +++ b/innobase/include/dict0dict.h @@ -98,6 +98,15 @@ ulint dict_col_get_clust_pos( /*===================*/ dict_col_t* col); +/******************************************************************** +If the given column name is reserved for InnoDB system columns, return +TRUE. */ + +ibool +dict_col_name_is_reserved( +/*======================*/ + /* out: TRUE if name is reserved */ + const char* name); /* in: column name */ /************************************************************************ Initializes the autoinc counter. It is not an error to initialize an already initialized counter. */ diff --git a/innobase/include/dict0mem.h b/innobase/include/dict0mem.h index 1e496a25477..5c3a4ed76d4 100644 --- a/innobase/include/dict0mem.h +++ b/innobase/include/dict0mem.h @@ -55,6 +55,13 @@ dict_mem_table_create( is ignored if the table is made a member of a cluster */ ulint n_cols); /* in: number of columns */ +/******************************************************************** +Free a table memory object. */ + +void +dict_mem_table_free( +/*================*/ + dict_table_t* table); /* in: table */ /************************************************************************** Creates a cluster memory object. */ diff --git a/innobase/include/univ.i b/innobase/include/univ.i index 625978ffc38..6939edbcaf8 100644 --- a/innobase/include/univ.i +++ b/innobase/include/univ.i @@ -242,6 +242,9 @@ contains the sum of the following flag and the locally stored len. */ #define UNIV_EXTERN_STORAGE_FIELD (UNIV_SQL_NULL - UNIV_PAGE_SIZE) +/* Compile-time constant of the given array's size. */ +#define UT_ARR_SIZE(a) (sizeof(a) / sizeof((a)[0])) + #include #include "ut0dbg.h" #include "ut0ut.h" diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c index ba50e6a3511..23d019b6f78 100644 --- a/innobase/row/row0mysql.c +++ b/innobase/row/row0mysql.c @@ -1474,6 +1474,7 @@ row_create_table_for_mysql( const char* table_name; ulint table_name_len; ulint err; + ulint i; ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); #ifdef UNIV_SYNC_DEBUG @@ -1510,6 +1511,19 @@ row_create_table_for_mysql( return(DB_ERROR); } + /* Check that no reserved column names are used. */ + for (i = 0; i < dict_table_get_n_user_cols(table); i++) { + dict_col_t* col = dict_table_get_nth_col(table, i); + + if (dict_col_name_is_reserved(col->name)) { + + dict_mem_table_free(table); + trx_commit_for_mysql(trx); + + return(DB_ERROR); + } + } + trx_start_if_not_started(trx); if (row_mysql_is_recovered_tmp_table(table->name)) { From 9225a51c58def1fe505d0b46473b5294862309a7 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 20 Apr 2006 22:15:38 -0700 Subject: [PATCH 16/20] Fixed bug #18767. The bug caused wrong result sets for union constructs of the form (SELECT ... ORDER BY order_list1 [LIMIT n]) ORDER BY order_list2. For such queries order lists were concatenated and limit clause was completely neglected. mysql-test/r/order_by.result: Added a test case for bug #18767. mysql-test/t/order_by.test: Added a test case for bug #18767. sql/sql_lex.h: Fixed bug #18767. Placed the code the created a fake SELECT_LEX into a separate function. sql/sql_parse.cc: Fixed bug #18767. Placed the code the created a fake SELECT_LEX into a separate function. sql/sql_select.cc: Fixed bug #18767. Changed the condition on which a SELECT is treated as part of a UNION. The SELECT in (SELECT ... ORDER BY order_list1 [LIMIT n]) ORDER BY order_list2 now is handled in the same way as the first SELECT in a UNION sequence. sql/sql_union.cc: Fixed bug #18767. Changed the condition at which a SELECT is treated as part of a UNION. The SELECT in (SELECT ... ORDER BY order_list1 [LIMIT n]) ORDER BY order_list2 now is handled in the same way as the first SELECT in a UNION sequence. sql/sql_yacc.yy: Fixed bug #18767. Changed the condition at which a SELECT is treated as part of a UNION. The SELECT in (SELECT ... ORDER BY order_list1 [LIMIT n]) ORDER BY order_list2 now is handled in the same way as the first SELECT in a UNION sequence. In the same way is handled the SELECT in (SELECT ... LIMIT n) ORDER BY order list. Yet if there is neither ORDER BY nor LIMIT in the single-select union construct (SELECT ...) ORDER BY order_list then it is still handled as simple select with an order clause. --- mysql-test/r/order_by.result | 32 ++++++++++++++++ mysql-test/t/order_by.test | 14 +++++++ sql/sql_lex.h | 1 + sql/sql_parse.cc | 74 +++++++++++++++++++++++++++--------- sql/sql_select.cc | 2 +- sql/sql_union.cc | 2 +- sql/sql_yacc.yy | 24 ++++++++++-- 7 files changed, 127 insertions(+), 22 deletions(-) diff --git a/mysql-test/r/order_by.result b/mysql-test/r/order_by.result index ee8ca5f0328..8126e223f55 100644 --- a/mysql-test/r/order_by.result +++ b/mysql-test/r/order_by.result @@ -788,3 +788,35 @@ a 2 2 DROP TABLE t1; +CREATE TABLE t1 (a int, b int); +INSERT INTO t1 VALUES (1,30), (2,20), (1,10), (2,30), (1,20), (2,10); +(SELECT b,a FROM t1 ORDER BY a,b) ORDER BY b,a; +b a +10 1 +10 2 +20 1 +20 2 +30 1 +30 2 +(SELECT b FROM t1 ORDER BY b DESC) ORDER BY b ASC; +b +10 +10 +20 +20 +30 +30 +(SELECT b,a FROM t1 ORDER BY b,a) ORDER BY a,b; +b a +10 1 +20 1 +30 1 +10 2 +20 2 +30 2 +(SELECT b,a FROM t1 ORDER by b,a LIMIT 3) ORDER by a,b; +b a +10 1 +20 1 +10 2 +DROP TABLE t1; diff --git a/mysql-test/t/order_by.test b/mysql-test/t/order_by.test index 4b7ec84dc54..1664afc70f9 100644 --- a/mysql-test/t/order_by.test +++ b/mysql-test/t/order_by.test @@ -545,4 +545,18 @@ SELECT a FROM t1 ORDER BY a; (SELECT a FROM t1) ORDER BY a; DROP TABLE t1; +# +# Bug #18767: global ORDER BY applied to a SELECT with ORDER BY either was +# ignored or 'concatened' to the latter. + +CREATE TABLE t1 (a int, b int); +INSERT INTO t1 VALUES (1,30), (2,20), (1,10), (2,30), (1,20), (2,10); + +(SELECT b,a FROM t1 ORDER BY a,b) ORDER BY b,a; +(SELECT b FROM t1 ORDER BY b DESC) ORDER BY b ASC; +(SELECT b,a FROM t1 ORDER BY b,a) ORDER BY a,b; +(SELECT b,a FROM t1 ORDER by b,a LIMIT 3) ORDER by a,b; + +DROP TABLE t1; + # End of 4.1 tests diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 47908ba8685..bd79a194122 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -400,6 +400,7 @@ public: bool check_updateable(char *db, char *table); void print(String *str); + bool add_fake_select_lex(THD *thd); ulong init_prepare_fake_select_lex(THD *thd); int change_result(select_subselect *result, select_subselect *old_result); inline bool is_prepared() { return prepared; } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 21a335637b9..e25a428aaa7 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -4189,23 +4189,9 @@ mysql_new_select(LEX *lex, bool move_down) else { select_lex->include_neighbour(lex->current_select); - SELECT_LEX_UNIT *unit= select_lex->master_unit(); - SELECT_LEX *fake= unit->fake_select_lex; - if (!fake) - { - /* - as far as we included SELECT_LEX for UNION unit should have - fake SELECT_LEX for UNION processing - */ - if (!(fake= unit->fake_select_lex= new(lex->thd->mem_root) SELECT_LEX())) - return 1; - fake->include_standalone(unit, - (SELECT_LEX_NODE**)&unit->fake_select_lex); - fake->select_number= INT_MAX; - fake->make_empty_select(); - fake->linkage= GLOBAL_OPTIONS_TYPE; - fake->select_limit= HA_POS_ERROR; - } + if (!select_lex->master_unit()->fake_select_lex && + select_lex->master_unit()->add_fake_select_lex(lex->thd)) + return 1; } select_lex->master_unit()->global_parameters= select_lex; @@ -4975,6 +4961,60 @@ void st_select_lex::set_lock_for_tables(thr_lock_type lock_type) } +/* + Create a fake SELECT_LEX for a unit + + SYNOPSIS: + add_fake_select_lex() + thd thread handle + + DESCRIPTION + The method create a fake SELECT_LEX object for a unit. + This object is created for any union construct containing a union + operation and also for any single select union construct of the form + (SELECT ... ORDER BY order_list [LIMIT n]) ORDER BY ... + or of the form + (SELECT ... ORDER BY LIMIT n) ORDER BY ... + + NOTES + The object is used to retrieve rows from the temporary table + where the result on the union is obtained. + + RETURN VALUES + 1 on failure to create the object + 0 on success +*/ + +bool st_select_lex_unit::add_fake_select_lex(THD *thd) +{ + SELECT_LEX *first_sl= first_select(); + DBUG_ENTER("add_fake_select_lex"); + DBUG_ASSERT(!fake_select_lex); + + if (!(fake_select_lex= new (thd->mem_root) SELECT_LEX())) + DBUG_RETURN(1); + fake_select_lex->include_standalone(this, + (SELECT_LEX_NODE**)&fake_select_lex); + fake_select_lex->select_number= INT_MAX; + fake_select_lex->make_empty_select(); + fake_select_lex->linkage= GLOBAL_OPTIONS_TYPE; + fake_select_lex->select_limit= HA_POS_ERROR; + + if (!first_sl->next_select()) + { + /* + This works only for + (SELECT ... ORDER BY list [LIMIT n]) ORDER BY order_list [LIMIT m], + (SELECT ... LIMIT n) ORDER BY order_list [LIMIT m] + just before the parser starts processing order_list + */ + global_parameters= fake_select_lex; + fake_select_lex->no_table_names_allowed= 1; + thd->lex->current_select= fake_select_lex; + } + DBUG_RETURN(0); +} + void add_join_on(TABLE_LIST *b,Item *expr) { if (expr) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 91fc808058f..46dba61cfc5 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -171,7 +171,7 @@ int handle_select(THD *thd, LEX *lex, select_result *result) register SELECT_LEX *select_lex = &lex->select_lex; DBUG_ENTER("handle_select"); - if (select_lex->next_select()) + if (select_lex->next_select() || select_lex->master_unit()->fake_select_lex) res=mysql_union(thd, lex, result, &lex->unit); else res= mysql_select(thd, &select_lex->ref_pointer_array, diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 8bb53f7b573..0948602bbb4 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -184,7 +184,7 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, thd_arg->lex->current_select= sl= first_select= first_select_in_union(); found_rows_for_union= first_select->options & OPTION_FOUND_ROWS; - is_union= test(first_select->next_select()); + is_union= test(first_select->next_select() || fake_select_lex); /* Global option */ diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 05d95b57abb..162b4183c84 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -3790,15 +3790,33 @@ order_clause: ORDER_SYM BY { LEX *lex=Lex; - if (lex->current_select->linkage != GLOBAL_OPTIONS_TYPE && - lex->current_select->olap != - UNSPECIFIED_OLAP_TYPE) + SELECT_LEX *sel= lex->current_select; + SELECT_LEX_UNIT *unit= sel-> master_unit(); + if (sel->linkage != GLOBAL_OPTIONS_TYPE && + sel->olap != UNSPECIFIED_OLAP_TYPE) { net_printf(lex->thd, ER_WRONG_USAGE, "CUBE/ROLLUP", "ORDER BY"); YYABORT; } + if (lex->sql_command != SQLCOM_ALTER_TABLE && !unit->fake_select_lex) + { + /* + A query of the of the form (SELECT ...) ORDER BY order_list is + executed in the same way as the query + SELECT ... ORDER BY order_list + unless the SELECT construct contains ORDER BY or LIMIT clauses. + Otherwise we create a fake SELECT_LEX if it has not been created + yet. + */ + SELECT_LEX *first_sl= unit->first_select(); + if (!first_sl->next_select() && + (first_sl->order_list.elements || + first_sl->select_limit != HA_POS_ERROR) && + unit->add_fake_select_lex(lex->thd)) + YYABORT; + } } order_list; order_list: From 301a24e3a012004eab52a7ae9de4f4f81bb3ff7e Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 21 Apr 2006 11:48:00 +0500 Subject: [PATCH 17/20] Bug #17896: MIN of CASE WHEN returns non-minimum value! - after review fixes sql/item_cmpfunc.cc: Bug #17896: MIN of CASE WHEN returns non-minimum value! - after review fixes. --- sql/item_cmpfunc.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 57a3c9e5623..3c41fb56d89 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -39,19 +39,23 @@ static void agg_result_type(Item_result *type, Item **items, uint nitems) { Item **item, **item_end; - /* Note: NULL items don't affect the result type */ *type= STRING_RESULT; /* Skip beginning NULL items */ for (item= items, item_end= item + nitems; item < item_end; item++) + { if ((*item)->type() != Item::NULL_ITEM) { *type= (*item)->result_type(); item++; break; } + } + /* Combine result types. Note: NULL items don't affect the result */ for (; item < item_end; item++) + { if ((*item)->type() != Item::NULL_ITEM) *type= item_store_type(type[0], (*item)->result_type()); + } } static void agg_cmp_type(Item_result *type, Item **items, uint nitems) From 329ebbd14dc562a1206997cd855c29c6291c3854 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 23 Apr 2006 12:18:57 +0300 Subject: [PATCH 18/20] Bug#17263 temporary tables and replication Backporting a changeset made for 5.0. Comments from there: The fix refines the algorithm of generating DROPs for binlog. Temp tables with common pseudo_thread_id are clustered into one query. Consequently one replication event per pseudo_thread_id is generated. mysql-test/r/rpl_temporary.result: results changed mysql-test/t/rpl_temporary.test: test to generate problematic drop in binlog to feed it to restarting slave to see no stop. sql/sql_base.cc: change in drop temprorary tables alg in close_temporary_tables. --- mysql-test/r/rpl_temporary.result | 14 +++ mysql-test/t/rpl_temporary.test | 28 ++++- sql/sql_base.cc | 171 ++++++++++++++++++++++-------- 3 files changed, 167 insertions(+), 46 deletions(-) diff --git a/mysql-test/r/rpl_temporary.result b/mysql-test/r/rpl_temporary.result index b9865d282fa..cd67d2592a8 100644 --- a/mysql-test/r/rpl_temporary.result +++ b/mysql-test/r/rpl_temporary.result @@ -89,3 +89,17 @@ f 7 drop table t1,t2; create temporary table t3 (f int); +set @session.pseudo_thread_id=100; +create temporary table t101 (id int); +create temporary table t102 (id int); +set @session.pseudo_thread_id=200; +create temporary table t201 (id int); +create temporary table `#not_user_table_prefixed_with_hash_sign_no_harm` (id int); +set @con1_id=connection_id(); +kill @con1_id; +create table t1(f int); +insert into t1 values (1); +select * from t1 /* must be 1 */; +f +1 +drop table t1; diff --git a/mysql-test/t/rpl_temporary.test b/mysql-test/t/rpl_temporary.test index 8dead9138c1..71d7b32b7c9 100644 --- a/mysql-test/t/rpl_temporary.test +++ b/mysql-test/t/rpl_temporary.test @@ -128,6 +128,32 @@ drop table t1,t2; create temporary table t3 (f int); sync_with_master; -# The server will now close done +# +# BUG#17263 incorrect generation DROP temp tables +# Temporary tables of connection are dropped in batches +# where a batch correspond to pseudo_thread_id +# value was set up at the moment of temp table creation +# +connection con1; +set @session.pseudo_thread_id=100; +create temporary table t101 (id int); +create temporary table t102 (id int); +set @session.pseudo_thread_id=200; +create temporary table t201 (id int); +create temporary table `#not_user_table_prefixed_with_hash_sign_no_harm` (id int); +set @con1_id=connection_id(); +kill @con1_id; + +#now do something to show that slave is ok after DROP temp tables +connection master; +create table t1(f int); +insert into t1 values (1); + +sync_slave_with_master; +#connection slave; +select * from t1 /* must be 1 */; + +connection master; +drop table t1; # End of 4.1 tests diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 223e8482f49..756fb8189dc 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -483,13 +483,23 @@ void close_temporary(TABLE *table,bool delete_table) DBUG_VOID_RETURN; } +/* close_temporary_tables' internal */ +static inline uint tmpkeyval(THD *thd, TABLE *table) +{ + return uint4korr(table->table_cache_key + table->key_length - + sizeof(thd->variables.pseudo_thread_id)); +} + +/* Creates one DROP TEMPORARY TABLE binlog event for each pseudo-thread */ void close_temporary_tables(THD *thd) { - TABLE *table,*next; - char *query, *end; - uint query_buf_size; - bool found_user_tables = 0; + TABLE *next, + *prev_table /* prev link is not maintained in TABLE's double-linked list */, + *table; + char *query= (gptr) 0, *end; + uint query_buf_size, max_names_len; + bool found_user_tables; if (!thd->temporary_tables) return; @@ -497,51 +507,122 @@ void close_temporary_tables(THD *thd) LINT_INIT(end); query_buf_size= 50; // Enough for DROP ... TABLE IF EXISTS - for (table=thd->temporary_tables ; table ; table=table->next) - /* - We are going to add 4 ` around the db/table names, so 1 does not look - enough; indeed it is enough, because table->key_length is greater (by 8, - because of server_id and thread_id) than db||table. - */ - query_buf_size+= table->key_length+1; - - if ((query = alloc_root(thd->mem_root, query_buf_size))) - // Better add "if exists", in case a RESET MASTER has been done - end=strmov(query, "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS "); - - for (table=thd->temporary_tables ; table ; table=next) + /* + insertion sort of temp tables by pseudo_thread_id to build ordered list + of sublists of equal pseudo_thread_id + */ + for (prev_table= thd->temporary_tables, + table= prev_table->next, + found_user_tables= (prev_table->table_name[0] != '#'); + table; + prev_table= table, table= table->next) { - if (query) // we might be out of memory, but this is not fatal + TABLE *prev_sorted /* same as for prev_table */, + *sorted; + /* + table not created directly by the user is moved to the tail. + Fixme/todo: nothing (I checked the manual) prevents user to create temp + with `#' + */ + if (table->real_name[0] == '#') + continue; + else { - // skip temporary tables not created directly by the user - if (table->real_name[0] != '#') - found_user_tables = 1; - /* - Here we assume table_cache_key always starts - with \0 terminated db name - */ - end = strxmov(end,"`",table->table_cache_key,"`.`", - table->real_name,"`,", NullS); + found_user_tables = 1; } - next=table->next; - close_temporary(table); - } - if (query && found_user_tables && mysql_bin_log.is_open()) + for (prev_sorted= NULL, sorted= thd->temporary_tables; sorted != table; + prev_sorted= sorted, sorted= sorted->next) + { + if (sorted->real_name[0] == '#' || tmpkeyval(thd, sorted) > tmpkeyval(thd, table)) + { + /* move into the sorted part of the list from the unsorted */ + prev_table->next= table->next; + table->next= sorted; + if (prev_sorted) + { + prev_sorted->next= table; + } + else + { + thd->temporary_tables= table; + } + table= prev_table; + break; + } + } + } + /* + calc query_buf_size as max per sublists, one sublist per pseudo thread id. + Also stop at first occurence of `#'-named table that starts + all implicitly created temp tables + */ + for (max_names_len= 0, table=thd->temporary_tables; + table && table->real_name[0] != '#'; + table=table->next) { - /* The -1 is to remove last ',' */ - thd->clear_error(); - Query_log_event qinfo(thd, query, (ulong)(end-query)-1, 0, FALSE); - /* - Imagine the thread had created a temp table, then was doing a SELECT, and - the SELECT was killed. Then it's not clever to mark the statement above as - "killed", because it's not really a statement updating data, and there - are 99.99% chances it will succeed on slave. - If a real update (one updating a persistent table) was killed on the - master, then this real update will be logged with error_code=killed, - rightfully causing the slave to stop. - */ - qinfo.error_code= 0; - mysql_bin_log.write(&qinfo); + uint tmp_names_len; + for (tmp_names_len= table->key_length + 1; + table->next && table->real_name[0] != '#' && + tmpkeyval(thd, table) == tmpkeyval(thd, table->next); + table=table->next) + { + /* + We are going to add 4 ` around the db/table names, so 1 might not look + enough; indeed it is enough, because table->key_length is greater (by 8, + because of server_id and thread_id) than db||table. + */ + tmp_names_len += table->next->key_length + 1; + } + if (tmp_names_len > max_names_len) max_names_len= tmp_names_len; + } + + /* allocate */ + if (found_user_tables && mysql_bin_log.is_open() && + (query = alloc_root(thd->mem_root, query_buf_size+= max_names_len))) + // Better add "if exists", in case a RESET MASTER has been done + end= strmov(query, "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS "); + + /* scan sorted tmps to generate sequence of DROP */ + for (table=thd->temporary_tables; table; table= next) + { + if (query // we might be out of memory, but this is not fatal + && table->real_name[0] != '#') + { + char *end_cur; + /* Set pseudo_thread_id to be that of the processed table */ + thd->variables.pseudo_thread_id= tmpkeyval(thd, table); + /* Loop forward through all tables within the sublist of + common pseudo_thread_id to create single DROP query */ + for (end_cur= end; + table && table->real_name[0] != '#' && + tmpkeyval(thd, table) == thd->variables.pseudo_thread_id; + table= next) + { + end_cur= strxmov(end_cur, "`", table->table_cache_key, "`.`", + table->real_name, "`,", NullS); + next= table->next; + close_temporary(table, 1); + } + thd->clear_error(); + /* The -1 is to remove last ',' */ + Query_log_event qinfo(thd, query, (ulong)(end_cur - query) - 1, 0, FALSE); + /* + Imagine the thread had created a temp table, then was doing a SELECT, and + the SELECT was killed. Then it's not clever to mark the statement above as + "killed", because it's not really a statement updating data, and there + are 99.99% chances it will succeed on slave. + If a real update (one updating a persistent table) was killed on the + master, then this real update will be logged with error_code=killed, + rightfully causing the slave to stop. + */ + qinfo.error_code= 0; + mysql_bin_log.write(&qinfo); + } + else + { + next= table->next; + close_temporary(table, 1); + } } thd->temporary_tables=0; } From 4189bfa421672924b9c8a86571717a78bf32de7a Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 23 Apr 2006 19:42:26 +0300 Subject: [PATCH 19/20] Bug#17263: incorrect DROP query in temporary tables replication accounting non-ai32 in tmpkeyval. This changeset is supposed to be specifically for 4.1. Another changeset is going to push into 5. sql/sql_base.cc: correction due to uint4korr definition: can not sizeof on not ia32. --- sql/sql_base.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 756fb8189dc..60e91aff3f9 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -483,11 +483,10 @@ void close_temporary(TABLE *table,bool delete_table) DBUG_VOID_RETURN; } -/* close_temporary_tables' internal */ +/* close_temporary_tables' internal, 4 is due to uint4korr definition */ static inline uint tmpkeyval(THD *thd, TABLE *table) { - return uint4korr(table->table_cache_key + table->key_length - - sizeof(thd->variables.pseudo_thread_id)); + return uint4korr(table->table_cache_key + table->key_length - 4); } /* Creates one DROP TEMPORARY TABLE binlog event for each pseudo-thread */ @@ -513,7 +512,7 @@ void close_temporary_tables(THD *thd) */ for (prev_table= thd->temporary_tables, table= prev_table->next, - found_user_tables= (prev_table->table_name[0] != '#'); + found_user_tables= (prev_table->real_name[0] != '#'); table; prev_table= table, table= table->next) { From 868c49e36c69c9f77c786143f7092bac9e9c1539 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 23 Apr 2006 19:26:56 -0500 Subject: [PATCH 20/20] myisam_ftdump.c: myisam_ftdump options: --help first, then rest in lexical order. myisam/myisam_ftdump.c: myisam_ftdump options: --help first, then rest in lexical order. --- myisam/myisam_ftdump.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/myisam/myisam_ftdump.c b/myisam/myisam_ftdump.c index 28aac0a8ecf..809d7bcca89 100644 --- a/myisam/myisam_ftdump.c +++ b/myisam/myisam_ftdump.c @@ -34,20 +34,20 @@ static uint lengths[256]; static struct my_option my_long_options[] = { + {"help", 'h', "Display help and exit.", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"help", '?', "Synonym for -h.", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"count", 'c', "Calculate per-word stats (counts and global weights).", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"dump", 'd', "Dump index (incl. data offsets and word weights).", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"length", 'l', "Report length distribution.", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"stats", 's', "Report global stats.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"verbose", 'v', "Be verbose.", (gptr*) &verbose, (gptr*) &verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"count", 'c', "Calculate per-word stats (counts and global weights).", - 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"length", 'l', "Report length distribution.", - 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"help", 'h', "Display help and exit.", - 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"help", '?', "Synonym for -h.", - 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} };