From 2a664ff6c200a7bfe5feee7aec7b1650dab8ccff Mon Sep 17 00:00:00 2001 From: "bar@mysql.com/bar.intranet.mysql.r18.ru" <> Date: Thu, 20 Jul 2006 15:52:48 +0500 Subject: [PATCH 01/60] Bug#20471 LIKE search fails with indexed utf8 char column The main problem was already fixed by Igor under terms of 16674. Adding some additional minor fixes and tests. --- include/m_ctype.h | 4 ++ mysql-test/r/ctype_utf8.result | 75 ++++++++++++++++++++++++++++++++++ mysql-test/t/ctype_utf8.test | 70 +++++++++++++++++++++++++++++++ strings/CHARSET_INFO.txt | 12 +++++- strings/ctype-mb.c | 21 ++++++++-- strings/ctype-utf8.c | 2 +- 6 files changed, 177 insertions(+), 7 deletions(-) diff --git a/include/m_ctype.h b/include/m_ctype.h index cd1dac9dde8..b9ed39414bb 100644 --- a/include/m_ctype.h +++ b/include/m_ctype.h @@ -108,6 +108,8 @@ enum my_lex_states struct charset_info_st; + +/* See strings/CHARSET_INFO.txt about information on this structure */ typedef struct my_collation_handler_st { my_bool (*init)(struct charset_info_st *, void *(*alloc)(uint)); @@ -147,6 +149,7 @@ extern MY_COLLATION_HANDLER my_collation_8bit_simple_ci_handler; extern MY_COLLATION_HANDLER my_collation_ucs2_uca_handler; +/* See strings/CHARSET_INFO.txt about information on this structure */ typedef struct my_charset_handler_st { my_bool (*init)(struct charset_info_st *, void *(*alloc)(uint)); @@ -204,6 +207,7 @@ extern MY_CHARSET_HANDLER my_charset_8bit_handler; extern MY_CHARSET_HANDLER my_charset_ucs2_handler; +/* See strings/CHARSET_INFO.txt about information on this structure */ typedef struct charset_info_st { uint number; diff --git a/mysql-test/r/ctype_utf8.result b/mysql-test/r/ctype_utf8.result index 4ceacaffcbb..61083510854 100644 --- a/mysql-test/r/ctype_utf8.result +++ b/mysql-test/r/ctype_utf8.result @@ -1124,6 +1124,81 @@ check table t1; Table Op Msg_type Msg_text test.t1 check status OK drop table t1; +set names utf8; +create table t1 (s1 char(5) character set utf8); +insert into t1 values +('a'),('b'),(null),('ペテルグル'),('ü'),('Y'); +create index it1 on t1 (s1); +select s1 as before_delete_general_ci from t1 where s1 like 'ペテ%'; +before_delete_general_ci +ペテルグル +delete from t1 where s1 = 'Y'; +select s1 as after_delete_general_ci from t1 where s1 like 'ペテ%'; +after_delete_general_ci +ペテルグル +drop table t1; +set names utf8; +create table t1 (s1 char(5) character set utf8 collate utf8_unicode_ci); +insert into t1 values +('a'),('b'),(null),('ペテルグル'),('ü'),('Y'); +create index it1 on t1 (s1); +select s1 as before_delete_unicode_ci from t1 where s1 like 'ペテ%'; +before_delete_unicode_ci +ペテルグル +delete from t1 where s1 = 'Y'; +select s1 as after_delete_unicode_ci from t1 where s1 like 'ペテ%'; +after_delete_unicode_ci +ペテルグル +drop table t1; +set names utf8; +create table t1 (s1 char(5) character set utf8 collate utf8_bin); +insert into t1 values +('a'),('b'),(null),('ペテルグル'),('ü'),('Y'); +create index it1 on t1 (s1); +select s1 as before_delete_bin from t1 where s1 like 'ペテ%'; +before_delete_bin +ペテルグル +delete from t1 where s1 = 'Y'; +select s1 as after_delete_bin from t1 where s1 like 'ペテ%'; +after_delete_bin +ペテルグル +drop table t1; +set names utf8; +create table t1 (a varchar(30) not null primary key) +engine=innodb default character set utf8 collate utf8_general_ci; +insert into t1 values ('あいうえおかきくけこさしすせそ'); +insert into t1 values ('さしすせそかきくけこあいうえお'); +select a as gci1 from t1 where a like 'さしすせそかきくけこあいうえお%'; +gci1 +さしすせそかきくけこあいうえお +select a as gci2 from t1 where a like 'あいうえおかきくけこさしすせそ'; +gci2 +あいうえおかきくけこさしすせそ +drop table t1; +set names utf8; +create table t1 (a varchar(30) not null primary key) +engine=innodb default character set utf8 collate utf8_unicode_ci; +insert into t1 values ('あいうえおかきくけこさしすせそ'); +insert into t1 values ('さしすせそかきくけこあいうえお'); +select a as uci1 from t1 where a like 'さしすせそかきくけこあいうえお%'; +uci1 +さしすせそかきくけこあいうえお +select a as uci2 from t1 where a like 'あいうえおかきくけこさしすせそ'; +uci2 +あいうえおかきくけこさしすせそ +drop table t1; +set names utf8; +create table t1 (a varchar(30) not null primary key) +engine=innodb default character set utf8 collate utf8_bin; +insert into t1 values ('あいうえおかきくけこさしすせそ'); +insert into t1 values ('さしすせそかきくけこあいうえお'); +select a as bin1 from t1 where a like 'さしすせそかきくけこあいうえお%'; +bin1 +さしすせそかきくけこあいうえお +select a as bin2 from t1 where a like 'あいうえおかきくけこさしすせそ'; +bin2 +あいうえおかきくけこさしすせそ +drop table t1; SET NAMES utf8; CREATE TABLE t1 (id int PRIMARY KEY, a varchar(16) collate utf8_unicode_ci NOT NULL default '', diff --git a/mysql-test/t/ctype_utf8.test b/mysql-test/t/ctype_utf8.test index b1d485ad1ce..0f6f6978e6c 100644 --- a/mysql-test/t/ctype_utf8.test +++ b/mysql-test/t/ctype_utf8.test @@ -926,6 +926,76 @@ INSERT INTO t1 VALUES('uUABCDEFGHIGKLMNOPRSTUVWXYZ̈bbbbbbbbbbbbbbbbbbbbbbbbbbbb check table t1; drop table t1; +# +# Bug#20471 LIKE search fails with indexed utf8 char column +# +set names utf8; +create table t1 (s1 char(5) character set utf8); +insert into t1 values +('a'),('b'),(null),('ペテルグル'),('ü'),('Y'); +create index it1 on t1 (s1); +select s1 as before_delete_general_ci from t1 where s1 like 'ペテ%'; +delete from t1 where s1 = 'Y'; +select s1 as after_delete_general_ci from t1 where s1 like 'ペテ%'; +drop table t1; + +set names utf8; +create table t1 (s1 char(5) character set utf8 collate utf8_unicode_ci); +insert into t1 values +('a'),('b'),(null),('ペテルグル'),('ü'),('Y'); +create index it1 on t1 (s1); +select s1 as before_delete_unicode_ci from t1 where s1 like 'ペテ%'; +delete from t1 where s1 = 'Y'; +select s1 as after_delete_unicode_ci from t1 where s1 like 'ペテ%'; +drop table t1; + +set names utf8; +create table t1 (s1 char(5) character set utf8 collate utf8_bin); +insert into t1 values +('a'),('b'),(null),('ペテルグル'),('ü'),('Y'); +create index it1 on t1 (s1); +select s1 as before_delete_bin from t1 where s1 like 'ペテ%'; +delete from t1 where s1 = 'Y'; +select s1 as after_delete_bin from t1 where s1 like 'ペテ%'; +drop table t1; + +# additional tests from duplicate bug#20744 MySQL return no result + +set names utf8; +--disable_warnings +create table t1 (a varchar(30) not null primary key) +engine=innodb default character set utf8 collate utf8_general_ci; +--enable_warnings +insert into t1 values ('あいうえおかきくけこさしすせそ'); +insert into t1 values ('さしすせそかきくけこあいうえお'); +select a as gci1 from t1 where a like 'さしすせそかきくけこあいうえお%'; +select a as gci2 from t1 where a like 'あいうえおかきくけこさしすせそ'; +drop table t1; + +set names utf8; +--disable_warnings +create table t1 (a varchar(30) not null primary key) +engine=innodb default character set utf8 collate utf8_unicode_ci; +--enable_warnings +insert into t1 values ('あいうえおかきくけこさしすせそ'); +insert into t1 values ('さしすせそかきくけこあいうえお'); +select a as uci1 from t1 where a like 'さしすせそかきくけこあいうえお%'; +select a as uci2 from t1 where a like 'あいうえおかきくけこさしすせそ'; +drop table t1; + +set names utf8; +--disable_warnings +create table t1 (a varchar(30) not null primary key) +engine=innodb default character set utf8 collate utf8_bin; +--enable_warnings +insert into t1 values ('あいうえおかきくけこさしすせそ'); +insert into t1 values ('さしすせそかきくけこあいうえお'); +select a as bin1 from t1 where a like 'さしすせそかきくけこあいうえお%'; +select a as bin2 from t1 where a like 'あいうえおかきくけこさしすせそ'; +drop table t1; + + + # # Bug#14896: Comparison with a key in a partial index over mb chararacter field # diff --git a/strings/CHARSET_INFO.txt b/strings/CHARSET_INFO.txt index f7a10f95880..3fd262c6f12 100644 --- a/strings/CHARSET_INFO.txt +++ b/strings/CHARSET_INFO.txt @@ -33,7 +33,7 @@ typedef struct charset_info_st uint strxfrm_multiply; uint mbminlen; uint mbmaxlen; - char max_sort_char; /* For LIKE optimization */ + uint16 max_sort_char; /* For LIKE optimization */ MY_CHARSET_HANDLER *cset; MY_COLLATION_HANDLER *coll; @@ -134,7 +134,15 @@ Misc fields mbmaxlen - maximum multibyte sequence length. 1 for 8bit charsets. Can be also 2 or 3. - + max_sort_char - for LIKE range + in case of 8bit character sets - native code + of maximum character (max_str pad byte); + in case of UTF8 and UCS2 - Unicode code of the maximum + possible character (usually U+FFFF). This code is + converted to multibyte representation (usually 0xEFBFBF) + and then used as a pad sequence for max_str. + in case of other multibyte character sets - + max_str pad byte (usually 0xFF). MY_CHARSET_HANDLER ================== diff --git a/strings/ctype-mb.c b/strings/ctype-mb.c index 4f57f7c78e4..6f63592c459 100644 --- a/strings/ctype-mb.c +++ b/strings/ctype-mb.c @@ -449,15 +449,28 @@ static void my_hash_sort_mb_bin(CHARSET_INFO *cs __attribute__((unused)), /* - Write max key: create a buffer with multibyte + Write max key: +- for non-Unicode character sets: + just set to 255. +- for Unicode character set (utf-8): + create a buffer with multibyte representation of the max_sort_char character, and copy it into max_str in a loop. */ static void pad_max_char(CHARSET_INFO *cs, char *str, char *end) { char buf[10]; - char buflen= cs->cset->wc_mb(cs, cs->max_sort_char, (uchar*) buf, - (uchar*) buf + sizeof(buf)); + char buflen; + + if (!(cs->state & MY_CS_UNICODE)) + { + bfill(str, end - str, 255); + return; + } + + buflen= cs->cset->wc_mb(cs, cs->max_sort_char, (uchar*) buf, + (uchar*) buf + sizeof(buf)); + DBUG_ASSERT(buflen > 0); do { @@ -894,7 +907,7 @@ MY_COLLATION_HANDLER my_collation_mb_bin_handler = my_strnncoll_mb_bin, my_strnncollsp_mb_bin, my_strnxfrm_mb_bin, - my_like_range_simple, + my_like_range_mb, my_wildcmp_mb_bin, my_strcasecmp_mb_bin, my_instr_mb, diff --git a/strings/ctype-utf8.c b/strings/ctype-utf8.c index 94e8e6ba797..79239394816 100644 --- a/strings/ctype-utf8.c +++ b/strings/ctype-utf8.c @@ -2373,7 +2373,7 @@ CHARSET_INFO my_charset_utf8_bin= 1, /* mbminlen */ 3, /* mbmaxlen */ 0, /* min_sort_char */ - 255, /* max_sort_char */ + 0xFFFF, /* max_sort_char */ 0, /* escape_with_backslash_is_dangerous */ &my_charset_utf8_handler, &my_collation_mb_bin_handler From 8f44d902bd96592891a61e5822a6fbe5446d11d1 Mon Sep 17 00:00:00 2001 From: "gluh@mysql.com/gluh.(none)" <> Date: Mon, 11 Sep 2006 14:50:46 +0500 Subject: [PATCH 02/60] Bug#20922 mysql removes a name of first column in a table 0xFF is internal separator for SET|ENUM names. If this symbol is present in SET|ENUM names then we replace it with ','(deprecated symbol for SET|ENUM names) during frm creation and restore to 0xFF during frm opening --- mysql-test/r/type_enum.result | 9 +++++++++ mysql-test/t/type_enum.test | 9 +++++++++ sql/table.cc | 16 +++++++++++++++- sql/unireg.cc | 15 +++++++++++++++ 4 files changed, 48 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/type_enum.result b/mysql-test/r/type_enum.result index ab3c441a7e2..0fe3f674fba 100644 --- a/mysql-test/r/type_enum.result +++ b/mysql-test/r/type_enum.result @@ -1745,3 +1745,12 @@ create table t1 (a set('x','y') default 'x'); alter table t1 alter a set default 'z'; ERROR 42000: Invalid default value for 'a' drop table t1; +create table t1 (f1 int); +alter table t1 add f2 enum(0xFFFF); +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `f1` int(11) default NULL, + `f2` enum('') default NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +drop table t1; diff --git a/mysql-test/t/type_enum.test b/mysql-test/t/type_enum.test index 0d479f312cd..68f5664c36d 100644 --- a/mysql-test/t/type_enum.test +++ b/mysql-test/t/type_enum.test @@ -127,4 +127,13 @@ create table t1 (a set('x','y') default 'x'); alter table t1 alter a set default 'z'; drop table t1; + +# +# Bug#20922 mysql removes a name of first column in a table +# +create table t1 (f1 int); +alter table t1 add f2 enum(0xFFFF); +show create table t1; +drop table t1; + # End of 4.1 tests diff --git a/sql/table.cc b/sql/table.cc index 7587531b2f9..f22caf36679 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -387,7 +387,21 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, count))) goto err_not_open; for (count= 0; count < interval->count; count++) - interval->type_lengths[count]= strlen(interval->type_names[count]); + { + char *val= (char*) interval->type_names[count]; + interval->type_lengths[count]= strlen(val); + /* + Replace all ',' symbols with NAMES_SEP_CHAR. + See the comment in unireg.cc, pack_fields() function + for details. + */ + for (uint cnt= 0 ; cnt < interval->type_lengths[count] ; cnt++) + { + char c= val[cnt]; + if (c == ',') + val[cnt]= NAMES_SEP_CHAR; + } + } interval->type_lengths[count]= 0; } } diff --git a/sql/unireg.cc b/sql/unireg.cc index e3bf763f700..16a0a66c3dd 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -637,6 +637,21 @@ static bool pack_fields(File file, List &create_fields, tmp.append(NAMES_SEP_CHAR); for (const char **pos=field->interval->type_names ; *pos ; pos++) { + char *val= (char*) *pos; + uint str_len= strlen(val); + /* + Note, hack: in old frm NAMES_SEP_CHAR is used to separate + names in the interval (ENUM/SET). To allow names to contain + NAMES_SEP_CHAR, we replace it with a comma before writing frm. + Backward conversion is done during frm file opening, + See table.cc, openfrm() function + */ + for (uint cnt= 0 ; cnt < str_len ; cnt++) + { + char c= val[cnt]; + if (c == NAMES_SEP_CHAR) + val[cnt]= ','; + } tmp.append(*pos); tmp.append(NAMES_SEP_CHAR); } From 72af9c2fef8069a9dd289dea7d6a46eea2fd71a5 Mon Sep 17 00:00:00 2001 From: "bar@mysql.com/bar.intranet.mysql.r18.ru" <> Date: Thu, 14 Sep 2006 10:05:07 +0500 Subject: [PATCH 03/60] Better comment text (thanks to SergeyP for suggestions made for the b#20471 patch) --- include/m_ctype.h | 2 +- strings/ctype-mb.c | 21 ++++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/include/m_ctype.h b/include/m_ctype.h index b9ed39414bb..09ebc781c8d 100644 --- a/include/m_ctype.h +++ b/include/m_ctype.h @@ -109,7 +109,7 @@ enum my_lex_states struct charset_info_st; -/* See strings/CHARSET_INFO.txt about information on this structure */ +/* See strings/CHARSET_INFO.txt for information about this structure */ typedef struct my_collation_handler_st { my_bool (*init)(struct charset_info_st *, void *(*alloc)(uint)); diff --git a/strings/ctype-mb.c b/strings/ctype-mb.c index 6f63592c459..bcbc128fc5c 100644 --- a/strings/ctype-mb.c +++ b/strings/ctype-mb.c @@ -449,13 +449,20 @@ static void my_hash_sort_mb_bin(CHARSET_INFO *cs __attribute__((unused)), /* - Write max key: -- for non-Unicode character sets: - just set to 255. -- for Unicode character set (utf-8): - create a buffer with multibyte - representation of the max_sort_char character, - and copy it into max_str in a loop. + Fill the given buffer with 'maximum character' for given charset + SYNOPSIS + pad_max_char() + cs Character set + str Start of buffer to fill + end End of buffer to fill + + DESCRIPTION + Write max key: + - for non-Unicode character sets: + just set to 255. + - for Unicode character set (utf-8): + create a buffer with multibyte representation of the max_sort_char + character, and copy it into max_str in a loop. */ static void pad_max_char(CHARSET_INFO *cs, char *str, char *end) { From e0461067b8d0efdfa24873fae58e0aba10acc27c Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Fri, 29 Sep 2006 00:50:00 +0400 Subject: [PATCH 04/60] Fixed bug#20503: Server crash due to the ORDER clause not taken into account while space allocation Under some circumstances DISTINCT clause can be converted to grouping. In such cases grouping is performed by all items in the select list. If an ORDER clause is present then items from it is prepended to group list. But the case with ORDER wasn't taken into account when allocating the array for sum functions. This leads to memory corruption and crash. The JOIN::alloc_func_list() function now allocates additional space if there is an ORDER by clause is specified and DISTINCT -> GROUP BY optimization is possible. --- mysql-test/r/select.result | 6 ++++++ mysql-test/t/select.test | 11 ++++++++++- sql/sql_select.cc | 12 ++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index 0c62d3f570f..350a05a13c8 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -3517,3 +3517,9 @@ id a b c d e 2 NULL NULL NULL 2 40 2 NULL NULL NULL 2 50 DROP TABLE t1,t2,t3; +create table t1 (c1 varchar(1), c2 int, c3 int, c4 int, c5 int, c6 int, +c7 int, c8 int, c9 int, fulltext key (`c1`)); +select distinct match (`c1`) against ('z') , c2, c3, c4,c5, c6,c7, c8 +from t1 where c9=1 order by c2, c2; +match (`c1`) against ('z') c2 c3 c4 c5 c6 c7 c8 +drop table t1; diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test index 36b3749b4d7..0686f670edf 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -2996,5 +2996,14 @@ SELECT * FROM t1 LEFT JOIN t2 ON t2.b=t1.a INNER JOIN t3 ON t3.d=t1.id SELECT * FROM t1 LEFT JOIN t2 ON t2.b=t1.a INNER JOIN t3 ON t3.d=t1.id WHERE t1.id=2; - DROP TABLE t1,t2,t3; + +# +# Bug#20503: Server crash due to the ORDER clause isn't taken into account +# while space allocation +# +create table t1 (c1 varchar(1), c2 int, c3 int, c4 int, c5 int, c6 int, +c7 int, c8 int, c9 int, fulltext key (`c1`)); +select distinct match (`c1`) against ('z') , c2, c3, c4,c5, c6,c7, c8 + from t1 where c9=1 order by c2, c2; +drop table t1; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index ac4b404ce8e..a412df8cdeb 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -13515,7 +13515,19 @@ bool JOIN::alloc_func_list() disctinct->group_by optimization */ if (select_distinct) + { group_parts+= fields_list.elements; + /* + If the ORDER clause is specified then it's possible that + it also will be optimized, so reserve space for it too + */ + if (order) + { + ORDER *ord; + for (ord= order; ord; ord= ord->next) + group_parts++; + } + } /* This must use calloc() as rollup_make_fields depends on this */ sum_funcs= (Item_sum**) thd->calloc(sizeof(Item_sum**) * (func_count+1) + From 3474fc9afd0476ec382f3ac178f809083cc0573b Mon Sep 17 00:00:00 2001 From: "holyfoot/hf@mysql.com/deer.(none)" <> Date: Fri, 29 Sep 2006 12:16:07 +0500 Subject: [PATCH 05/60] bug #16813 (WITH CHECK OPTION fails with UPDATE) We use the condition from CHECK OPTION twice handling UPDATE command. First we construnct 'update_cond' AND 'option_cond' condition to select records to be updated, then we check the 'option_cond' for the updated row. The problem is that first 'AND' condition is optimized during the 'select' which can break 'option_cond' structure, so it will be unusable for the sectond use - to check the updated row. Possible soultion is either use copy of the condition in the first use or to make optimization less traumatic for the operands. I picked the first one. --- mysql-test/r/view.result | 10 ++++++++++ mysql-test/t/view.test | 15 +++++++++++++++ sql/table.cc | 5 +++-- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 9c05aba168c..f7b75374c8a 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -2935,4 +2935,14 @@ id select_type table type possible_keys key key_len ref rows Extra 2 SUBQUERY t1 ALL NULL NULL NULL NULL 3 DROP VIEW v1; DROP TABLE t1; +CREATE TABLE t1(id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, val INT UNSIGNED NOT NULL); +CREATE VIEW v1 AS SELECT id, val FROM t1 WHERE val >= 1 AND val <= 5 WITH CHECK OPTION; +INSERT INTO v1 (val) VALUES (2); +INSERT INTO v1 (val) VALUES (4); +INSERT INTO v1 (val) VALUES (6); +ERROR HY000: CHECK OPTION failed 'test.v1' +UPDATE v1 SET val=6 WHERE id=2; +ERROR HY000: CHECK OPTION failed 'test.v1' +DROP VIEW v1; +DROP TABLE t1; End of 5.0 tests. diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index 4745804e847..7d25f24fcf9 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -2850,4 +2850,19 @@ EXPLAIN SELECT * FROM v1 t WHERE t.s1+1 < (SELECT MAX(t1.s1) FROM t1); DROP VIEW v1; DROP TABLE t1; + +# +# Bug #16813 (WITH CHECK OPTION doesn't work with UPDATE) +# +CREATE TABLE t1(id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, val INT UNSIGNED NOT NULL); +CREATE VIEW v1 AS SELECT id, val FROM t1 WHERE val >= 1 AND val <= 5 WITH CHECK OPTION; +INSERT INTO v1 (val) VALUES (2); +INSERT INTO v1 (val) VALUES (4); +-- error 1369 +INSERT INTO v1 (val) VALUES (6); +-- error 1369 +UPDATE v1 SET val=6 WHERE id=2; +DROP VIEW v1; +DROP TABLE t1; + --echo End of 5.0 tests. diff --git a/sql/table.cc b/sql/table.cc index 054736401ff..d7b474eca02 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1965,12 +1965,13 @@ bool st_table_list::prep_where(THD *thd, Item **conds, this expression will not be moved to WHERE condition (i.e. will be clean correctly for PS/SP) */ - tbl->on_expr= and_conds(tbl->on_expr, where); + tbl->on_expr= and_conds(tbl->on_expr, + where->copy_andor_structure(thd)); break; } } if (tbl == 0) - *conds= and_conds(*conds, where); + *conds= and_conds(*conds, where->copy_andor_structure(thd)); if (arena) thd->restore_active_arena(arena, &backup); where_processed= TRUE; From 8190b7231b14a4d8773730405edeb7fb07784650 Mon Sep 17 00:00:00 2001 From: "holyfoot/hf@mysql.com/deer.(none)" <> Date: Fri, 29 Sep 2006 17:56:02 +0500 Subject: [PATCH 06/60] bug #21888 (Query on GEOMETRY field crashes the server) RTree keys are really different from BTree and need specific paramters to be set by optimizer to work. Sometimes optimizer doesn't set those properly. Here we decided just to add code to check that the parameters are correct. Hope to fix optimizer sometimes. --- myisam/mi_range.c | 15 +++++++++++++++ mysql-test/r/gis-rtree.result | 11 +++++++++++ mysql-test/t/gis-rtree.test | 10 ++++++++++ 3 files changed, 36 insertions(+) diff --git a/myisam/mi_range.c b/myisam/mi_range.c index 1e0fd42334e..de042845d1e 100644 --- a/myisam/mi_range.c +++ b/myisam/mi_range.c @@ -71,6 +71,21 @@ ha_rows mi_records_in_range(MI_INFO *info, int inx, key_range *min_key, uchar * key_buff; uint start_key_len; + /* + The problem is that the optimizer doesn't support + RTree keys properly at the moment. + Hope this will be fixed some day. + But now NULL in the min_key means that we + didn't make the task for the RTree key + and expect BTree functionality from it. + As it's not able to handle such request + we return the error. + */ + if (!min_key) + { + res= HA_POS_ERROR; + break; + } key_buff= info->lastkey+info->s->base.max_key_length; start_key_len= _mi_pack_key(info,inx, key_buff, (uchar*) min_key->key, min_key->length, diff --git a/mysql-test/r/gis-rtree.result b/mysql-test/r/gis-rtree.result index 3fcae8843c0..bdb3de4de75 100644 --- a/mysql-test/r/gis-rtree.result +++ b/mysql-test/r/gis-rtree.result @@ -857,3 +857,14 @@ CHECK TABLE t1 EXTENDED; Table Op Msg_type Msg_text test.t1 check status OK DROP TABLE t1; +CREATE TABLE t1 (foo GEOMETRY NOT NULL, SPATIAL INDEX(foo) ); +INSERT INTO t1 (foo) VALUES (PointFromWKB(POINT(1,1))); +INSERT INTO t1 (foo) VALUES (PointFromWKB(POINT(1,0))); +INSERT INTO t1 (foo) VALUES (PointFromWKB(POINT(0,1))); +INSERT INTO t1 (foo) VALUES (PointFromWKB(POINT(0,0))); +SELECT 1 FROM t1 WHERE foo != PointFromWKB(POINT(0,0)); +1 +1 +1 +1 +DROP TABLE t1; diff --git a/mysql-test/t/gis-rtree.test b/mysql-test/t/gis-rtree.test index eba53a8a9c5..cdd8d1f3f0f 100644 --- a/mysql-test/t/gis-rtree.test +++ b/mysql-test/t/gis-rtree.test @@ -232,4 +232,14 @@ INSERT INTO t1 (c1) VALUES ( CHECK TABLE t1 EXTENDED; DROP TABLE t1; +# +# Bug #21888: Query on GEOMETRY field using PointFromWKB() results in lost connection +# +CREATE TABLE t1 (foo GEOMETRY NOT NULL, SPATIAL INDEX(foo) ); +INSERT INTO t1 (foo) VALUES (PointFromWKB(POINT(1,1))); +INSERT INTO t1 (foo) VALUES (PointFromWKB(POINT(1,0))); +INSERT INTO t1 (foo) VALUES (PointFromWKB(POINT(0,1))); +INSERT INTO t1 (foo) VALUES (PointFromWKB(POINT(0,0))); +SELECT 1 FROM t1 WHERE foo != PointFromWKB(POINT(0,0)); +DROP TABLE t1; # End of 4.1 tests From 89028dbba42b92d7577ec6b34200b2787b9843eb Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Fri, 29 Sep 2006 07:43:25 -0700 Subject: [PATCH 07/60] Fixed bug #22753. After the patch for big 21698 equality propagation stopped working for BETWEEN and IN predicates with STRING arguments. This changeset completes the solution of the above patch. --- mysql-test/r/select.result | 29 +++++++++++++++++++++++++++++ mysql-test/t/select.test | 29 ++++++++++++++++++++++++++++- sql/item_cmpfunc.h | 1 + 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index 0c62d3f570f..34b2a93e39b 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -3517,3 +3517,32 @@ id a b c d e 2 NULL NULL NULL 2 40 2 NULL NULL NULL 2 50 DROP TABLE t1,t2,t3; +CREATE TABLE t1 (pk varchar(10) PRIMARY KEY, fk varchar(16)); +CREATE TABLE t2 (pk varchar(16) PRIMARY KEY, fk varchar(10)); +INSERT INTO t1 VALUES +('d','dddd'), ('i','iii'), ('a','aa'), ('b','bb'), ('g','gg'), +('e','eee'), ('c','cccc'), ('h','hhh'), ('j','jjj'), ('f','fff'); +INSERT INTO t2 VALUES +('jjj', 'j'), ('cc','c'), ('ccc','c'), ('aaa', 'a'), ('jjjj','j'), +('hhh','h'), ('gg','g'), ('fff','f'), ('ee','e'), ('ffff','f'), +('bbb','b'), ('ff','f'), ('cccc','c'), ('dddd','d'), ('jj','j'), +('aaaa','a'), ('bb','b'), ('eeee','e'), ('aa','a'), ('hh','h'); +EXPLAIN SELECT t2.* +FROM t1 JOIN t2 ON t2.fk=t1.pk +WHERE t2.fk < 'c' AND t2.pk=t1.fk; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range PRIMARY PRIMARY 12 NULL 3 Using where +1 SIMPLE t2 ref PRIMARY PRIMARY 18 test.t1.fk 1 Using where +EXPLAIN SELECT t2.* +FROM t1 JOIN t2 ON t2.fk=t1.pk +WHERE t2.fk BETWEEN 'a' AND 'b' AND t2.pk=t1.fk; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range PRIMARY PRIMARY 12 NULL 2 Using where +1 SIMPLE t2 ref PRIMARY PRIMARY 18 test.t1.fk 1 Using where +EXPLAIN SELECT t2.* +FROM t1 JOIN t2 ON t2.fk=t1.pk +WHERE t2.fk IN ('a','b') AND t2.pk=t1.fk; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range PRIMARY PRIMARY 12 NULL 2 Using where +1 SIMPLE t2 ref PRIMARY PRIMARY 18 test.t1.fk 1 Using where +DROP TABLE t1,t2; diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test index 36b3749b4d7..861b9160644 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -2996,5 +2996,32 @@ SELECT * FROM t1 LEFT JOIN t2 ON t2.b=t1.a INNER JOIN t3 ON t3.d=t1.id SELECT * FROM t1 LEFT JOIN t2 ON t2.b=t1.a INNER JOIN t3 ON t3.d=t1.id WHERE t1.id=2; - DROP TABLE t1,t2,t3; + +# +# Bug #22735: no equality propagation for BETWEEN and IN with STRING arguments +# + +CREATE TABLE t1 (pk varchar(10) PRIMARY KEY, fk varchar(16)); +CREATE TABLE t2 (pk varchar(16) PRIMARY KEY, fk varchar(10)); + +INSERT INTO t1 VALUES + ('d','dddd'), ('i','iii'), ('a','aa'), ('b','bb'), ('g','gg'), + ('e','eee'), ('c','cccc'), ('h','hhh'), ('j','jjj'), ('f','fff'); +INSERT INTO t2 VALUES + ('jjj', 'j'), ('cc','c'), ('ccc','c'), ('aaa', 'a'), ('jjjj','j'), + ('hhh','h'), ('gg','g'), ('fff','f'), ('ee','e'), ('ffff','f'), + ('bbb','b'), ('ff','f'), ('cccc','c'), ('dddd','d'), ('jj','j'), + ('aaaa','a'), ('bb','b'), ('eeee','e'), ('aa','a'), ('hh','h'); + +EXPLAIN SELECT t2.* + FROM t1 JOIN t2 ON t2.fk=t1.pk + WHERE t2.fk < 'c' AND t2.pk=t1.fk; +EXPLAIN SELECT t2.* + FROM t1 JOIN t2 ON t2.fk=t1.pk + WHERE t2.fk BETWEEN 'a' AND 'b' AND t2.pk=t1.fk; +EXPLAIN SELECT t2.* + FROM t1 JOIN t2 ON t2.fk=t1.pk + WHERE t2.fk IN ('a','b') AND t2.pk=t1.fk; + +DROP TABLE t1,t2; diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index f2c43833bd9..c8439cba303 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -446,6 +446,7 @@ public: negated= !negated; return this; } + bool subst_argument_checker(byte **arg) { return TRUE; } }; From 0c9f941bb26da9e24ad2f59b4308333d6fb7fc78 Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Fri, 29 Sep 2006 20:02:53 +0400 Subject: [PATCH 08/60] Fixed bug#20825: rollup puts non-equal values together Fix for bug 7894 replaces a field(s) in a non-aggregate function with a item reference if such a field was specified in the GROUP BY clause in order to get a correct result. When ROLLUP is involved this lead to a wrong result due to value of a such field is got through a copy function and copying happens after the function evaluation. Such replacement isn't needed if grouping is also done by such a function. The change_group_ref() function now isn't called for a function present in the group list. --- mysql-test/r/olap.result | 15 +++++++++++++++ mysql-test/t/olap.test | 9 +++++++++ sql/sql_select.cc | 7 ++++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/olap.result b/mysql-test/r/olap.result index fef990297d9..74b7570ea2a 100644 --- a/mysql-test/r/olap.result +++ b/mysql-test/r/olap.result @@ -541,3 +541,18 @@ a max(b) NULL 2 a 1 drop table t1; +create table t1 (a varchar(22) not null , b int); +insert into t1 values ("2006-07-01 21:30", 1), ("2006-07-01 23:30", 10); +select left(a,10), a, sum(b) from t1 group by 1,2 with rollup; +left(a,10) a sum(b) +2006-07-01 2006-07-01 21:30 1 +2006-07-01 2006-07-01 23:30 10 +2006-07-01 NULL 11 +NULL NULL 11 +select left(a,10) x, a, sum(b) from t1 group by x,a with rollup; +x a sum(b) +2006-07-01 2006-07-01 21:30 1 +2006-07-01 2006-07-01 23:30 10 +2006-07-01 NULL 11 +NULL NULL 11 +drop table t1; diff --git a/mysql-test/t/olap.test b/mysql-test/t/olap.test index 4f9790b0de6..683e1402678 100644 --- a/mysql-test/t/olap.test +++ b/mysql-test/t/olap.test @@ -272,4 +272,13 @@ select a, max(b) from t1 group by a with rollup; select distinct a, max(b) from t1 group by a with rollup; drop table t1; +# +# Bug #20825: rollup puts non-equal values together +# +create table t1 (a varchar(22) not null , b int); +insert into t1 values ("2006-07-01 21:30", 1), ("2006-07-01 23:30", 10); +select left(a,10), a, sum(b) from t1 group by 1,2 with rollup; +select left(a,10) x, a, sum(b) from t1 group by x,a with rollup; +drop table t1; + # End of 4.1 tests diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 74a9fc573c4..57b7fcceebe 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -9706,12 +9706,17 @@ bool JOIN::rollup_init() while ((item= it++)) { ORDER *group_tmp; + bool found_in_group= 0; + for (group_tmp= group_list; group_tmp; group_tmp= group_tmp->next) { if (*group_tmp->item == item) + { item->maybe_null= 1; + found_in_group= 1; + } } - if (item->type() == Item::FUNC_ITEM) + if (item->type() == Item::FUNC_ITEM && !found_in_group) { bool changed= FALSE; if (change_group_ref(thd, (Item_func *) item, group_list, &changed)) From 13633864bbea56928291443bdafa00dec0be6825 Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@macbook.local" <> Date: Mon, 9 Oct 2006 19:51:41 +0400 Subject: [PATCH 09/60] Bug #22781: SQL_BIG_RESULT fails to influence sort plan Currently SQL_BIG_RESULT is checked only at compile time. However, additional optimizations may take place after this check that change the sort method from 'filesort' to sorting via index. As a result the actual plan executed is not the one specified by the SQL_BIG_RESULT hint. Similarly, there is no such test when executing EXPLAIN, resulting in incorrect output. The patch corrects the problem by testing for SQL_BIG_RESULT both during the explain and execution phases. --- mysql-test/r/bdb.result | 72 ++++++++++++++++---------------- mysql-test/r/group_by.result | 67 ++++++++++++++++++++++++++++- mysql-test/r/innodb.result | 16 +++---- mysql-test/r/innodb_mysql.result | 16 +++++++ mysql-test/r/myisam.result | 16 +++---- mysql-test/t/group_by.test | 22 ++++++++++ mysql-test/t/innodb_mysql.test | 18 ++++++++ sql/sql_select.cc | 13 ++++-- 8 files changed, 183 insertions(+), 57 deletions(-) diff --git a/mysql-test/r/bdb.result b/mysql-test/r/bdb.result index ee7cdceefda..c5b3b1f86e7 100644 --- a/mysql-test/r/bdb.result +++ b/mysql-test/r/bdb.result @@ -1509,27 +1509,27 @@ i 10 select sql_big_result v,count(t) from t1 group by v limit 10; v count(t) a 1 -a 10 -b 10 -c 10 -d 10 -e 10 -f 10 -g 10 -h 10 -i 10 +a 10 +b 10 +c 10 +d 10 +e 10 +f 10 +g 10 +h 10 +i 10 select sql_big_result v,count(c) from t1 group by v limit 10; v count(c) a 1 -a 10 -b 10 -c 10 -d 10 -e 10 -f 10 -g 10 -h 10 -i 10 +a 10 +b 10 +c 10 +d 10 +e 10 +f 10 +g 10 +h 10 +i 10 select c,count(*) from t1 group by c limit 10; c count(*) a 1 @@ -1673,15 +1673,15 @@ i 10 select sql_big_result v,count(t) from t1 group by v limit 10; v count(t) a 1 -a 10 -b 10 -c 10 -d 10 -e 10 -f 10 -g 10 -h 10 -i 10 +a 10 +b 10 +c 10 +d 10 +e 10 +f 10 +g 10 +h 10 +i 10 alter table t1 drop key v, add key v (v(30)); show create table t1; Table Create Table @@ -1800,15 +1800,15 @@ i 10 select sql_big_result v,count(t) from t1 group by v limit 10; v count(t) a 1 -a 10 -b 10 -c 10 -d 10 -e 10 -f 10 -g 10 -h 10 -i 10 +a 10 +b 10 +c 10 +d 10 +e 10 +f 10 +g 10 +h 10 +i 10 drop table t1; create table t1 (a char(10), unique (a)); insert into t1 values ('a '); diff --git a/mysql-test/r/group_by.result b/mysql-test/r/group_by.result index 5eb2e5744c1..5a55abbc923 100644 --- a/mysql-test/r/group_by.result +++ b/mysql-test/r/group_by.result @@ -303,10 +303,10 @@ spid sum(userid) 1 1 explain select sql_big_result score,count(*) from t1 group by score desc; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 index NULL score 3 NULL 8 Using index +1 SIMPLE t1 index NULL score 3 NULL 8 Using index; Using filesort explain select sql_big_result score,count(*) from t1 group by score desc order by null; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 index NULL score 3 NULL 8 Using index +1 SIMPLE t1 index NULL score 3 NULL 8 Using index; Using filesort select sql_big_result score,count(*) from t1 group by score desc; score count(*) 3 5 @@ -821,3 +821,66 @@ a b real_b 68 France France DROP VIEW v1; DROP TABLE t1,t2; +CREATE TABLE t1 (a INT PRIMARY KEY, b INT, key (b)); +INSERT INTO t1 VALUES (1, 1); +INSERT INTO t1 SELECT a + 1 , MOD(a + 1 , 20) FROM t1; +INSERT INTO t1 SELECT a + 2 , MOD(a + 2 , 20) FROM t1; +INSERT INTO t1 SELECT a + 4 , MOD(a + 4 , 20) FROM t1; +INSERT INTO t1 SELECT a + 8 , MOD(a + 8 , 20) FROM t1; +INSERT INTO t1 SELECT a + 16, MOD(a + 16, 20) FROM t1; +INSERT INTO t1 SELECT a + 32, MOD(a + 32, 20) FROM t1; +INSERT INTO t1 SELECT a + 64, MOD(a + 64, 20) FROM t1; +SELECT MIN(b), MAX(b) from t1; +MIN(b) MAX(b) +0 19 +EXPLAIN SELECT b, sum(1) FROM t1 GROUP BY b; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index NULL b 5 NULL 128 Using index +EXPLAIN SELECT SQL_BIG_RESULT b, sum(1) FROM t1 GROUP BY b; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index NULL b 5 NULL 128 Using index; Using filesort +SELECT b, sum(1) FROM t1 GROUP BY b; +b sum(1) +0 6 +1 7 +2 7 +3 7 +4 7 +5 7 +6 7 +7 7 +8 7 +9 6 +10 6 +11 6 +12 6 +13 6 +14 6 +15 6 +16 6 +17 6 +18 6 +19 6 +SELECT SQL_BIG_RESULT b, sum(1) FROM t1 GROUP BY b; +b sum(1) +0 6 +1 7 +2 7 +3 7 +4 7 +5 7 +6 7 +7 7 +8 7 +9 6 +10 6 +11 6 +12 6 +13 6 +14 6 +15 6 +16 6 +17 6 +18 6 +19 6 +DROP TABLE t1; diff --git a/mysql-test/r/innodb.result b/mysql-test/r/innodb.result index 1d1f26e4b01..38d71ac7a42 100644 --- a/mysql-test/r/innodb.result +++ b/mysql-test/r/innodb.result @@ -2070,15 +2070,15 @@ i 10 select sql_big_result v,count(c) from t1 group by v limit 10; v count(c) a 1 -a 10 -b 10 -c 10 -d 10 -e 10 -f 10 -g 10 +a 10 +b 10 +c 10 +d 10 +e 10 +f 10 +g 10 h 10 -i 10 +i 10 select c,count(*) from t1 group by c limit 10; c count(*) a 1 diff --git a/mysql-test/r/innodb_mysql.result b/mysql-test/r/innodb_mysql.result index b4101e037f2..c4564251a35 100644 --- a/mysql-test/r/innodb_mysql.result +++ b/mysql-test/r/innodb_mysql.result @@ -337,3 +337,19 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2 index NULL fkey 5 NULL 5 Using index 1 SIMPLE t1 eq_ref PRIMARY PRIMARY 4 test.t2.fkey 1 Using where DROP TABLE t1,t2; +CREATE TABLE t1 (a INT PRIMARY KEY, b INT, c FLOAT, KEY b(b)) ENGINE = INNODB; +INSERT INTO t1 VALUES ( 1 , 1 , 1); +INSERT INTO t1 SELECT a + 1 , MOD(a + 1 , 20), 1 FROM t1; +INSERT INTO t1 SELECT a + 2 , MOD(a + 2 , 20), 1 FROM t1; +INSERT INTO t1 SELECT a + 4 , MOD(a + 4 , 20), 1 FROM t1; +INSERT INTO t1 SELECT a + 8 , MOD(a + 8 , 20), 1 FROM t1; +INSERT INTO t1 SELECT a + 16, MOD(a + 16, 20), 1 FROM t1; +INSERT INTO t1 SELECT a + 32, MOD(a + 32, 20), 1 FROM t1; +INSERT INTO t1 SELECT a + 64, MOD(a + 64, 20), 1 FROM t1; +EXPLAIN SELECT b, SUM(c) FROM t1 GROUP BY b; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index NULL b 5 NULL 128 +EXPLAIN SELECT SQL_BIG_RESULT b, SUM(c) FROM t1 GROUP BY b; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 128 Using filesort +DROP TABLE t1; diff --git a/mysql-test/r/myisam.result b/mysql-test/r/myisam.result index 0021e6717ee..b1d5765a304 100644 --- a/mysql-test/r/myisam.result +++ b/mysql-test/r/myisam.result @@ -1002,15 +1002,15 @@ i 10 select sql_big_result v,count(c) from t1 group by v limit 10; v count(c) a 1 -a 10 -b 10 -c 10 -d 10 -e 10 -f 10 -g 10 +a 10 +b 10 +c 10 +d 10 +e 10 +f 10 +g 10 h 10 -i 10 +i 10 select c,count(*) from t1 group by c limit 10; c count(*) a 1 diff --git a/mysql-test/t/group_by.test b/mysql-test/t/group_by.test index ce1e4e59600..5d47f64f3d1 100644 --- a/mysql-test/t/group_by.test +++ b/mysql-test/t/group_by.test @@ -655,3 +655,25 @@ where t2.b=v1.a GROUP BY t2.b; DROP VIEW v1; DROP TABLE t1,t2; + +# +# Bug#22781: SQL_BIG_RESULT fails to influence sort plan +# +CREATE TABLE t1 (a INT PRIMARY KEY, b INT, key (b)); + +INSERT INTO t1 VALUES (1, 1); +INSERT INTO t1 SELECT a + 1 , MOD(a + 1 , 20) FROM t1; +INSERT INTO t1 SELECT a + 2 , MOD(a + 2 , 20) FROM t1; +INSERT INTO t1 SELECT a + 4 , MOD(a + 4 , 20) FROM t1; +INSERT INTO t1 SELECT a + 8 , MOD(a + 8 , 20) FROM t1; +INSERT INTO t1 SELECT a + 16, MOD(a + 16, 20) FROM t1; +INSERT INTO t1 SELECT a + 32, MOD(a + 32, 20) FROM t1; +INSERT INTO t1 SELECT a + 64, MOD(a + 64, 20) FROM t1; + +SELECT MIN(b), MAX(b) from t1; + +EXPLAIN SELECT b, sum(1) FROM t1 GROUP BY b; +EXPLAIN SELECT SQL_BIG_RESULT b, sum(1) FROM t1 GROUP BY b; +SELECT b, sum(1) FROM t1 GROUP BY b; +SELECT SQL_BIG_RESULT b, sum(1) FROM t1 GROUP BY b; +DROP TABLE t1; diff --git a/mysql-test/t/innodb_mysql.test b/mysql-test/t/innodb_mysql.test index 59dbe5e96d4..4d87e9dfb02 100644 --- a/mysql-test/t/innodb_mysql.test +++ b/mysql-test/t/innodb_mysql.test @@ -302,3 +302,21 @@ SELECT COUNT(*) FROM t2 LEFT JOIN t1 ON t2.fkey = t1.id WHERE t1.name LIKE 'A%' OR FALSE; DROP TABLE t1,t2; + +# +# Bug#22781: SQL_BIG_RESULT fails to influence sort plan +# +CREATE TABLE t1 (a INT PRIMARY KEY, b INT, c FLOAT, KEY b(b)) ENGINE = INNODB; + +INSERT INTO t1 VALUES ( 1 , 1 , 1); +INSERT INTO t1 SELECT a + 1 , MOD(a + 1 , 20), 1 FROM t1; +INSERT INTO t1 SELECT a + 2 , MOD(a + 2 , 20), 1 FROM t1; +INSERT INTO t1 SELECT a + 4 , MOD(a + 4 , 20), 1 FROM t1; +INSERT INTO t1 SELECT a + 8 , MOD(a + 8 , 20), 1 FROM t1; +INSERT INTO t1 SELECT a + 16, MOD(a + 16, 20), 1 FROM t1; +INSERT INTO t1 SELECT a + 32, MOD(a + 32, 20), 1 FROM t1; +INSERT INTO t1 SELECT a + 64, MOD(a + 64, 20), 1 FROM t1; + +EXPLAIN SELECT b, SUM(c) FROM t1 GROUP BY b; +EXPLAIN SELECT SQL_BIG_RESULT b, SUM(c) FROM t1 GROUP BY b; +DROP TABLE t1; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index b328d9162d5..e9c282c1d24 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1399,7 +1399,8 @@ JOIN::exec() simple_order= simple_group; skip_sort_order= 0; } - if (order && + if (order && + (order != group_list || !(select_options & SELECT_BIG_RESULT)) && (const_tables == tables || ((simple_order || skip_sort_order) && test_if_skip_sort_order(&join_tab[const_tables], order, @@ -11995,11 +11996,17 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order, table= tab->table; select= tab->select; - if (test_if_skip_sort_order(tab,order,select_limit,0)) + /* + When there is SQL_BIG_RESULT do not sort using index for GROUP BY, + and thus force sorting on disk. + */ + if ((order != join->group_list || + !(join->select_options & SELECT_BIG_RESULT)) && + test_if_skip_sort_order(tab,order,select_limit,0)) DBUG_RETURN(0); if (!(sortorder=make_unireg_sortorder(order,&length))) goto err; /* purecov: inspected */ - /* It's not fatal if the following alloc fails */ + table->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE), MYF(MY_WME | MY_ZEROFILL)); table->status=0; // May be wrong if quick_select From 5f08a831863c28561b6b99f8f8246a3a24b2f2c2 Mon Sep 17 00:00:00 2001 From: "istruewing@chilla.local" <> Date: Mon, 9 Oct 2006 19:26:55 +0200 Subject: [PATCH 10/60] Bug#8283 - OPTIMIZE TABLE causes data loss OPTIMIZE TABLE with myisam_repair_threads > 1 performs a non-quick parallel repair. This means that it does not only rebuild all indexes, but also the data file. Non-quick parallel repair works so that there is one thread per index. The first of the threads rebuilds also the new data file. The problem was that all threads shared the read io cache on the old data file. If there were holes (deleted records) in the table, the first thread skipped them, writing only contiguous, non-deleted records to the new data file. Then it built the new index so that its entries pointed to the correct record positions. But the other threads didn't know the new record positions, but put the positions from the old data file into the index. The new design is so that there is a shared io cache which is filled by the first thread (the data file writer) with the new contiguous records and read by the other threads. Now they know the new record positions. Another problem was that for the parallel repair of compressed tables a common bit_buff and rec_buff was used. I changed it so that thread specific buffers are used for parallel repair. A similar problem existed for checksum calculation. I made this multi-thread safe too. --- include/my_sys.h | 22 +- include/myisam.h | 2 +- myisam/mi_check.c | 306 +++++++++++++++---- myisam/mi_open.c | 5 +- myisam/mi_packrec.c | 76 +++-- myisam/myisamdef.h | 30 +- myisam/sort.c | 126 ++++---- mysql-test/r/myisam.result | 126 ++++++++ mysql-test/t/myisam.test | 93 ++++++ mysys/mf_iocache.c | 584 +++++++++++++++++++++++++++++++------ 10 files changed, 1135 insertions(+), 235 deletions(-) diff --git a/include/my_sys.h b/include/my_sys.h index 02ea188a18e..92f3758a846 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -325,12 +325,18 @@ typedef int (*IO_CACHE_CALLBACK)(struct st_io_cache*); #ifdef THREAD typedef struct st_io_cache_share { - /* to sync on reads into buffer */ - pthread_mutex_t mutex; - pthread_cond_t cond; - int count, total; - /* actual IO_CACHE that filled the buffer */ - struct st_io_cache *active; + pthread_mutex_t mutex; /* To sync on reads into buffer. */ + pthread_cond_t cond; /* To wait for signals. */ + pthread_cond_t cond_writer; /* For a synchronized writer. */ + /* Offset in file corresponding to the first byte of buffer. */ + my_off_t pos_in_file; + /* If a synchronized write cache is the source of the data. */ + struct st_io_cache *source_cache; + byte *buffer; /* The read buffer. */ + byte *read_end; /* Behind last valid byte of buffer. */ + int running_threads; /* threads not in lock. */ + int total_threads; /* threads sharing the cache. */ + int error; /* Last error. */ #ifdef NOT_YET_IMPLEMENTED /* whether the structure should be free'd */ my_bool alloced; @@ -672,8 +678,8 @@ extern void setup_io_cache(IO_CACHE* info); extern int _my_b_read(IO_CACHE *info,byte *Buffer,uint Count); #ifdef THREAD extern int _my_b_read_r(IO_CACHE *info,byte *Buffer,uint Count); -extern void init_io_cache_share(IO_CACHE *info, - IO_CACHE_SHARE *s, uint num_threads); +extern void init_io_cache_share(IO_CACHE *read_cache, IO_CACHE_SHARE *cshare, + IO_CACHE *write_cache, uint num_threads); extern void remove_io_thread(IO_CACHE *info); #endif extern int _my_b_seq_read(IO_CACHE *info,byte *Buffer,uint Count); diff --git a/include/myisam.h b/include/myisam.h index c2d3d99a414..0a808070748 100644 --- a/include/myisam.h +++ b/include/myisam.h @@ -345,7 +345,7 @@ typedef struct st_mi_check_param uint testflag, key_cache_block_size; uint8 language; my_bool using_global_keycache, opt_lock_memory, opt_follow_links; - my_bool retry_repair, force_sort, calc_checksum; + my_bool retry_repair, force_sort; char temp_filename[FN_REFLEN],*isam_file_name; MY_TMPDIR *tmpdir; int tmpfile_createflag; diff --git a/myisam/mi_check.c b/myisam/mi_check.c index 2395640d5bf..35b56419367 100644 --- a/myisam/mi_check.c +++ b/myisam/mi_check.c @@ -16,6 +16,31 @@ /* Describe, check and repair of MyISAM tables */ +/* + About checksum calculation. + + There are two types of checksums. Table checksum and row checksum. + + Row checksum is an additional byte at the end of dynamic length + records. It must be calculated if the table is configured for them. + Otherwise they must not be used. The variable + MYISAM_SHARE::calc_checksum determines if row checksums are used. + MI_INFO::checksum is used as temporary storage during row handling. + For parallel repair we must assure that only one thread can use this + variable. There is no problem on the write side as this is done by one + thread only. But when checking a record after read this could go + wrong. But since all threads read through a common read buffer, it is + sufficient if only one thread checks it. + + Table checksum is an eight byte value in the header of the index file. + It can be calculated even if row checksums are not used. The variable + MI_CHECK::glob_crc is calculated over all records. + MI_SORT_PARAM::calc_checksum determines if this should be done. This + variable is not part of MI_CHECK because it must be set per thread for + parallel repair. The global glob_crc must be changed by one thread + only. And it is sufficient to calculate the checksum once only. +*/ + #include "ftdefs.h" #include #include @@ -41,8 +66,7 @@ static int chk_index(MI_CHECK *param, MI_INFO *info,MI_KEYDEF *keyinfo, ha_checksum *key_checksum, uint level); static uint isam_key_length(MI_INFO *info,MI_KEYDEF *keyinfo); static ha_checksum calc_checksum(ha_rows count); -static int writekeys(MI_CHECK *param, MI_INFO *info,byte *buff, - my_off_t filepos); +static int writekeys(MI_SORT_PARAM *sort_param); static int sort_one_index(MI_CHECK *param, MI_INFO *info,MI_KEYDEF *keyinfo, my_off_t pagepos, File new_file); static int sort_key_read(MI_SORT_PARAM *sort_param,void *key); @@ -1101,7 +1125,8 @@ int chk_data_link(MI_CHECK *param, MI_INFO *info,int extend) goto err; start_recpos=pos; splits++; - VOID(_mi_pack_get_block_info(info,&block_info, -1, start_recpos)); + VOID(_mi_pack_get_block_info(info, &info->bit_buff, &block_info, + &info->rec_buff, -1, start_recpos)); pos=block_info.filepos+block_info.rec_len; if (block_info.rec_len < (uint) info->s->min_pack_length || block_info.rec_len > (uint) info->s->max_pack_length) @@ -1115,7 +1140,8 @@ int chk_data_link(MI_CHECK *param, MI_INFO *info,int extend) if (_mi_read_cache(¶m->read_cache,(byte*) info->rec_buff, block_info.filepos, block_info.rec_len, READING_NEXT)) goto err; - if (_mi_pack_rec_unpack(info,record,info->rec_buff,block_info.rec_len)) + if (_mi_pack_rec_unpack(info, &info->bit_buff, record, + info->rec_buff, block_info.rec_len)) { mi_check_print_error(param,"Found wrong record at %s", llstr(start_recpos,llbuff)); @@ -1394,7 +1420,7 @@ int mi_repair(MI_CHECK *param, register MI_INFO *info, info->state->empty=0; param->glob_crc=0; if (param->testflag & T_CALC_CHECKSUM) - param->calc_checksum=1; + sort_param.calc_checksum= 1; info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED); for (i=0 ; i < info->s->base.keys ; i++) @@ -1418,7 +1444,7 @@ int mi_repair(MI_CHECK *param, register MI_INFO *info, lock_memory(param); /* Everything is alloced */ while (!(error=sort_get_next_record(&sort_param))) { - if (writekeys(param,info,(byte*)sort_param.record,sort_param.filepos)) + if (writekeys(&sort_param)) { if (my_errno != HA_ERR_FOUND_DUPP_KEY) goto err; @@ -1563,11 +1589,13 @@ err: /* Uppate keyfile when doing repair */ -static int writekeys(MI_CHECK *param, register MI_INFO *info, byte *buff, - my_off_t filepos) +static int writekeys(MI_SORT_PARAM *sort_param) { register uint i; - uchar *key; + uchar *key; + MI_INFO *info= sort_param->sort_info->info; + byte *buff= sort_param->record; + my_off_t filepos= sort_param->filepos; DBUG_ENTER("writekeys"); key=info->lastkey+info->s->base.max_key_length; @@ -1621,8 +1649,8 @@ static int writekeys(MI_CHECK *param, register MI_INFO *info, byte *buff, } } /* Remove checksum that was added to glob_crc in sort_get_next_record */ - if (param->calc_checksum) - param->glob_crc-= info->checksum; + if (sort_param->calc_checksum) + sort_param->sort_info->param->glob_crc-= info->checksum; DBUG_PRINT("error",("errno: %d",my_errno)); DBUG_RETURN(-1); } /* writekeys */ @@ -2127,7 +2155,7 @@ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info, del=info->state->del; param->glob_crc=0; if (param->testflag & T_CALC_CHECKSUM) - param->calc_checksum=1; + sort_param.calc_checksum= 1; rec_per_key_part= param->rec_per_key_part; for (sort_param.key=0 ; sort_param.key < share->base.keys ; @@ -2189,7 +2217,8 @@ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info, param->retry_repair=1; goto err; } - param->calc_checksum=0; /* No need to calc glob_crc */ + /* No need to calculate checksum again. */ + sort_param.calc_checksum= 0; /* Set for next loop */ sort_info.max_records= (ha_rows) info->state->records; @@ -2352,6 +2381,28 @@ err: Each key is handled by a separate thread. TODO: make a number of threads a parameter + In parallel repair we use one thread per index. There are two modes: + + Quick + + Only the indexes are rebuilt. All threads share a read buffer. + Every thread that needs fresh data in the buffer enters the shared + cache lock. The last thread joining the lock reads the buffer from + the data file and wakes all other threads. + + Non-quick + + The data file is rebuilt and all indexes are rebuilt to point to + the new record positions. One thread is the master thread. It + reads from the old data file and writes to the new data file. It + also creates one of the indexes. The other threads read from a + buffer which is filled by the master. If they need fresh data, + they enter the shared cache lock. If the masters write buffer is + full, it flushes it to the new data file and enters the shared + cache lock too. When all threads joined in the lock, the master + copies its write buffer to the read buffer for the other threads + and wakes them. + RESULT 0 ok <>0 Error @@ -2374,6 +2425,7 @@ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info, ulong *rec_per_key_part; HA_KEYSEG *keyseg; char llbuff[22]; + IO_CACHE new_data_cache; /* For non-quick repair. */ IO_CACHE_SHARE io_share; SORT_INFO sort_info; ulonglong key_map=share->state.key_map; @@ -2395,19 +2447,55 @@ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info, if (info->s->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD)) param->testflag|=T_CALC_CHECKSUM; + /* + Quick repair (not touching data file, rebuilding indexes): + { + Read cache is (MI_CHECK *param)->read_cache using info->dfile. + } + + Non-quick repair (rebuilding data file and indexes): + { + Master thread: + + Read cache is (MI_CHECK *param)->read_cache using info->dfile. + Write cache is (MI_INFO *info)->rec_cache using new_file. + + Slave threads: + + Read cache is new_data_cache synced to master rec_cache. + + The final assignment of the filedescriptor for rec_cache is done + after the cache creation. + + Don't check file size on new_data_cache, as the resulting file size + is not known yet. + + As rec_cache and new_data_cache are synced, write_buffer_length is + used for the read cache 'new_data_cache'. Both start at the same + position 'new_header_length'. + } + */ + DBUG_PRINT("info", ("is quick repair: %d", rep_quick)); bzero((char*)&sort_info,sizeof(sort_info)); + /* Initialize pthread structures before goto err. */ + pthread_mutex_init(&sort_info.mutex, MY_MUTEX_INIT_FAST); + pthread_cond_init(&sort_info.cond, 0); + if (!(sort_info.key_block= - alloc_key_blocks(param, - (uint) param->sort_key_blocks, - share->base.max_key_block_length)) - || init_io_cache(¶m->read_cache,info->dfile, - (uint) param->read_buffer_length, - READ_CACHE,share->pack.header_length,1,MYF(MY_WME)) || - (! rep_quick && - init_io_cache(&info->rec_cache,info->dfile, - (uint) param->write_buffer_length, - WRITE_CACHE,new_header_length,1, - MYF(MY_WME | MY_WAIT_IF_FULL) & param->myf_rw))) + alloc_key_blocks(param, (uint) param->sort_key_blocks, + share->base.max_key_block_length)) || + init_io_cache(¶m->read_cache, info->dfile, + (uint) param->read_buffer_length, + READ_CACHE, share->pack.header_length, 1, MYF(MY_WME)) || + (!rep_quick && + (init_io_cache(&info->rec_cache, info->dfile, + (uint) param->write_buffer_length, + WRITE_CACHE, new_header_length, 1, + MYF(MY_WME | MY_WAIT_IF_FULL) & param->myf_rw) || + init_io_cache(&new_data_cache, -1, + (uint) param->write_buffer_length, + READ_CACHE, new_header_length, 1, + MYF(MY_WME | MY_DONT_CHECK_FILESIZE))))) goto err; sort_info.key_block_end=sort_info.key_block+param->sort_key_blocks; info->opt_flag|=WRITE_CACHE_USED; @@ -2497,8 +2585,6 @@ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info, del=info->state->del; param->glob_crc=0; - if (param->testflag & T_CALC_CHECKSUM) - param->calc_checksum=1; if (!(sort_param=(MI_SORT_PARAM *) my_malloc((uint) share->base.keys * @@ -2548,6 +2634,7 @@ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info, sort_param[i].sort_info=&sort_info; sort_param[i].master=0; sort_param[i].fix_datafile=0; + sort_param[i].calc_checksum= 0; sort_param[i].filepos=new_header_length; sort_param[i].max_pos=sort_param[i].pos=share->pack.header_length; @@ -2584,19 +2671,45 @@ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info, sort_info.total_keys=i; sort_param[0].master= 1; sort_param[0].fix_datafile= (my_bool)(! rep_quick); + sort_param[0].calc_checksum= test(param->testflag & T_CALC_CHECKSUM); sort_info.got_error=0; - pthread_mutex_init(&sort_info.mutex, MY_MUTEX_INIT_FAST); - pthread_cond_init(&sort_info.cond, 0); pthread_mutex_lock(&sort_info.mutex); - init_io_cache_share(¶m->read_cache, &io_share, i); + /* + Initialize the I/O cache share for use with the read caches and, in + case of non-quick repair, the write cache. When all threads join on + the cache lock, the writer copies the write cache contents to the + read caches. + */ + if (i > 1) + { + if (rep_quick) + init_io_cache_share(¶m->read_cache, &io_share, NULL, i); + else + init_io_cache_share(&new_data_cache, &io_share, &info->rec_cache, i); + } + else + io_share.total_threads= 0; /* share not used */ + (void) pthread_attr_init(&thr_attr); (void) pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED); for (i=0 ; i < sort_info.total_keys ; i++) { - sort_param[i].read_cache=param->read_cache; + /* + Copy the properly initialized IO_CACHE structure so that every + thread has its own copy. In quick mode param->read_cache is shared + for use by all threads. In non-quick mode all threads but the + first copy the shared new_data_cache, which is synchronized to the + write cache of the first thread. The first thread copies + param->read_cache, which is not shared. + */ + sort_param[i].read_cache= ((rep_quick || !i) ? param->read_cache : + new_data_cache); + DBUG_PRINT("io_cache_share", ("thread: %u read_cache: 0x%lx", + i, (long) &sort_param[i].read_cache)); + /* two approaches: the same amount of memory for each thread or the memory for the same number of keys for each thread... @@ -2614,7 +2727,10 @@ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info, (void *) (sort_param+i))) { mi_check_print_error(param,"Cannot start a repair thread"); - remove_io_thread(¶m->read_cache); + /* Cleanup: Detach from the share. Avoid others to be blocked. */ + if (io_share.total_threads) + remove_io_thread(&sort_param[i].read_cache); + DBUG_PRINT("error", ("Cannot start a repair thread")); sort_info.got_error=1; } else @@ -2636,6 +2752,11 @@ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info, if (sort_param[0].fix_datafile) { + /* + Append some nuls to the end of a memory mapped file. Destroy the + write cache. The master thread did already detach from the share + by remove_io_thread() in sort.c:thr_find_all_keys(). + */ if (write_data_suffix(&sort_info,1) || end_io_cache(&info->rec_cache)) goto err; if (param->testflag & T_SAFE_REPAIR) @@ -2651,8 +2772,14 @@ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info, sort_param->filepos; /* Only whole records */ share->state.version=(ulong) time((time_t*) 0); + + /* + Exchange the data file descriptor of the table, so that we use the + new file from now on. + */ my_close(info->dfile,MYF(0)); info->dfile=new_file; + share->data_file_type=sort_info.new_data_file_type; share->pack.header_length=(ulong) new_header_length; } @@ -2707,7 +2834,20 @@ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info, err: got_error|= flush_blocks(param, share->key_cache, share->kfile); + /* + Destroy the write cache. The master thread did already detach from + the share by remove_io_thread() or it was not yet started (if the + error happend before creating the thread). + */ VOID(end_io_cache(&info->rec_cache)); + /* + Destroy the new data cache in case of non-quick repair. All slave + threads did either detach from the share by remove_io_thread() + already or they were not yet started (if the error happend before + creating the threads). + */ + if (!rep_quick) + VOID(end_io_cache(&new_data_cache)); if (!got_error) { /* Replace the actual file with the temporary file */ @@ -2838,12 +2978,41 @@ static int sort_ft_key_read(MI_SORT_PARAM *sort_param, void *key) } /* sort_ft_key_read */ - /* Read next record from file using parameters in sort_info */ - /* Return -1 if end of file, 0 if ok and > 0 if error */ +/* + Read next record from file using parameters in sort_info. + + SYNOPSIS + sort_get_next_record() + sort_param Information about and for the sort process + + NOTE + + Dynamic Records With Non-Quick Parallel Repair + + For non-quick parallel repair we use a synchronized read/write + cache. This means that one thread is the master who fixes the data + file by reading each record from the old data file and writing it + to the new data file. By doing this the records in the new data + file are written contiguously. Whenever the write buffer is full, + it is copied to the read buffer. The slaves read from the read + buffer, which is not associated with a file. Thus read_cache.file + is -1. When using _mi_read_cache(), the slaves must always set + flag to READING_NEXT so that the function never tries to read from + file. This is safe because the records are contiguous. There is no + need to read outside the cache. This condition is evaluated in the + variable 'parallel_flag' for quick reference. read_cache.file must + be >= 0 in every other case. + + RETURN + -1 end of file + 0 ok + > 0 error +*/ static int sort_get_next_record(MI_SORT_PARAM *sort_param) { int searching; + int parallel_flag; uint found_record,b_type,left_length; my_off_t pos; byte *to; @@ -2881,7 +3050,7 @@ static int sort_get_next_record(MI_SORT_PARAM *sort_param) sort_param->max_pos=(sort_param->pos+=share->base.pack_reclength); if (*sort_param->record) { - if (param->calc_checksum) + if (sort_param->calc_checksum) param->glob_crc+= (info->checksum= mi_static_checksum(info,sort_param->record)); DBUG_RETURN(0); @@ -2896,6 +3065,7 @@ static int sort_get_next_record(MI_SORT_PARAM *sort_param) LINT_INIT(to); pos=sort_param->pos; searching=(sort_param->fix_datafile && (param->testflag & T_EXTEND)); + parallel_flag= (sort_param->read_cache.file < 0) ? READING_NEXT : 0; for (;;) { found_record=block_info.second_read= 0; @@ -2926,7 +3096,7 @@ static int sort_get_next_record(MI_SORT_PARAM *sort_param) (byte*) block_info.header,pos, MI_BLOCK_INFO_HEADER_LENGTH, (! found_record ? READING_NEXT : 0) | - READING_HEADER)) + parallel_flag | READING_HEADER)) { if (found_record) { @@ -3103,9 +3273,31 @@ static int sort_get_next_record(MI_SORT_PARAM *sort_param) llstr(sort_param->start_recpos,llbuff)); goto try_next; } - if (_mi_read_cache(&sort_param->read_cache,to,block_info.filepos, - block_info.data_len, - (found_record == 1 ? READING_NEXT : 0))) + /* + Copy information that is already read. Avoid accessing data + below the cache start. This could happen if the header + streched over the end of the previous buffer contents. + */ + { + uint header_len= (uint) (block_info.filepos - pos); + uint prefetch_len= (MI_BLOCK_INFO_HEADER_LENGTH - header_len); + + if (prefetch_len > block_info.data_len) + prefetch_len= block_info.data_len; + if (prefetch_len) + { + memcpy(to, block_info.header + header_len, prefetch_len); + block_info.filepos+= prefetch_len; + block_info.data_len-= prefetch_len; + left_length-= prefetch_len; + to+= prefetch_len; + } + } + if (block_info.data_len && + _mi_read_cache(&sort_param->read_cache,to,block_info.filepos, + block_info.data_len, + (found_record == 1 ? READING_NEXT : 0) | + parallel_flag)) { mi_check_print_info(param, "Read error for block at: %s (error: %d); Skipped", @@ -3135,13 +3327,14 @@ static int sort_get_next_record(MI_SORT_PARAM *sort_param) { if (sort_param->read_cache.error < 0) DBUG_RETURN(1); - if (info->s->calc_checksum) - info->checksum=mi_checksum(info,sort_param->record); + if (sort_param->calc_checksum) + info->checksum= mi_checksum(info, sort_param->record); if ((param->testflag & (T_EXTEND | T_REP)) || searching) { if (_mi_rec_check(info, sort_param->record, sort_param->rec_buff, sort_param->find_length, (param->testflag & T_QUICK) && + sort_param->calc_checksum && test(info->s->calc_checksum))) { mi_check_print_info(param,"Found wrong packed record at %s", @@ -3149,7 +3342,7 @@ static int sort_get_next_record(MI_SORT_PARAM *sort_param) goto try_next; } } - if (param->calc_checksum) + if (sort_param->calc_checksum) param->glob_crc+= info->checksum; DBUG_RETURN(0); } @@ -3176,7 +3369,8 @@ static int sort_get_next_record(MI_SORT_PARAM *sort_param) DBUG_RETURN(1); /* Something wrong with data */ } sort_param->start_recpos=sort_param->pos; - if (_mi_pack_get_block_info(info,&block_info,-1,sort_param->pos)) + if (_mi_pack_get_block_info(info, &sort_param->bit_buff, &block_info, + &sort_param->rec_buff, -1, sort_param->pos)) DBUG_RETURN(-1); if (!block_info.rec_len && sort_param->pos + MEMMAP_EXTRA_MARGIN == @@ -3200,15 +3394,14 @@ static int sort_get_next_record(MI_SORT_PARAM *sort_param) llstr(sort_param->pos,llbuff)); continue; } - if (_mi_pack_rec_unpack(info,sort_param->record,sort_param->rec_buff, - block_info.rec_len)) + if (_mi_pack_rec_unpack(info, &sort_param->bit_buff, sort_param->record, + sort_param->rec_buff, block_info.rec_len)) { if (! searching) mi_check_print_info(param,"Found wrong record at %s", llstr(sort_param->pos,llbuff)); continue; } - info->checksum=mi_checksum(info,sort_param->record); if (!sort_param->fix_datafile) { sort_param->filepos=sort_param->pos; @@ -3218,8 +3411,9 @@ static int sort_get_next_record(MI_SORT_PARAM *sort_param) sort_param->max_pos=(sort_param->pos=block_info.filepos+ block_info.rec_len); info->packed_length=block_info.rec_len; - if (param->calc_checksum) - param->glob_crc+= info->checksum; + if (sort_param->calc_checksum) + param->glob_crc+= (info->checksum= + mi_checksum(info, sort_param->record)); DBUG_RETURN(0); } } @@ -3227,7 +3421,20 @@ static int sort_get_next_record(MI_SORT_PARAM *sort_param) } - /* Write record to new file */ +/* + Write record to new file. + + SYNOPSIS + sort_write_record() + sort_param Sort parameters. + + NOTE + This is only called by a master thread if parallel repair is used. + + RETURN + 0 OK + 1 Error +*/ int sort_write_record(MI_SORT_PARAM *sort_param) { @@ -3276,6 +3483,7 @@ int sort_write_record(MI_SORT_PARAM *sort_param) } from=sort_info->buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER); } + /* We can use info->checksum here as only one thread calls this. */ info->checksum=mi_checksum(info,sort_param->record); reclength=_mi_rec_pack(info,from,sort_param->record); flag=0; @@ -3685,7 +3893,7 @@ static int sort_delete_record(MI_SORT_PARAM *sort_param) DBUG_RETURN(1); } } - if (param->calc_checksum) + if (sort_param->calc_checksum) param->glob_crc-=(*info->s->calc_checksum)(info, sort_param->record); } error=flush_io_cache(&info->rec_cache) || (*info->s->delete_record)(info); diff --git a/myisam/mi_open.c b/myisam/mi_open.c index a5b303f86d4..4f298397615 100644 --- a/myisam/mi_open.c +++ b/myisam/mi_open.c @@ -201,7 +201,10 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) ((open_flags & HA_OPEN_ABORT_IF_CRASHED) && (my_disable_locking && share->state.open_count)))) { - DBUG_PRINT("error",("Table is marked as crashed")); + DBUG_PRINT("error",("Table is marked as crashed. open_flags: %u " + "changed: %u open_count: %u !locking: %d", + open_flags, share->state.changed, + share->state.open_count, my_disable_locking)); my_errno=((share->state.changed & STATE_CRASHED_ON_REPAIR) ? HA_ERR_CRASHED_ON_REPAIR : HA_ERR_CRASHED_ON_USAGE); goto err; diff --git a/myisam/mi_packrec.c b/myisam/mi_packrec.c index bd2d162d100..c425bf54b1a 100644 --- a/myisam/mi_packrec.c +++ b/myisam/mi_packrec.c @@ -101,7 +101,8 @@ static uint fill_and_get_bits(MI_BIT_BUFF *bit_buff,uint count); static void fill_buffer(MI_BIT_BUFF *bit_buff); static uint max_bit(uint value); #ifdef HAVE_MMAP -static uchar *_mi_mempack_get_block_info(MI_INFO *myisam,MI_BLOCK_INFO *info, +static uchar *_mi_mempack_get_block_info(MI_INFO *myisam, MI_BIT_BUFF *bit_buff, + MI_BLOCK_INFO *info, byte **rec_buff_p, uchar *header); #endif @@ -428,13 +429,15 @@ int _mi_read_pack_record(MI_INFO *info, my_off_t filepos, byte *buf) DBUG_RETURN(-1); /* _search() didn't find record */ file=info->dfile; - if (_mi_pack_get_block_info(info, &block_info, file, filepos)) + if (_mi_pack_get_block_info(info, &info->bit_buff, &block_info, + &info->rec_buff, file, filepos)) goto err; if (my_read(file,(byte*) info->rec_buff + block_info.offset , block_info.rec_len - block_info.offset, MYF(MY_NABP))) goto panic; info->update|= HA_STATE_AKTIV; - DBUG_RETURN(_mi_pack_rec_unpack(info,buf,info->rec_buff,block_info.rec_len)); + DBUG_RETURN(_mi_pack_rec_unpack(info, &info->bit_buff, buf, + info->rec_buff, block_info.rec_len)); panic: my_errno=HA_ERR_WRONG_IN_RECORD; err: @@ -443,8 +446,8 @@ err: -int _mi_pack_rec_unpack(register MI_INFO *info, register byte *to, byte *from, - ulong reclength) +int _mi_pack_rec_unpack(register MI_INFO *info, MI_BIT_BUFF *bit_buff, + register byte *to, byte *from, ulong reclength) { byte *end_field; reg3 MI_COLUMNDEF *end; @@ -452,18 +455,18 @@ int _mi_pack_rec_unpack(register MI_INFO *info, register byte *to, byte *from, MYISAM_SHARE *share=info->s; DBUG_ENTER("_mi_pack_rec_unpack"); - init_bit_buffer(&info->bit_buff, (uchar*) from,reclength); + init_bit_buffer(bit_buff, (uchar*) from, reclength); for (current_field=share->rec, end=current_field+share->base.fields ; current_field < end ; current_field++,to=end_field) { end_field=to+current_field->length; - (*current_field->unpack)(current_field,&info->bit_buff,(uchar*) to, + (*current_field->unpack)(current_field, bit_buff, (uchar*) to, (uchar*) end_field); } - if (! info->bit_buff.error && - info->bit_buff.pos - info->bit_buff.bits/8 == info->bit_buff.end) + if (!bit_buff->error && + bit_buff->pos - bit_buff->bits / 8 == bit_buff->end) DBUG_RETURN(0); info->update&= ~HA_STATE_AKTIV; DBUG_RETURN(my_errno=HA_ERR_WRONG_IN_RECORD); @@ -977,13 +980,16 @@ int _mi_read_rnd_pack_record(MI_INFO *info, byte *buf, if (info->opt_flag & READ_CACHE_USED) { - if (_mi_read_cache(&info->rec_cache,(byte*) block_info.header,filepos, - share->pack.ref_length, skip_deleted_blocks)) + if (_mi_read_cache(&info->rec_cache, (byte*) block_info.header, + filepos, share->pack.ref_length, + skip_deleted_blocks ? READING_NEXT : 0)) goto err; - b_type=_mi_pack_get_block_info(info,&block_info,-1, filepos); + b_type=_mi_pack_get_block_info(info, &info->bit_buff, &block_info, + &info->rec_buff, -1, filepos); } else - b_type=_mi_pack_get_block_info(info,&block_info,info->dfile,filepos); + b_type=_mi_pack_get_block_info(info, &info->bit_buff, &block_info, + &info->rec_buff, info->dfile, filepos); if (b_type) goto err; /* Error code is already set */ #ifndef DBUG_OFF @@ -996,9 +1002,9 @@ int _mi_read_rnd_pack_record(MI_INFO *info, byte *buf, if (info->opt_flag & READ_CACHE_USED) { - if (_mi_read_cache(&info->rec_cache,(byte*) info->rec_buff, - block_info.filepos, block_info.rec_len, - skip_deleted_blocks)) + if (_mi_read_cache(&info->rec_cache, (byte*) info->rec_buff, + block_info.filepos, block_info.rec_len, + skip_deleted_blocks ? READING_NEXT : 0)) goto err; } else @@ -1013,8 +1019,8 @@ int _mi_read_rnd_pack_record(MI_INFO *info, byte *buf, info->nextpos=block_info.filepos+block_info.rec_len; info->update|= HA_STATE_AKTIV | HA_STATE_KEY_CHANGED; - DBUG_RETURN (_mi_pack_rec_unpack(info,buf,info->rec_buff, - block_info.rec_len)); + DBUG_RETURN (_mi_pack_rec_unpack(info, &info->bit_buff, buf, + info->rec_buff, block_info.rec_len)); err: DBUG_RETURN(my_errno); } @@ -1022,8 +1028,9 @@ int _mi_read_rnd_pack_record(MI_INFO *info, byte *buf, /* Read and process header from a huff-record-file */ -uint _mi_pack_get_block_info(MI_INFO *myisam, MI_BLOCK_INFO *info, File file, - my_off_t filepos) +uint _mi_pack_get_block_info(MI_INFO *myisam, MI_BIT_BUFF *bit_buff, + MI_BLOCK_INFO *info, byte **rec_buff_p, + File file, my_off_t filepos) { uchar *header=info->header; uint head_length,ref_length; @@ -1048,17 +1055,17 @@ uint _mi_pack_get_block_info(MI_INFO *myisam, MI_BLOCK_INFO *info, File file, head_length+= read_pack_length((uint) myisam->s->pack.version, header + head_length, &info->blob_len); if (!(mi_alloc_rec_buff(myisam,info->rec_len + info->blob_len, - &myisam->rec_buff))) + rec_buff_p))) return BLOCK_FATAL_ERROR; /* not enough memory */ - myisam->bit_buff.blob_pos=(uchar*) myisam->rec_buff+info->rec_len; - myisam->bit_buff.blob_end= myisam->bit_buff.blob_pos+info->blob_len; + bit_buff->blob_pos= (uchar*) *rec_buff_p + info->rec_len; + bit_buff->blob_end= bit_buff->blob_pos + info->blob_len; myisam->blob_length=info->blob_len; } info->filepos=filepos+head_length; if (file > 0) { info->offset=min(info->rec_len, ref_length - head_length); - memcpy(myisam->rec_buff, header+head_length, info->offset); + memcpy(*rec_buff_p, header + head_length, info->offset); } return 0; } @@ -1198,7 +1205,8 @@ void _mi_unmap_file(MI_INFO *info) } -static uchar *_mi_mempack_get_block_info(MI_INFO *myisam,MI_BLOCK_INFO *info, +static uchar *_mi_mempack_get_block_info(MI_INFO *myisam, MI_BIT_BUFF *bit_buff, + MI_BLOCK_INFO *info, byte **rec_buff_p, uchar *header) { header+= read_pack_length((uint) myisam->s->pack.version, header, @@ -1209,10 +1217,10 @@ static uchar *_mi_mempack_get_block_info(MI_INFO *myisam,MI_BLOCK_INFO *info, &info->blob_len); /* mi_alloc_rec_buff sets my_errno on error */ if (!(mi_alloc_rec_buff(myisam, info->blob_len, - &myisam->rec_buff))) + rec_buff_p))) return 0; /* not enough memory */ - myisam->bit_buff.blob_pos=(uchar*) myisam->rec_buff; - myisam->bit_buff.blob_end= (uchar*) myisam->rec_buff + info->blob_len; + bit_buff->blob_pos= (uchar*) *rec_buff_p; + bit_buff->blob_end= (uchar*) *rec_buff_p + info->blob_len; } return header; } @@ -1228,11 +1236,13 @@ static int _mi_read_mempack_record(MI_INFO *info, my_off_t filepos, byte *buf) if (filepos == HA_OFFSET_ERROR) DBUG_RETURN(-1); /* _search() didn't find record */ - if (!(pos= (byte*) _mi_mempack_get_block_info(info,&block_info, + if (!(pos= (byte*) _mi_mempack_get_block_info(info, &info->bit_buff, + &block_info, &info->rec_buff, (uchar*) share->file_map+ filepos))) DBUG_RETURN(-1); - DBUG_RETURN(_mi_pack_rec_unpack(info, buf, pos, block_info.rec_len)); + DBUG_RETURN(_mi_pack_rec_unpack(info, &info->bit_buff, buf, + pos, block_info.rec_len)); } @@ -1252,7 +1262,8 @@ static int _mi_read_rnd_mempack_record(MI_INFO *info, byte *buf, my_errno=HA_ERR_END_OF_FILE; goto err; } - if (!(pos= (byte*) _mi_mempack_get_block_info(info,&block_info, + if (!(pos= (byte*) _mi_mempack_get_block_info(info, &info->bit_buff, + &block_info, &info->rec_buff, (uchar*) (start=share->file_map+ filepos)))) @@ -1269,7 +1280,8 @@ static int _mi_read_rnd_mempack_record(MI_INFO *info, byte *buf, info->nextpos=filepos+(uint) (pos-start)+block_info.rec_len; info->update|= HA_STATE_AKTIV | HA_STATE_KEY_CHANGED; - DBUG_RETURN (_mi_pack_rec_unpack(info,buf,pos, block_info.rec_len)); + DBUG_RETURN (_mi_pack_rec_unpack(info, &info->bit_buff, buf, + pos, block_info.rec_len)); err: DBUG_RETURN(my_errno); } diff --git a/myisam/myisamdef.h b/myisam/myisamdef.h index a766d59d72a..6365f0e1b0c 100644 --- a/myisam/myisamdef.h +++ b/myisam/myisamdef.h @@ -75,7 +75,7 @@ typedef struct st_mi_state_info ulong sec_index_changed; /* Updated when new sec_index */ ulong sec_index_used; /* which extra index are in use */ ulonglong key_map; /* Which keys are in use */ - ha_checksum checksum; + ha_checksum checksum; /* Table checksum */ ulong version; /* timestamp of create */ time_t create_time; /* Time when created database */ time_t recover_time; /* Time for last recover */ @@ -176,6 +176,7 @@ typedef struct st_mi_isam_share { /* Shared between opens */ int (*delete_record)(struct st_myisam_info*); int (*read_rnd)(struct st_myisam_info*, byte*, my_off_t, my_bool); int (*compare_record)(struct st_myisam_info*, const byte *); + /* Function to use for a row checksum. */ ha_checksum (*calc_checksum)(struct st_myisam_info*, const byte *); int (*compare_unique)(struct st_myisam_info*, MI_UNIQUEDEF *, const byte *record, my_off_t pos); @@ -249,7 +250,7 @@ struct st_myisam_info { my_off_t last_keypage; /* Last key page read */ my_off_t last_search_keypage; /* Last keypage when searching */ my_off_t dupp_key_pos; - ha_checksum checksum; + ha_checksum checksum; /* Temp storage for row checksum */ /* QQ: the folloing two xxx_length fields should be removed, as they are not compatible with parallel repair */ ulong packed_length,blob_length; /* Length of found, packed record */ @@ -297,8 +298,9 @@ typedef struct st_mi_sort_param pthread_t thr; IO_CACHE read_cache, tempfile, tempfile_for_exceptions; DYNAMIC_ARRAY buffpek; - - /* + MI_BIT_BUFF bit_buff; /* For parallel repair of packrec. */ + + /* The next two are used to collect statistics, see update_key_parts for description. */ @@ -309,6 +311,7 @@ typedef struct st_mi_sort_param uint key, key_length,real_key_length,sortbuff_size; uint maxbuffers, keys, find_length, sort_keys_length; my_bool fix_datafile, master; + my_bool calc_checksum; /* calculate table checksum */ MI_KEYDEF *keyinfo; HA_KEYSEG *seg; SORT_INFO *sort_info; @@ -361,8 +364,15 @@ typedef struct st_mi_sort_param #define mi_putint(x,y,nod) { uint16 boh=(nod ? (uint16) 32768 : 0) + (uint16) (y);\ mi_int2store(x,boh); } #define mi_test_if_nod(x) (x[0] & 128 ? info->s->base.key_reflength : 0) -#define mi_mark_crashed(x) (x)->s->state.changed|=STATE_CRASHED -#define mi_mark_crashed_on_repair(x) { (x)->s->state.changed|=STATE_CRASHED|STATE_CRASHED_ON_REPAIR ; (x)->update|= HA_STATE_CHANGED; } +#define mi_mark_crashed(x) do{(x)->s->state.changed|= STATE_CRASHED; \ + DBUG_PRINT("error", ("Marked table crashed")); \ + }while(0) +#define mi_mark_crashed_on_repair(x) do{(x)->s->state.changed|= \ + STATE_CRASHED|STATE_CRASHED_ON_REPAIR; \ + (x)->update|= HA_STATE_CHANGED; \ + DBUG_PRINT("error", \ + ("Marked table crashed")); \ + }while(0) #define mi_is_crashed(x) ((x)->s->state.changed & STATE_CRASHED) #define mi_is_crashed_on_repair(x) ((x)->s->state.changed & STATE_CRASHED_ON_REPAIR) @@ -600,8 +610,8 @@ extern void _mi_print_key(FILE *stream,HA_KEYSEG *keyseg,const uchar *key, extern my_bool _mi_read_pack_info(MI_INFO *info,pbool fix_keys); extern int _mi_read_pack_record(MI_INFO *info,my_off_t filepos,byte *buf); extern int _mi_read_rnd_pack_record(MI_INFO*, byte *,my_off_t, my_bool); -extern int _mi_pack_rec_unpack(MI_INFO *info,byte *to,byte *from, - ulong reclength); +extern int _mi_pack_rec_unpack(MI_INFO *info, MI_BIT_BUFF *bit_buff, + byte *to, byte *from, ulong reclength); extern ulonglong mi_safe_mul(ulonglong a,ulonglong b); extern int _mi_ft_update(MI_INFO *info, uint keynr, byte *keybuf, const byte *oldrec, const byte *newrec, my_off_t pos); @@ -666,7 +676,9 @@ extern "C" { extern uint _mi_get_block_info(MI_BLOCK_INFO *,File, my_off_t); extern uint _mi_rec_pack(MI_INFO *info,byte *to,const byte *from); -extern uint _mi_pack_get_block_info(MI_INFO *, MI_BLOCK_INFO *, File, my_off_t); +extern uint _mi_pack_get_block_info(MI_INFO *myisam, MI_BIT_BUFF *bit_buff, + MI_BLOCK_INFO *info, byte **rec_buff_p, + File file, my_off_t filepos); extern void _my_store_blob_length(byte *pos,uint pack_length,uint length); extern void _myisam_log(enum myisam_log_commands command,MI_INFO *info, const byte *buffert,uint length); diff --git a/myisam/sort.c b/myisam/sort.c index 96b55d599c8..2e6fd20ade8 100644 --- a/myisam/sort.c +++ b/myisam/sort.c @@ -309,7 +309,7 @@ static ha_rows NEAR_F find_all_keys(MI_SORT_PARAM *info, uint keys, pthread_handler_decl(thr_find_all_keys,arg) { - MI_SORT_PARAM *info= (MI_SORT_PARAM*) arg; + MI_SORT_PARAM *sort_param= (MI_SORT_PARAM*) arg; int error; uint memavl,old_memavl,keys,sort_length; uint idx, maxbuffer; @@ -321,32 +321,34 @@ pthread_handler_decl(thr_find_all_keys,arg) if (my_thread_init()) goto err; - if (info->sort_info->got_error) + DBUG_ENTER("thr_find_all_keys"); + DBUG_PRINT("enter", ("master: %d", sort_param->master)); + if (sort_param->sort_info->got_error) goto err; - if (info->keyinfo->flag && HA_VAR_LENGTH_KEY) + if (sort_param->keyinfo->flag && HA_VAR_LENGTH_KEY) { - info->write_keys=write_keys_varlen; - info->read_to_buffer=read_to_buffer_varlen; - info->write_key=write_merge_key_varlen; + sort_param->write_keys= write_keys_varlen; + sort_param->read_to_buffer= read_to_buffer_varlen; + sort_param->write_key= write_merge_key_varlen; } else { - info->write_keys=write_keys; - info->read_to_buffer=read_to_buffer; - info->write_key=write_merge_key; + sort_param->write_keys= write_keys; + sort_param->read_to_buffer= read_to_buffer; + sort_param->write_key= write_merge_key; } - my_b_clear(&info->tempfile); - my_b_clear(&info->tempfile_for_exceptions); - bzero((char*) &info->buffpek,sizeof(info->buffpek)); - bzero((char*) &info->unique, sizeof(info->unique)); + my_b_clear(&sort_param->tempfile); + my_b_clear(&sort_param->tempfile_for_exceptions); + bzero((char*) &sort_param->buffpek, sizeof(sort_param->buffpek)); + bzero((char*) &sort_param->unique, sizeof(sort_param->unique)); sort_keys= (uchar **) NULL; - memavl=max(info->sortbuff_size, MIN_SORT_MEMORY); - idx= info->sort_info->max_records; - sort_length= info->key_length; - maxbuffer= 1; + memavl= max(sort_param->sortbuff_size, MIN_SORT_MEMORY); + idx= sort_param->sort_info->max_records; + sort_length= sort_param->key_length; + maxbuffer= 1; while (memavl >= MIN_SORT_MEMORY) { @@ -363,18 +365,19 @@ pthread_handler_decl(thr_find_all_keys,arg) (keys=(memavl-sizeof(BUFFPEK)*maxbuffer)/ (sort_length+sizeof(char*))) <= 1) { - mi_check_print_error(info->sort_info->param, + mi_check_print_error(sort_param->sort_info->param, "sort_buffer_size is to small"); goto err; } } while ((maxbuffer= (int) (idx/(keys-1)+1)) != skr); } - if ((sort_keys=(uchar **)my_malloc(keys*(sort_length+sizeof(char*))+ - ((info->keyinfo->flag & HA_FULLTEXT) ? - HA_FT_MAXBYTELEN : 0), MYF(0)))) + if ((sort_keys= (uchar**) + my_malloc(keys*(sort_length+sizeof(char*))+ + ((sort_param->keyinfo->flag & HA_FULLTEXT) ? + HA_FT_MAXBYTELEN : 0), MYF(0)))) { - if (my_init_dynamic_array(&info->buffpek, sizeof(BUFFPEK), + if (my_init_dynamic_array(&sort_param->buffpek, sizeof(BUFFPEK), maxbuffer, maxbuffer/2)) my_free((gptr) sort_keys,MYF(0)); else @@ -386,69 +389,87 @@ pthread_handler_decl(thr_find_all_keys,arg) } if (memavl < MIN_SORT_MEMORY) { - mi_check_print_error(info->sort_info->param,"Sort buffer to small"); /* purecov: tested */ + mi_check_print_error(sort_param->sort_info->param, "Sort buffer too small"); goto err; /* purecov: tested */ } - if (info->sort_info->param->testflag & T_VERBOSE) - printf("Key %d - Allocating buffer for %d keys\n",info->key+1,keys); - info->sort_keys=sort_keys; + if (sort_param->sort_info->param->testflag & T_VERBOSE) + printf("Key %d - Allocating buffer for %d keys\n", + sort_param->key + 1, keys); + sort_param->sort_keys= sort_keys; idx=error=0; sort_keys[0]=(uchar*) (sort_keys+keys); - while (!(error=info->sort_info->got_error) && - !(error=(*info->key_read)(info,sort_keys[idx]))) + DBUG_PRINT("info", ("reading keys")); + while (!(error= sort_param->sort_info->got_error) && + !(error= (*sort_param->key_read)(sort_param, sort_keys[idx]))) { - if (info->real_key_length > info->key_length) + if (sort_param->real_key_length > sort_param->key_length) { - if (write_key(info,sort_keys[idx], &info->tempfile_for_exceptions)) + if (write_key(sort_param, sort_keys[idx], + &sort_param->tempfile_for_exceptions)) goto err; continue; } if (++idx == keys) { - if (info->write_keys(info,sort_keys,idx-1, - (BUFFPEK *)alloc_dynamic(&info->buffpek), - &info->tempfile)) + if (sort_param->write_keys(sort_param, sort_keys, idx - 1, + (BUFFPEK*) alloc_dynamic(&sort_param->buffpek), + &sort_param->tempfile)) goto err; sort_keys[0]=(uchar*) (sort_keys+keys); - memcpy(sort_keys[0],sort_keys[idx-1],(size_t) info->key_length); + memcpy(sort_keys[0], sort_keys[idx - 1], (size_t) sort_param->key_length); idx=1; } - sort_keys[idx]=sort_keys[idx-1]+info->key_length; + sort_keys[idx]= sort_keys[idx - 1] + sort_param->key_length; } if (error > 0) goto err; - if (info->buffpek.elements) + if (sort_param->buffpek.elements) { - if (info->write_keys(info,sort_keys, idx, - (BUFFPEK *) alloc_dynamic(&info->buffpek), &info->tempfile)) + if (sort_param->write_keys(sort_param, sort_keys, idx, + (BUFFPEK*) alloc_dynamic(&sort_param->buffpek), + &sort_param->tempfile)) goto err; - info->keys=(info->buffpek.elements-1)*(keys-1)+idx; + sort_param->keys= (sort_param->buffpek.elements - 1) * (keys - 1) + idx; } else - info->keys=idx; + sort_param->keys= idx; - info->sort_keys_length=keys; + sort_param->sort_keys_length= keys; goto ok; err: - info->sort_info->got_error=1; /* no need to protect this with a mutex */ + DBUG_PRINT("error", ("got some error")); + sort_param->sort_info->got_error= 1; /* no need to protect with a mutex */ if (sort_keys) my_free((gptr) sort_keys,MYF(0)); - info->sort_keys=0; - delete_dynamic(& info->buffpek); - close_cached_file(&info->tempfile); - close_cached_file(&info->tempfile_for_exceptions); + sort_param->sort_keys= 0; + delete_dynamic(& sort_param->buffpek); + close_cached_file(&sort_param->tempfile); + close_cached_file(&sort_param->tempfile_for_exceptions); ok: - remove_io_thread(&info->read_cache); - pthread_mutex_lock(&info->sort_info->mutex); - info->sort_info->threads_running--; - pthread_cond_signal(&info->sort_info->cond); - pthread_mutex_unlock(&info->sort_info->mutex); + /* + Detach from the share if the writer is involved. Avoid others to + be blocked. This includes a flush of the write buffer. This will + also indicate EOF to the readers. + */ + if (sort_param->sort_info->info->rec_cache.share) + remove_io_thread(&sort_param->sort_info->info->rec_cache); + + /* Readers detach from the share if any. Avoid others to be blocked. */ + if (sort_param->read_cache.share) + remove_io_thread(&sort_param->read_cache); + + pthread_mutex_lock(&sort_param->sort_info->mutex); + if (!--sort_param->sort_info->threads_running) + pthread_cond_signal(&sort_param->sort_info->cond); + pthread_mutex_unlock(&sort_param->sort_info->mutex); + + DBUG_PRINT("exit", ("======== ending thread ========")); my_thread_end(); return NULL; } @@ -466,6 +487,7 @@ int thr_write_keys(MI_SORT_PARAM *sort_param) MYISAM_SHARE *share=info->s; MI_SORT_PARAM *sinfo; byte *mergebuf=0; + DBUG_ENTER("thr_write_keys"); LINT_INIT(length); for (i= 0, sinfo= sort_param ; @@ -604,7 +626,7 @@ int thr_write_keys(MI_SORT_PARAM *sort_param) } } my_free((gptr) mergebuf,MYF(MY_ALLOW_ZERO_PTR)); - return got_error; + DBUG_RETURN(got_error); } #endif /* THREAD */ diff --git a/mysql-test/r/myisam.result b/mysql-test/r/myisam.result index 7aaf3c8f85a..cb3e6afffa4 100644 --- a/mysql-test/r/myisam.result +++ b/mysql-test/r/myisam.result @@ -756,3 +756,129 @@ a b xxxxxxxxx bbbbbb xxxxxxxxx bbbbbb DROP TABLE t1; +SET @@myisam_repair_threads=2; +SHOW VARIABLES LIKE 'myisam_repair%'; +Variable_name Value +myisam_repair_threads 2 +CREATE TABLE t1 ( +`_id` int(11) NOT NULL default '0', +`url` text, +`email` text, +`description` text, +`loverlap` int(11) default NULL, +`roverlap` int(11) default NULL, +`lneighbor_id` int(11) default NULL, +`rneighbor_id` int(11) default NULL, +`length_` int(11) default NULL, +`sequence` mediumtext, +`name` text, +`_obj_class` text NOT NULL, +PRIMARY KEY (`_id`), +UNIQUE KEY `sequence_name_index` (`name`(50)), +KEY (`length_`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; +INSERT INTO t1 VALUES +(1,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample1',''), +(2,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample2',''), +(3,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample3',''), +(4,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample4',''), +(5,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample5',''), +(6,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample6',''), +(7,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample7',''), +(8,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample8',''), +(9,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample9',''); +SELECT _id FROM t1; +_id +1 +2 +3 +4 +5 +6 +7 +8 +9 +DELETE FROM t1 WHERE _id < 8; +SHOW TABLE STATUS LIKE 't1'; +Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment +t1 MyISAM 9 Dynamic 2 # # # # 140 # # # # # # +CHECK TABLE t1 EXTENDED; +Table Op Msg_type Msg_text +test.t1 check status OK +OPTIMIZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 optimize status OK +CHECK TABLE t1 EXTENDED; +Table Op Msg_type Msg_text +test.t1 check status OK +SHOW TABLE STATUS LIKE 't1'; +Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment +t1 MyISAM 9 Dynamic 2 # # # # 0 # # # # # # +SELECT _id FROM t1; +_id +8 +9 +DROP TABLE t1; +CREATE TABLE t1 ( +`_id` int(11) NOT NULL default '0', +`url` text, +`email` text, +`description` text, +`loverlap` int(11) default NULL, +`roverlap` int(11) default NULL, +`lneighbor_id` int(11) default NULL, +`rneighbor_id` int(11) default NULL, +`length_` int(11) default NULL, +`sequence` mediumtext, +`name` text, +`_obj_class` text NOT NULL, +PRIMARY KEY (`_id`), +UNIQUE KEY `sequence_name_index` (`name`(50)), +KEY (`length_`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; +INSERT INTO t1 VALUES +(1,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample1',''), +(2,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample2',''), +(3,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample3',''), +(4,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample4',''), +(5,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample5',''), +(6,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample6',''), +(7,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample7',''), +(8,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample8',''), +(9,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample9',''); +SELECT _id FROM t1; +_id +1 +2 +3 +4 +5 +6 +7 +8 +9 +DELETE FROM t1 WHERE _id < 8; +SHOW TABLE STATUS LIKE 't1'; +Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment +t1 MyISAM 9 Dynamic 2 # # # # 140 # # # # # # +CHECK TABLE t1 EXTENDED; +Table Op Msg_type Msg_text +test.t1 check status OK +REPAIR TABLE t1 QUICK; +Table Op Msg_type Msg_text +test.t1 repair status OK +CHECK TABLE t1 EXTENDED; +Table Op Msg_type Msg_text +test.t1 check status OK +SHOW TABLE STATUS LIKE 't1'; +Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment +t1 MyISAM 9 Dynamic 2 # # # # 140 # # # # # # +SELECT _id FROM t1; +_id +8 +9 +DROP TABLE t1; +SET @@myisam_repair_threads=1; +SHOW VARIABLES LIKE 'myisam_repair%'; +Variable_name Value +myisam_repair_threads 1 diff --git a/mysql-test/t/myisam.test b/mysql-test/t/myisam.test index 5f948973141..0b9fad07ac4 100644 --- a/mysql-test/t/myisam.test +++ b/mysql-test/t/myisam.test @@ -714,4 +714,97 @@ UPDATE t1 AS ta1,t1 AS ta2 SET ta1.b='aaaaaa',ta2.b='bbbbbb'; SELECT * FROM t1; DROP TABLE t1; +# +# Bug#8283 - OPTIMIZE TABLE causes data loss +# +SET @@myisam_repair_threads=2; +SHOW VARIABLES LIKE 'myisam_repair%'; +# +# Test OPTIMIZE. This creates a new data file. +CREATE TABLE t1 ( + `_id` int(11) NOT NULL default '0', + `url` text, + `email` text, + `description` text, + `loverlap` int(11) default NULL, + `roverlap` int(11) default NULL, + `lneighbor_id` int(11) default NULL, + `rneighbor_id` int(11) default NULL, + `length_` int(11) default NULL, + `sequence` mediumtext, + `name` text, + `_obj_class` text NOT NULL, + PRIMARY KEY (`_id`), + UNIQUE KEY `sequence_name_index` (`name`(50)), + KEY (`length_`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; +# +INSERT INTO t1 VALUES + (1,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample1',''), + (2,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample2',''), + (3,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample3',''), + (4,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample4',''), + (5,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample5',''), + (6,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample6',''), + (7,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample7',''), + (8,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample8',''), + (9,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample9',''); +# +SELECT _id FROM t1; +DELETE FROM t1 WHERE _id < 8; +--replace_column 6 # 7 # 8 # 9 # 11 # 12 # 13 # 14 # 15 # 16 # +SHOW TABLE STATUS LIKE 't1'; +CHECK TABLE t1 EXTENDED; +OPTIMIZE TABLE t1; +CHECK TABLE t1 EXTENDED; +--replace_column 6 # 7 # 8 # 9 # 11 # 12 # 13 # 14 # 15 # 16 # +SHOW TABLE STATUS LIKE 't1'; +SELECT _id FROM t1; +DROP TABLE t1; +# +# Test REPAIR QUICK. This retains the old data file. +CREATE TABLE t1 ( + `_id` int(11) NOT NULL default '0', + `url` text, + `email` text, + `description` text, + `loverlap` int(11) default NULL, + `roverlap` int(11) default NULL, + `lneighbor_id` int(11) default NULL, + `rneighbor_id` int(11) default NULL, + `length_` int(11) default NULL, + `sequence` mediumtext, + `name` text, + `_obj_class` text NOT NULL, + PRIMARY KEY (`_id`), + UNIQUE KEY `sequence_name_index` (`name`(50)), + KEY (`length_`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; +# +INSERT INTO t1 VALUES + (1,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample1',''), + (2,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample2',''), + (3,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample3',''), + (4,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample4',''), + (5,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample5',''), + (6,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample6',''), + (7,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample7',''), + (8,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample8',''), + (9,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'sample9',''); +# +SELECT _id FROM t1; +DELETE FROM t1 WHERE _id < 8; +--replace_column 6 # 7 # 8 # 9 # 11 # 12 # 13 # 14 # 15 # 16 # +SHOW TABLE STATUS LIKE 't1'; +CHECK TABLE t1 EXTENDED; +REPAIR TABLE t1 QUICK; +CHECK TABLE t1 EXTENDED; +--replace_column 6 # 7 # 8 # 9 # 11 # 12 # 13 # 14 # 15 # 16 # +SHOW TABLE STATUS LIKE 't1'; +SELECT _id FROM t1; +DROP TABLE t1; +# +SET @@myisam_repair_threads=1; +SHOW VARIABLES LIKE 'myisam_repair%'; + # End of 4.1 tests diff --git a/mysys/mf_iocache.c b/mysys/mf_iocache.c index 0007784c2b2..58d4756b702 100644 --- a/mysys/mf_iocache.c +++ b/mysys/mf_iocache.c @@ -70,7 +70,6 @@ static void my_aiowait(my_aio_result *result); #define IO_ROUND_UP(X) (((X)+IO_SIZE-1) & ~(IO_SIZE-1)) #define IO_ROUND_DN(X) ( (X) & ~(IO_SIZE-1)) - /* Setup internal pointers inside IO_CACHE @@ -500,65 +499,366 @@ int _my_b_read(register IO_CACHE *info, byte *Buffer, uint Count) DBUG_RETURN(0); } + #ifdef THREAD -/* Prepare IO_CACHE for shared use */ -void init_io_cache_share(IO_CACHE *info, IO_CACHE_SHARE *s, uint num_threads) +/* + Prepare IO_CACHE for shared use. + + SYNOPSIS + init_io_cache_share() + read_cache A read cache. This will be copied for + every thread after setup. + cshare The share. + write_cache If non-NULL a write cache that is to be + synchronized with the read caches. + num_threads Number of threads sharing the cache + including the write thread if any. + + DESCRIPTION + + The shared cache is used so: One IO_CACHE is initialized with + init_io_cache(). This includes the allocation of a buffer. Then a + share is allocated and init_io_cache_share() is called with the io + cache and the share. Then the io cache is copied for each thread. So + every thread has its own copy of IO_CACHE. But the allocated buffer + is shared because cache->buffer is the same for all caches. + + One thread reads data from the file into the buffer. All threads + read from the buffer, but every thread maintains its own set of + pointers into the buffer. When all threads have used up the buffer + contents, one of the threads reads the next block of data into the + buffer. To accomplish this, each thread enters the cache lock before + accessing the buffer. They wait in lock_io_cache() until all threads + joined the lock. The last thread entering the lock is in charge of + reading from file to buffer. It wakes all threads when done. + + Synchronizing a write cache to the read caches works so: Whenever + the write buffer needs a flush, the write thread enters the lock and + waits for all other threads to enter the lock too. They do this when + they have used up the read buffer. When all threads are in the lock, + the write thread copies the write buffer to the read buffer and + wakes all threads. + + share->running_threads is the number of threads not being in the + cache lock. When entering lock_io_cache() the number is decreased. + When the thread that fills the buffer enters unlock_io_cache() the + number is reset to the number of threads. The condition + running_threads == 0 means that all threads are in the lock. Bumping + up the number to the full count is non-intuitive. But increasing the + number by one for each thread that leaves the lock could lead to a + solo run of one thread. The last thread to join a lock reads from + file to buffer, wakes the other threads, processes the data in the + cache and enters the lock again. If no other thread left the lock + meanwhile, it would think it's the last one again and read the next + block... + + The share has copies of 'error', 'buffer', 'read_end', and + 'pos_in_file' from the thread that filled the buffer. We may not be + able to access this information directly from its cache because the + thread may be removed from the share before the variables could be + copied by all other threads. Or, if a write buffer is synchronized, + it would change its 'pos_in_file' after waking the other threads, + possibly before they could copy its value. + + However, the 'buffer' variable in the share is for a synchronized + write cache. It needs to know where to put the data. Otherwise it + would need access to the read cache of one of the threads that is + not yet removed from the share. + + RETURN + void +*/ + +void init_io_cache_share(IO_CACHE *read_cache, IO_CACHE_SHARE *cshare, + IO_CACHE *write_cache, uint num_threads) { - DBUG_ASSERT(info->type == READ_CACHE); - pthread_mutex_init(&s->mutex, MY_MUTEX_INIT_FAST); - pthread_cond_init (&s->cond, 0); - s->total=s->count=num_threads-1; - s->active=0; - info->share=s; - info->read_function=_my_b_read_r; - info->current_pos= info->current_end= 0; + DBUG_ENTER("init_io_cache_share"); + DBUG_PRINT("io_cache_share", ("read_cache: 0x%lx share: 0x%lx " + "write_cache: 0x%lx threads: %u", + read_cache, cshare, write_cache, num_threads)); + + DBUG_ASSERT(num_threads > 1); + DBUG_ASSERT(read_cache->type == READ_CACHE); + DBUG_ASSERT(!write_cache || (write_cache->type == WRITE_CACHE)); + + pthread_mutex_init(&cshare->mutex, MY_MUTEX_INIT_FAST); + pthread_cond_init(&cshare->cond, 0); + pthread_cond_init(&cshare->cond_writer, 0); + + cshare->running_threads= num_threads; + cshare->total_threads= num_threads; + cshare->error= 0; /* Initialize. */ + cshare->buffer= read_cache->buffer; + cshare->read_end= NULL; /* See function comment of lock_io_cache(). */ + cshare->pos_in_file= 0; /* See function comment of lock_io_cache(). */ + cshare->source_cache= write_cache; /* Can be NULL. */ + + read_cache->share= cshare; + read_cache->read_function= _my_b_read_r; + read_cache->current_pos= NULL; + read_cache->current_end= NULL; + + if (write_cache) + write_cache->share= cshare; + + DBUG_VOID_RETURN; } + /* - Remove a thread from shared access to IO_CACHE - Every thread should do that on exit for not - to deadlock other threads + Remove a thread from shared access to IO_CACHE. + + SYNOPSIS + remove_io_thread() + cache The IO_CACHE to be removed from the share. + + NOTE + + Every thread must do that on exit for not to deadlock other threads. + + The last thread destroys the pthread resources. + + A writer flushes its cache first. + + RETURN + void */ -void remove_io_thread(IO_CACHE *info) + +void remove_io_thread(IO_CACHE *cache) { - IO_CACHE_SHARE *s=info->share; + IO_CACHE_SHARE *cshare= cache->share; + uint total; + DBUG_ENTER("remove_io_thread"); - pthread_mutex_lock(&s->mutex); - s->total--; - if (! s->count--) - pthread_cond_signal(&s->cond); - pthread_mutex_unlock(&s->mutex); -} + /* If the writer goes, it needs to flush the write cache. */ + if (cache == cshare->source_cache) + flush_io_cache(cache); -static int lock_io_cache(IO_CACHE *info, my_off_t pos) -{ - int total; - IO_CACHE_SHARE *s=info->share; + pthread_mutex_lock(&cshare->mutex); + DBUG_PRINT("io_cache_share", ("%s: 0x%lx", + (cache == cshare->source_cache) ? + "writer" : "reader", cache)); - pthread_mutex_lock(&s->mutex); - if (!s->count) + /* Remove from share. */ + total= --cshare->total_threads; + DBUG_PRINT("io_cache_share", ("remaining threads: %u", total)); + + /* Detach from share. */ + cache->share= NULL; + + /* If the writer goes, let the readers know. */ + if (cache == cshare->source_cache) { - s->count=s->total; - return 1; + DBUG_PRINT("io_cache_share", ("writer leaves")); + cshare->source_cache= NULL; } - total=s->total; - s->count--; - while (!s->active || s->active->pos_in_file < pos) - pthread_cond_wait(&s->cond, &s->mutex); + /* If all threads are waiting for me to join the lock, wake them. */ + if (!--cshare->running_threads) + { + DBUG_PRINT("io_cache_share", ("the last running thread leaves, wake all")); + pthread_cond_signal(&cshare->cond_writer); + pthread_cond_broadcast(&cshare->cond); + } - if (s->total < total && - (!s->active || s->active->pos_in_file < pos)) - return 1; + pthread_mutex_unlock(&cshare->mutex); - pthread_mutex_unlock(&s->mutex); - return 0; + if (!total) + { + DBUG_PRINT("io_cache_share", ("last thread removed, destroy share")); + pthread_cond_destroy (&cshare->cond_writer); + pthread_cond_destroy (&cshare->cond); + pthread_mutex_destroy(&cshare->mutex); + } + + DBUG_VOID_RETURN; } -static void unlock_io_cache(IO_CACHE *info) + +/* + Lock IO cache and wait for all other threads to join. + + SYNOPSIS + lock_io_cache() + cache The cache of the thread entering the lock. + pos File position of the block to read. + Unused for the write thread. + + DESCRIPTION + + Wait for all threads to finish with the current buffer. We want + all threads to proceed in concert. The last thread to join + lock_io_cache() will read the block from file and all threads start + to use it. Then they will join again for reading the next block. + + The waiting threads detect a fresh buffer by comparing + cshare->pos_in_file with the position they want to process next. + Since the first block may start at position 0, we take + cshare->read_end as an additional condition. This variable is + initialized to NULL and will be set after a block of data is written + to the buffer. + + RETURN + 1 OK, lock in place, go ahead and read. + 0 OK, unlocked, another thread did the read. +*/ + +static int lock_io_cache(IO_CACHE *cache, my_off_t pos) { - pthread_cond_broadcast(&info->share->cond); - pthread_mutex_unlock(&info->share->mutex); + IO_CACHE_SHARE *cshare= cache->share; + DBUG_ENTER("lock_io_cache"); + + /* Enter the lock. */ + pthread_mutex_lock(&cshare->mutex); + cshare->running_threads--; + DBUG_PRINT("io_cache_share", ("%s: 0x%lx pos: %lu running: %u", + (cache == cshare->source_cache) ? + "writer" : "reader", cache, (ulong) pos, + cshare->running_threads)); + + if (cshare->source_cache) + { + /* A write cache is synchronized to the read caches. */ + + if (cache == cshare->source_cache) + { + /* The writer waits until all readers are here. */ + while (cshare->running_threads) + { + DBUG_PRINT("io_cache_share", ("writer waits in lock")); + pthread_cond_wait(&cshare->cond_writer, &cshare->mutex); + } + DBUG_PRINT("io_cache_share", ("writer awoke, going to copy")); + + /* Stay locked. Leave the lock later by unlock_io_cache(). */ + DBUG_RETURN(1); + } + + /* The last thread wakes the writer. */ + if (!cshare->running_threads) + { + DBUG_PRINT("io_cache_share", ("waking writer")); + pthread_cond_signal(&cshare->cond_writer); + } + + /* + Readers wait until the data is copied from the writer. Another + reason to stop waiting is the removal of the write thread. If this + happens, we leave the lock with old data in the buffer. + */ + while ((!cshare->read_end || (cshare->pos_in_file < pos)) && + cshare->source_cache) + { + DBUG_PRINT("io_cache_share", ("reader waits in lock")); + pthread_cond_wait(&cshare->cond, &cshare->mutex); + } + + /* + If the writer was removed from the share while this thread was + asleep, we need to simulate an EOF condition. The writer cannot + reset the share variables as they might still be in use by readers + of the last block. When we awake here then because the last + joining thread signalled us. If the writer is not the last, it + will not signal. So it is safe to clear the buffer here. + */ + if (!cshare->read_end || (cshare->pos_in_file < pos)) + { + DBUG_PRINT("io_cache_share", ("reader found writer removed. EOF")); + cshare->read_end= cshare->buffer; /* Empty buffer. */ + cshare->error= 0; /* EOF is not an error. */ + } + } + else + { + /* + There are read caches only. The last thread arriving in + lock_io_cache() continues with a locked cache and reads the block. + */ + if (!cshare->running_threads) + { + DBUG_PRINT("io_cache_share", ("last thread joined, going to read")); + /* Stay locked. Leave the lock later by unlock_io_cache(). */ + DBUG_RETURN(1); + } + + /* + All other threads wait until the requested block is read by the + last thread arriving. Another reason to stop waiting is the + removal of a thread. If this leads to all threads being in the + lock, we have to continue also. The first of the awaken threads + will then do the read. + */ + while ((!cshare->read_end || (cshare->pos_in_file < pos)) && + cshare->running_threads) + { + DBUG_PRINT("io_cache_share", ("reader waits in lock")); + pthread_cond_wait(&cshare->cond, &cshare->mutex); + } + + /* If the block is not yet read, continue with a locked cache and read. */ + if (!cshare->read_end || (cshare->pos_in_file < pos)) + { + DBUG_PRINT("io_cache_share", ("reader awoke, going to read")); + /* Stay locked. Leave the lock later by unlock_io_cache(). */ + DBUG_RETURN(1); + } + + /* Another thread did read the block already. */ + } + DBUG_PRINT("io_cache_share", ("reader awoke, going to process %u bytes", + cshare->read_end ? (uint) + (cshare->read_end - cshare->buffer) : 0)); + + /* + Leave the lock. Do not call unlock_io_cache() later. The thread that + filled the buffer did this and marked all threads as running. + */ + pthread_mutex_unlock(&cshare->mutex); + DBUG_RETURN(0); +} + + +/* + Unlock IO cache. + + SYNOPSIS + unlock_io_cache() + cache The cache of the thread leaving the lock. + + NOTE + This is called by the thread that filled the buffer. It marks all + threads as running and awakes them. This must not be done by any + other thread. + + Do not signal cond_writer. Either there is no writer or the writer + is the only one who can call this function. + + The reason for resetting running_threads to total_threads before + waking all other threads is that it could be possible that this + thread is so fast with processing the buffer that it enters the lock + before even one other thread has left it. If every awoken thread + would increase running_threads by one, this thread could think that + he is again the last to join and would not wait for the other + threads to process the data. + + RETURN + void +*/ + +static void unlock_io_cache(IO_CACHE *cache) +{ + IO_CACHE_SHARE *cshare= cache->share; + DBUG_ENTER("unlock_io_cache"); + DBUG_PRINT("io_cache_share", ("%s: 0x%lx pos: %lu running: %u", + (cache == cshare->source_cache) ? + "writer" : "reader", + cache, (ulong) cshare->pos_in_file, + cshare->total_threads)); + + cshare->running_threads= cshare->total_threads; + pthread_cond_broadcast(&cshare->cond); + pthread_mutex_unlock(&cshare->mutex); + DBUG_VOID_RETURN; } @@ -567,7 +867,7 @@ static void unlock_io_cache(IO_CACHE *info) SYNOPSIS _my_b_read_r() - info IO_CACHE pointer + cache IO_CACHE pointer Buffer Buffer to retrieve count bytes from file Count Number of bytes to read into Buffer @@ -579,7 +879,7 @@ static void unlock_io_cache(IO_CACHE *info) It works as follows: when a thread tries to read from a file (that is, after using all the data from the (shared) buffer), it just - hangs on lock_io_cache(), wating for other threads. When the very + hangs on lock_io_cache(), waiting for other threads. When the very last thread attempts a read, lock_io_cache() returns 1, the thread does actual IO and unlock_io_cache(), which signals all the waiting threads that data is in the buffer. @@ -599,16 +899,17 @@ static void unlock_io_cache(IO_CACHE *info) 1 Error: can't read requested characters */ -int _my_b_read_r(register IO_CACHE *info, byte *Buffer, uint Count) +int _my_b_read_r(register IO_CACHE *cache, byte *Buffer, uint Count) { my_off_t pos_in_file; uint length, diff_length, left_length; + IO_CACHE_SHARE *cshare= cache->share; DBUG_ENTER("_my_b_read_r"); - if ((left_length= (uint) (info->read_end - info->read_pos))) + if ((left_length= (uint) (cache->read_end - cache->read_pos))) { DBUG_ASSERT(Count >= left_length); /* User is not using my_b_read() */ - memcpy(Buffer, info->read_pos, (size_t) (left_length)); + memcpy(Buffer, cache->read_pos, (size_t) (left_length)); Buffer+= left_length; Count-= left_length; } @@ -616,55 +917,133 @@ int _my_b_read_r(register IO_CACHE *info, byte *Buffer, uint Count) { int cnt, len; - pos_in_file= info->pos_in_file + (info->read_end - info->buffer); + pos_in_file= cache->pos_in_file + (cache->read_end - cache->buffer); diff_length= (uint) (pos_in_file & (IO_SIZE-1)); length=IO_ROUND_UP(Count+diff_length)-diff_length; - length=(length <= info->read_length) ? - length + IO_ROUND_DN(info->read_length - length) : - length - IO_ROUND_UP(length - info->read_length) ; - if (info->type != READ_FIFO && - (length > (info->end_of_file - pos_in_file))) - length= (uint) (info->end_of_file - pos_in_file); + length= ((length <= cache->read_length) ? + length + IO_ROUND_DN(cache->read_length - length) : + length - IO_ROUND_UP(length - cache->read_length)); + if (cache->type != READ_FIFO && + (length > (cache->end_of_file - pos_in_file))) + length= (uint) (cache->end_of_file - pos_in_file); if (length == 0) { - info->error= (int) left_length; + cache->error= (int) left_length; DBUG_RETURN(1); } - if (lock_io_cache(info, pos_in_file)) + if (lock_io_cache(cache, pos_in_file)) { - info->share->active=info; - if (info->seek_not_done) /* File touched, do seek */ - VOID(my_seek(info->file,pos_in_file,MY_SEEK_SET,MYF(0))); - len=(int)my_read(info->file,info->buffer, length, info->myflags); - info->read_end=info->buffer + (len == -1 ? 0 : len); - info->error=(len == (int)length ? 0 : len); - info->pos_in_file=pos_in_file; - unlock_io_cache(info); + /* With a synchronized write/read cache we won't come here... */ + DBUG_ASSERT(!cshare->source_cache); + /* + ... unless the writer has gone before this thread entered the + lock. Simulate EOF in this case. It can be distinguished by + cache->file. + */ + if (cache->file < 0) + len= 0; + else + { + if (cache->seek_not_done) /* File touched, do seek */ + VOID(my_seek(cache->file, pos_in_file, MY_SEEK_SET, MYF(0))); + len= (int) my_read(cache->file, cache->buffer, length, cache->myflags); + } + DBUG_PRINT("io_cache_share", ("read %d bytes", len)); + + cache->read_end= cache->buffer + (len == -1 ? 0 : len); + cache->error= (len == (int)length ? 0 : len); + cache->pos_in_file= pos_in_file; + + /* Copy important values to the share. */ + cshare->error= cache->error; + cshare->read_end= cache->read_end; + cshare->pos_in_file= pos_in_file; + + /* Mark all threads as running and wake them. */ + unlock_io_cache(cache); } else { - info->error= info->share->active->error; - info->read_end= info->share->active->read_end; - info->pos_in_file= info->share->active->pos_in_file; - len= (info->error == -1 ? -1 : info->read_end-info->buffer); + /* + With a synchronized write/read cache readers always come here. + Copy important values from the share. + */ + cache->error= cshare->error; + cache->read_end= cshare->read_end; + cache->pos_in_file= cshare->pos_in_file; + + len= ((cache->error == -1) ? -1 : cache->read_end - cache->buffer); } - info->read_pos=info->buffer; - info->seek_not_done=0; + cache->read_pos= cache->buffer; + cache->seek_not_done= 0; if (len <= 0) { - info->error= (int) left_length; + DBUG_PRINT("io_cache_share", ("reader error. len %d left %u", + len, left_length)); + cache->error= (int) left_length; DBUG_RETURN(1); } cnt= ((uint) len > Count) ? (int) Count : len; - memcpy(Buffer, info->read_pos, (size_t) cnt); + memcpy(Buffer, cache->read_pos, (size_t) cnt); Count -= cnt; Buffer+= cnt; left_length+= cnt; - info->read_pos+= cnt; + cache->read_pos+= cnt; } DBUG_RETURN(0); } -#endif + + +/* + Copy data from write cache to read cache. + + SYNOPSIS + copy_to_read_buffer() + write_cache The write cache. + write_buffer The source of data, mostly the cache buffer. + write_length The number of bytes to copy. + + NOTE + The write thread will wait for all read threads to join the cache + lock. Then it copies the data over and wakes the read threads. + + RETURN + void +*/ + +static void copy_to_read_buffer(IO_CACHE *write_cache, + const byte *write_buffer, uint write_length) +{ + IO_CACHE_SHARE *cshare= write_cache->share; + + DBUG_ASSERT(cshare->source_cache == write_cache); + /* + write_length is usually less or equal to buffer_length. + It can be bigger if _my_b_write() is called with a big length. + */ + while (write_length) + { + uint copy_length= min(write_length, write_cache->buffer_length); + int __attribute__((unused)) rc; + + rc= lock_io_cache(write_cache, write_cache->pos_in_file); + /* The writing thread does always have the lock when it awakes. */ + DBUG_ASSERT(rc); + + memcpy(cshare->buffer, write_buffer, copy_length); + + cshare->error= 0; + cshare->read_end= cshare->buffer + copy_length; + cshare->pos_in_file= write_cache->pos_in_file; + + /* Mark all threads as running and wake them. */ + unlock_io_cache(write_cache); + + write_buffer+= copy_length; + write_length-= copy_length; + } +} +#endif /*THREAD*/ /* @@ -1016,6 +1395,7 @@ int _my_b_write(register IO_CACHE *info, const byte *Buffer, uint Count) Buffer+=rest_length; Count-=rest_length; info->write_pos+=rest_length; + if (flush_io_cache(info)) return 1; if (Count >= IO_SIZE) @@ -1028,6 +1408,23 @@ int _my_b_write(register IO_CACHE *info, const byte *Buffer, uint Count) } if (my_write(info->file,Buffer,(uint) length,info->myflags | MY_NABP)) return info->error= -1; + +#ifdef THREAD + /* + In case of a shared I/O cache with a writer we normally do direct + write cache to read cache copy. Simulate this here by direct + caller buffer to read cache copy. Do it after the write so that + the cache readers actions on the flushed part can go in parallel + with the write of the extra stuff. copy_to_read_buffer() + synchronizes writer and readers so that after this call the + readers can act on the extra stuff while the writer can go ahead + and prepare the next output. copy_to_read_buffer() relies on + info->pos_in_file. + */ + if (info->share) + copy_to_read_buffer(info, Buffer, length); +#endif + Count-=length; Buffer+=length; info->pos_in_file+=length; @@ -1048,6 +1445,14 @@ int my_b_append(register IO_CACHE *info, const byte *Buffer, uint Count) { uint rest_length,length; +#ifdef THREAD + /* + Assert that we cannot come here with a shared cache. If we do one + day, we might need to add a call to copy_to_read_buffer(). + */ + DBUG_ASSERT(!info->share); +#endif + lock_append_buffer(info); rest_length=(uint) (info->write_end - info->write_pos); if (Count <= rest_length) @@ -1108,6 +1513,14 @@ int my_block_write(register IO_CACHE *info, const byte *Buffer, uint Count, uint length; int error=0; +#ifdef THREAD + /* + Assert that we cannot come here with a shared cache. If we do one + day, we might need to add a call to copy_to_read_buffer(). + */ + DBUG_ASSERT(!info->share); +#endif + if (pos < info->pos_in_file) { /* Of no overlap, write everything without buffering */ @@ -1184,6 +1597,17 @@ int my_b_flush_io_cache(IO_CACHE *info, int need_append_buffer_lock) if ((length=(uint) (info->write_pos - info->write_buffer))) { +#ifdef THREAD + /* + In case of a shared I/O cache with a writer we do direct write + cache to read cache copy. Do it before the write here so that + the readers can work in parallel with the write. + copy_to_read_buffer() relies on info->pos_in_file. + */ + if (info->share) + copy_to_read_buffer(info, info->write_buffer, length); +#endif + pos_in_file=info->pos_in_file; /* If we have append cache, we always open the file with @@ -1262,16 +1686,10 @@ int end_io_cache(IO_CACHE *info) #ifdef THREAD /* - if IO_CACHE is shared between several threads, only one - thread needs to call end_io_cache() - just as init_io_cache() - should be called only once and then memcopy'ed + Every thread must call remove_io_thread(). The last one destroys + the share elements. */ - if (info->share) - { - pthread_cond_destroy (&info->share->cond); - pthread_mutex_destroy(&info->share->mutex); - info->share=0; - } + DBUG_ASSERT(!info->share || !info->share->total_threads); #endif if ((pre_close=info->pre_close)) From 014c1c885e5f99238c321a39d8db75e7ded0b963 Mon Sep 17 00:00:00 2001 From: "istruewing@chilla.local" <> Date: Mon, 9 Oct 2006 22:16:22 +0200 Subject: [PATCH 11/60] Bug#8283 - OPTIMIZE TABLE causes data loss After merge fix. MyISAM version 10. --- mysql-test/r/myisam.result | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/myisam.result b/mysql-test/r/myisam.result index e514867ee43..b85d834420e 100644 --- a/mysql-test/r/myisam.result +++ b/mysql-test/r/myisam.result @@ -841,7 +841,7 @@ _id DELETE FROM t1 WHERE _id < 8; SHOW TABLE STATUS LIKE 't1'; Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment -t1 MyISAM 9 Dynamic 2 # # # # 140 # # # # # # +t1 MyISAM 10 Dynamic 2 # # # # 140 # # # # # # CHECK TABLE t1 EXTENDED; Table Op Msg_type Msg_text test.t1 check status OK @@ -853,7 +853,7 @@ Table Op Msg_type Msg_text test.t1 check status OK SHOW TABLE STATUS LIKE 't1'; Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment -t1 MyISAM 9 Dynamic 2 # # # # 0 # # # # # # +t1 MyISAM 10 Dynamic 2 # # # # 0 # # # # # # SELECT _id FROM t1; _id 8 @@ -900,7 +900,7 @@ _id DELETE FROM t1 WHERE _id < 8; SHOW TABLE STATUS LIKE 't1'; Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment -t1 MyISAM 9 Dynamic 2 # # # # 140 # # # # # # +t1 MyISAM 10 Dynamic 2 # # # # 140 # # # # # # CHECK TABLE t1 EXTENDED; Table Op Msg_type Msg_text test.t1 check status OK @@ -912,7 +912,7 @@ Table Op Msg_type Msg_text test.t1 check status OK SHOW TABLE STATUS LIKE 't1'; Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment -t1 MyISAM 9 Dynamic 2 # # # # 140 # # # # # # +t1 MyISAM 10 Dynamic 2 # # # # 140 # # # # # # SELECT _id FROM t1; _id 8 From bd69350e43e3c400ce7085520ed7f800a231e073 Mon Sep 17 00:00:00 2001 From: "istruewing@chilla.local" <> Date: Tue, 10 Oct 2006 20:01:39 +0200 Subject: [PATCH 12/60] Bug#8283 - OPTIMIZE TABLE causes data loss After merge fix. Renamed 'info' -> 'sort_param'. --- storage/myisam/sort.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/myisam/sort.c b/storage/myisam/sort.c index f80db31f112..37eb1b5bde5 100644 --- a/storage/myisam/sort.c +++ b/storage/myisam/sort.c @@ -455,7 +455,7 @@ err: close_cached_file(&sort_param->tempfile_for_exceptions); ok: - free_root(&info->wordroot, MYF(0)); + free_root(&sort_param->wordroot, MYF(0)); /* Detach from the share if the writer is involved. Avoid others to be blocked. This includes a flush of the write buffer. This will From 01f11289ed24deee2805eb6bb05a17ff0cae1397 Mon Sep 17 00:00:00 2001 From: "istruewing@chilla.local" <> Date: Tue, 10 Oct 2006 20:30:33 +0200 Subject: [PATCH 13/60] Bug#21476 - Lost Database Connection During Query Raised STACK_MIN_SIZE for Debian GNU/Linux Sid, Linux kernel 2.6.16, gcc version 3.3.6 (Debian 1:3.3.6-13), libc6-dbg 2.3.6.ds1-4, Pentium4 (x86), BUILD/compile-pentium-debug-max Raised about 100 Bytes above the required minimum. --- sql/mysql_priv.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 339ca9d965a..f5fada5c164 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -150,7 +150,7 @@ MY_LOCALE *my_locale_by_name(const char *name); Feel free to raise this by the smallest amount you can to get the "execution_constants" test to pass. */ -#define STACK_MIN_SIZE 9336 // Abort if less stack during eval. +#define STACK_MIN_SIZE 10788 // Abort if less stack during eval. #define STACK_MIN_SIZE_FOR_OPEN 1024*80 #define STACK_BUFF_ALLOC 256 // For stack overrun checks From 296d64dbc3376aeb01c837459502e72225330074 Mon Sep 17 00:00:00 2001 From: "istruewing@chilla.local" <> Date: Wed, 11 Oct 2006 12:24:59 +0200 Subject: [PATCH 14/60] Bug#12240 - Rows Examined in Slow Log showing incorrect number? Examined rows are counted for every join part. The per-join-part counter was incremented over all iterations. The result variable was replaced at the end of every iteration. The final result was the number of examined rows by the join part that ended its execution as the last one. The numbers of other join parts was lost. Now we reset the per-join-part counter before every iteration and add it to the result variable at the end of the iteration. That way we get the sum of all iterations of all join parts. No test case. Testing this needs a look into the slow query log. I don't know of a way to do this portably with the test suite. --- sql/sql_select.cc | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 74a9fc573c4..5a5c7663365 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1171,12 +1171,13 @@ JOIN::exec() thd->examined_row_count= 0; DBUG_VOID_RETURN; } - /* - don't reset the found rows count if there're no tables - as FOUND_ROWS() may be called. - */ + /* + Don't reset the found rows count if there're no tables as + FOUND_ROWS() may be called. Never reset the examined row count here. + It must be accumulated from all join iterations of all join parts. + */ if (tables) - thd->limit_found_rows= thd->examined_row_count= 0; + thd->limit_found_rows= 0; if (zero_result_cause) { @@ -1224,6 +1225,12 @@ JOIN::exec() List *curr_all_fields= &all_fields; List *curr_fields_list= &fields_list; TABLE *curr_tmp_table= 0; + /* + Initialize examined rows here because the values from all join parts + must be accumulated in examined_row_count. Hence every join + iteration must count from zero. + */ + curr_join->examined_rows= 0; /* Create a tmp table if distinct or if the sort is too complicated */ if (need_tmp) @@ -1572,7 +1579,10 @@ JOIN::exec() error= thd->net.report_error ? -1 : do_select(curr_join, curr_fields_list, NULL, procedure); thd->limit_found_rows= curr_join->send_records; - thd->examined_row_count= curr_join->examined_rows; + /* Accumulate the counts from all join iterations of all join parts. */ + thd->examined_row_count+= curr_join->examined_rows; + DBUG_PRINT("counts", ("thd->examined_row_count: %lu", + (ulong) thd->examined_row_count)); DBUG_VOID_RETURN; } @@ -6179,6 +6189,8 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records) return -2; /* purecov: inspected */ } join->examined_rows++; + DBUG_PRINT("counts", ("join->examined_rows++: %lu", + (ulong) join->examined_rows)); join->thd->row_count++; if (!on_expr || on_expr->val_int()) { From 8e794f458bbe31ef04cfd7b7e433e8de7e6dd173 Mon Sep 17 00:00:00 2001 From: "ted@ted.mysql.internal" <> Date: Wed, 11 Oct 2006 15:53:56 +0400 Subject: [PATCH 15/60] BUG#21524 ps.test updated to meet recent changes in SQL parser --- mysql-test/r/ps.result | 461 ++++++++++++++++++++------------------ mysql-test/t/disabled.def | 1 - mysql-test/t/ps.test | 105 +++++---- 3 files changed, 290 insertions(+), 277 deletions(-) diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index b7dae03bf47..f7a9f0440a3 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -499,54 +499,6 @@ create temporary table if not exists t1 (a1 int); execute stmt; drop temporary table t1; deallocate prepare stmt; -CREATE TABLE t1( -ID int(10) unsigned NOT NULL auto_increment, -Member_ID varchar(15) NOT NULL default '', -Action varchar(12) NOT NULL, -Action_Date datetime NOT NULL, -Track varchar(15) default NULL, -User varchar(12) default NULL, -Date_Updated timestamp NOT NULL default CURRENT_TIMESTAMP on update -CURRENT_TIMESTAMP, -PRIMARY KEY (ID), -KEY Action (Action), -KEY Action_Date (Action_Date) -); -INSERT INTO t1(Member_ID, Action, Action_Date, Track) VALUES -('111111', 'Disenrolled', '2006-03-01', 'CAD' ), -('111111', 'Enrolled', '2006-03-01', 'CAD' ), -('111111', 'Disenrolled', '2006-07-03', 'CAD' ), -('222222', 'Enrolled', '2006-03-07', 'CAD' ), -('222222', 'Enrolled', '2006-03-07', 'CHF' ), -('222222', 'Disenrolled', '2006-08-02', 'CHF' ), -('333333', 'Enrolled', '2006-03-01', 'CAD' ), -('333333', 'Disenrolled', '2006-03-01', 'CAD' ), -('444444', 'Enrolled', '2006-03-01', 'CAD' ), -('555555', 'Disenrolled', '2006-03-01', 'CAD' ), -('555555', 'Enrolled', '2006-07-21', 'CAD' ), -('555555', 'Disenrolled', '2006-03-01', 'CHF' ), -('666666', 'Enrolled', '2006-02-09', 'CAD' ), -('666666', 'Enrolled', '2006-05-12', 'CHF' ), -('666666', 'Disenrolled', '2006-06-01', 'CAD' ); -PREPARE STMT FROM -"SELECT GROUP_CONCAT(Track SEPARATOR ', ') FROM t1 - WHERE Member_ID=? AND Action='Enrolled' AND - (Track,Action_Date) IN (SELECT Track, MAX(Action_Date) FROM t1 - WHERE Member_ID=? - GROUP BY Track - HAVING Track>='CAD' AND - MAX(Action_Date)>'2006-03-01')"; -SET @id='111111'; -EXECUTE STMT USING @id,@id; -GROUP_CONCAT(Track SEPARATOR ', ') -NULL -SET @id='222222'; -EXECUTE STMT USING @id,@id; -GROUP_CONCAT(Track SEPARATOR ', ') -CAD -DEALLOCATE PREPARE STMT; -DROP TABLE t1; -End of 4.1 tests create table t1 (a varchar(20)); insert into t1 values ('foo'); prepare stmt FROM 'SELECT char_length (a) FROM t1'; @@ -564,77 +516,6 @@ SELECT FOUND_ROWS(); FOUND_ROWS() 2 deallocate prepare stmt; -create table t1 (a char(3) not null, b char(3) not null, -c char(3) not null, primary key (a, b, c)); -create table t2 like t1; -prepare stmt from -"select t1.a from (t1 left outer join t2 on t2.a=1 and t1.b=t2.b) - where t1.a=1"; -execute stmt; -a -execute stmt; -a -execute stmt; -a -prepare stmt from -"select t1.a, t1.b, t1.c, t2.a, t2.b, t2.c from -(t1 left outer join t2 on t2.a=? and t1.b=t2.b) -left outer join t2 t3 on t3.a=? where t1.a=?"; -set @a:=1, @b:=1, @c:=1; -execute stmt using @a, @b, @c; -a b c a b c -execute stmt using @a, @b, @c; -a b c a b c -execute stmt using @a, @b, @c; -a b c a b c -deallocate prepare stmt; -drop table t1,t2; -SET @aux= "SELECT COUNT(*) - FROM INFORMATION_SCHEMA.COLUMNS A, - INFORMATION_SCHEMA.COLUMNS B - WHERE A.TABLE_SCHEMA = B.TABLE_SCHEMA - AND A.TABLE_NAME = B.TABLE_NAME - AND A.COLUMN_NAME = B.COLUMN_NAME AND - A.TABLE_NAME = 'user'"; -prepare my_stmt from @aux; -execute my_stmt; -COUNT(*) -39 -execute my_stmt; -COUNT(*) -39 -execute my_stmt; -COUNT(*) -39 -deallocate prepare my_stmt; -drop procedure if exists p1| -drop table if exists t1| -create table t1 (id int)| -insert into t1 values(1)| -create procedure p1(a int, b int) -begin -declare c int; -select max(id)+1 into c from t1; -insert into t1 select a+b; -insert into t1 select a-b; -insert into t1 select a-c; -end| -set @a= 3, @b= 4| -prepare stmt from "call p1(?, ?)"| -execute stmt using @a, @b| -execute stmt using @a, @b| -select * from t1| -id -1 -7 --1 -1 -7 --1 --5 -deallocate prepare stmt| -drop procedure p1| -drop table t1| drop table if exists t1; Warnings: Note 1051 Unknown table 't1' @@ -698,47 +579,6 @@ id 3 deallocate prepare stmt; drop table t1, t2; -create table t1 (a int); -insert into t1 (a) values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10); -prepare stmt from "select * from t1 limit ?, ?"; -set @offset=0, @limit=1; -execute stmt using @offset, @limit; -a -1 -select * from t1 limit 0, 1; -a -1 -set @offset=3, @limit=2; -execute stmt using @offset, @limit; -a -4 -5 -select * from t1 limit 3, 2; -a -4 -5 -prepare stmt from "select * from t1 limit ?"; -execute stmt using @limit; -a -1 -2 -prepare stmt from "select * from t1 where a in (select a from t1 limit ?)"; -ERROR 42000: This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery' -prepare stmt from "select * from t1 union all select * from t1 limit ?, ?"; -set @offset=9; -set @limit=2; -execute stmt using @offset, @limit; -a -10 -1 -prepare stmt from "(select * from t1 limit ?, ?) union all - (select * from t1 limit ?, ?) order by a limit ?"; -execute stmt using @offset, @limit, @offset, @limit, @limit; -a -10 -10 -drop table t1; -deallocate prepare stmt; create table t1 (id int); prepare stmt from "insert into t1 (id) select id from t1 union select id from t1"; execute stmt; @@ -839,15 +679,6 @@ ERROR 42000: You have an error in your SQL syntax; check the manual that corresp select ? from t1; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '? from t1' at line 1 drop table t1; -CREATE TABLE b12651_T1(a int) ENGINE=MYISAM; -CREATE TABLE b12651_T2(b int) ENGINE=MYISAM; -CREATE VIEW b12651_V1 as SELECT b FROM b12651_T2; -PREPARE b12651 FROM 'SELECT 1 FROM b12651_T1 WHERE a IN (SELECT b FROM b12651_V1)'; -EXECUTE b12651; -1 -DROP VIEW b12651_V1; -DROP TABLE b12651_T1, b12651_T2; -DEALLOCATE PREPARE b12651; prepare stmt from "select @@time_zone"; execute stmt; @@time_zone @@ -1064,6 +895,194 @@ select @@max_prepared_stmt_count, @@prepared_stmt_count; @@max_prepared_stmt_count @@prepared_stmt_count 3 0 set global max_prepared_stmt_count= @old_max_prepared_stmt_count; +drop table if exists t1; +create temporary table if not exists t1 (a1 int); +prepare stmt from "delete t1 from t1 where (cast(a1/3 as unsigned) * 3) = a1"; +drop temporary table t1; +create temporary table if not exists t1 (a1 int); +execute stmt; +drop temporary table t1; +create temporary table if not exists t1 (a1 int); +execute stmt; +drop temporary table t1; +create temporary table if not exists t1 (a1 int); +execute stmt; +drop temporary table t1; +deallocate prepare stmt; +CREATE TABLE t1( +ID int(10) unsigned NOT NULL auto_increment, +Member_ID varchar(15) NOT NULL default '', +Action varchar(12) NOT NULL, +Action_Date datetime NOT NULL, +Track varchar(15) default NULL, +User varchar(12) default NULL, +Date_Updated timestamp NOT NULL default CURRENT_TIMESTAMP on update +CURRENT_TIMESTAMP, +PRIMARY KEY (ID), +KEY Action (Action), +KEY Action_Date (Action_Date) +); +INSERT INTO t1(Member_ID, Action, Action_Date, Track) VALUES +('111111', 'Disenrolled', '2006-03-01', 'CAD' ), +('111111', 'Enrolled', '2006-03-01', 'CAD' ), +('111111', 'Disenrolled', '2006-07-03', 'CAD' ), +('222222', 'Enrolled', '2006-03-07', 'CAD' ), +('222222', 'Enrolled', '2006-03-07', 'CHF' ), +('222222', 'Disenrolled', '2006-08-02', 'CHF' ), +('333333', 'Enrolled', '2006-03-01', 'CAD' ), +('333333', 'Disenrolled', '2006-03-01', 'CAD' ), +('444444', 'Enrolled', '2006-03-01', 'CAD' ), +('555555', 'Disenrolled', '2006-03-01', 'CAD' ), +('555555', 'Enrolled', '2006-07-21', 'CAD' ), +('555555', 'Disenrolled', '2006-03-01', 'CHF' ), +('666666', 'Enrolled', '2006-02-09', 'CAD' ), +('666666', 'Enrolled', '2006-05-12', 'CHF' ), +('666666', 'Disenrolled', '2006-06-01', 'CAD' ); +PREPARE STMT FROM +"SELECT GROUP_CONCAT(Track SEPARATOR ', ') FROM t1 + WHERE Member_ID=? AND Action='Enrolled' AND + (Track,Action_Date) IN (SELECT Track, MAX(Action_Date) FROM t1 + WHERE Member_ID=? + GROUP BY Track + HAVING Track>='CAD' AND + MAX(Action_Date)>'2006-03-01')"; +SET @id='111111'; +EXECUTE STMT USING @id,@id; +GROUP_CONCAT(Track SEPARATOR ', ') +NULL +SET @id='222222'; +EXECUTE STMT USING @id,@id; +GROUP_CONCAT(Track SEPARATOR ', ') +CAD +DEALLOCATE PREPARE STMT; +DROP TABLE t1; +End of 4.1 tests +create table t1 (a varchar(20)); +insert into t1 values ('foo'); +prepare stmt FROM 'SELECT char_length (a) FROM t1'; +ERROR 42000: FUNCTION test.char_length does not exist +drop table t1; +create table t1 (a char(3) not null, b char(3) not null, +c char(3) not null, primary key (a, b, c)); +create table t2 like t1; +prepare stmt from +"select t1.a from (t1 left outer join t2 on t2.a=1 and t1.b=t2.b) + where t1.a=1"; +execute stmt; +a +execute stmt; +a +execute stmt; +a +prepare stmt from +"select t1.a, t1.b, t1.c, t2.a, t2.b, t2.c from +(t1 left outer join t2 on t2.a=? and t1.b=t2.b) +left outer join t2 t3 on t3.a=? where t1.a=?"; +set @a:=1, @b:=1, @c:=1; +execute stmt using @a, @b, @c; +a b c a b c +execute stmt using @a, @b, @c; +a b c a b c +execute stmt using @a, @b, @c; +a b c a b c +deallocate prepare stmt; +drop table t1,t2; +SET @aux= "SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS A, + INFORMATION_SCHEMA.COLUMNS B + WHERE A.TABLE_SCHEMA = B.TABLE_SCHEMA + AND A.TABLE_NAME = B.TABLE_NAME + AND A.COLUMN_NAME = B.COLUMN_NAME AND + A.TABLE_NAME = 'user'"; +prepare my_stmt from @aux; +execute my_stmt; +COUNT(*) +39 +execute my_stmt; +COUNT(*) +39 +execute my_stmt; +COUNT(*) +39 +deallocate prepare my_stmt; +drop procedure if exists p1| +drop table if exists t1| +create table t1 (id int)| +insert into t1 values(1)| +create procedure p1(a int, b int) +begin +declare c int; +select max(id)+1 into c from t1; +insert into t1 select a+b; +insert into t1 select a-b; +insert into t1 select a-c; +end| +set @a= 3, @b= 4| +prepare stmt from "call p1(?, ?)"| +execute stmt using @a, @b| +execute stmt using @a, @b| +select * from t1| +id +1 +7 +-1 +1 +7 +-1 +-5 +deallocate prepare stmt| +drop procedure p1| +drop table t1| +create table t1 (a int); +insert into t1 (a) values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10); +prepare stmt from "select * from t1 limit ?, ?"; +set @offset=0, @limit=1; +execute stmt using @offset, @limit; +a +1 +select * from t1 limit 0, 1; +a +1 +set @offset=3, @limit=2; +execute stmt using @offset, @limit; +a +4 +5 +select * from t1 limit 3, 2; +a +4 +5 +prepare stmt from "select * from t1 limit ?"; +execute stmt using @limit; +a +1 +2 +prepare stmt from "select * from t1 where a in (select a from t1 limit ?)"; +ERROR 42000: This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery' +prepare stmt from "select * from t1 union all select * from t1 limit ?, ?"; +set @offset=9; +set @limit=2; +execute stmt using @offset, @limit; +a +10 +1 +prepare stmt from "(select * from t1 limit ?, ?) union all + (select * from t1 limit ?, ?) order by a limit ?"; +execute stmt using @offset, @limit, @offset, @limit, @limit; +a +10 +10 +drop table t1; +deallocate prepare stmt; +CREATE TABLE b12651_T1(a int) ENGINE=MYISAM; +CREATE TABLE b12651_T2(b int) ENGINE=MYISAM; +CREATE VIEW b12651_V1 as SELECT b FROM b12651_T2; +PREPARE b12651 FROM 'SELECT 1 FROM b12651_T1 WHERE a IN (SELECT b FROM b12651_V1)'; +EXECUTE b12651; +1 +DROP VIEW b12651_V1; +DROP TABLE b12651_T1, b12651_T2; +DEALLOCATE PREPARE b12651; create table t1 (id int); prepare ins_call from "insert into t1 (id) values (1)"; execute ins_call; @@ -1365,22 +1384,26 @@ create procedure proc_1() reset query cache; call proc_1(); call proc_1(); call proc_1(); -drop procedure proc_1; create function func_1() returns int begin reset query cache; return 1; end| +ERROR 0A000: RESET is not allowed in stored function or trigger +create function func_1() returns int begin call proc_1(); return 1; end| select func_1(), func_1(), func_1() from dual; -ERROR 0A000: FLUSH is not allowed in stored function or trigger +ERROR 0A000: RESET is not allowed in stored function or trigger drop function func_1; +drop procedure proc_1; prepare abc from "reset query cache"; execute abc; execute abc; execute abc; deallocate prepare abc; create procedure proc_1() reset master; -drop procedure proc_1; create function func_1() returns int begin reset master; return 1; end| +ERROR 0A000: RESET is not allowed in stored function or trigger +create function func_1() returns int begin call proc_1(); return 1; end| select func_1(), func_1(), func_1() from dual; -ERROR 0A000: FLUSH is not allowed in stored function or trigger +ERROR 0A000: RESET is not allowed in stored function or trigger drop function func_1; +drop procedure proc_1; prepare abc from "reset master"; execute abc; execute abc; @@ -1390,11 +1413,13 @@ create procedure proc_1() reset slave; call proc_1(); call proc_1(); call proc_1(); -drop procedure proc_1; create function func_1() returns int begin reset slave; return 1; end| +ERROR 0A000: RESET is not allowed in stored function or trigger +create function func_1() returns int begin call proc_1(); return 1; end| select func_1(), func_1(), func_1() from dual; -ERROR 0A000: FLUSH is not allowed in stored function or trigger +ERROR 0A000: RESET is not allowed in stored function or trigger drop function func_1; +drop procedure proc_1; prepare abc from "reset slave"; execute abc; execute abc; @@ -1429,13 +1454,13 @@ call proc_1(); call proc_1(); call proc_1(); call proc_1(); -drop procedure proc_1; create function func_1() returns int begin flush hosts; return 1; end| ERROR 0A000: FLUSH is not allowed in stored function or trigger +create function func_1() returns int begin call proc_1(); return 1; end| select func_1(), func_1(), func_1() from dual; -ERROR 42000: FUNCTION test.func_1 does not exist +ERROR 0A000: FLUSH is not allowed in stored function or trigger drop function func_1; -ERROR 42000: FUNCTION test.func_1 does not exist +drop procedure proc_1; prepare abc from "flush hosts"; execute abc; execute abc; @@ -1445,13 +1470,13 @@ create procedure proc_1() flush privileges; call proc_1(); call proc_1(); call proc_1(); -drop procedure proc_1; create function func_1() returns int begin flush privileges; return 1; end| ERROR 0A000: FLUSH is not allowed in stored function or trigger +create function func_1() returns int begin call proc_1(); return 1; end| select func_1(), func_1(), func_1() from dual; -ERROR 42000: FUNCTION test.func_1 does not exist +ERROR 0A000: FLUSH is not allowed in stored function or trigger drop function func_1; -ERROR 42000: FUNCTION test.func_1 does not exist +drop procedure proc_1; prepare abc from "flush privileges"; deallocate prepare abc; create procedure proc_1() flush tables with read lock; @@ -1461,9 +1486,13 @@ call proc_1(); unlock tables; call proc_1(); unlock tables; -drop procedure proc_1; create function func_1() returns int begin flush tables with read lock; return 1; end| ERROR 0A000: FLUSH is not allowed in stored function or trigger +create function func_1() returns int begin call proc_1(); return 1; end| +select func_1(), func_1(), func_1() from dual; +ERROR 0A000: FLUSH is not allowed in stored function or trigger +drop function func_1; +drop procedure proc_1; prepare abc from "flush tables with read lock"; execute abc; execute abc; @@ -1474,13 +1503,13 @@ create procedure proc_1() flush tables; call proc_1(); call proc_1(); call proc_1(); -drop procedure proc_1; create function func_1() returns int begin flush tables; return 1; end| ERROR 0A000: FLUSH is not allowed in stored function or trigger +create function func_1() returns int begin call proc_1(); return 1; end| select func_1(), func_1(), func_1() from dual; -ERROR 42000: FUNCTION test.func_1 does not exist +ERROR 0A000: FLUSH is not allowed in stored function or trigger drop function func_1; -ERROR 42000: FUNCTION test.func_1 does not exist +drop procedure proc_1; prepare abc from "flush tables"; execute abc; execute abc; @@ -1540,13 +1569,13 @@ mysql user 0 0 mysql general_log 1 0 mysql host 0 0 flush tables; -drop procedure proc_1; create function func_1() returns int begin flush tables; return 1; end| ERROR 0A000: FLUSH is not allowed in stored function or trigger +create function func_1() returns int begin call proc_1(); return 1; end| select func_1(), func_1(), func_1() from dual; -ERROR 42000: FUNCTION test.func_1 does not exist +ERROR 0A000: FLUSH is not allowed in stored function or trigger drop function func_1; -ERROR 42000: FUNCTION test.func_1 does not exist +drop procedure proc_1; flush tables; select Host, User from mysql.user limit 0; Host User @@ -1603,13 +1632,13 @@ create procedure proc_1() flush logs; call proc_1(); call proc_1(); call proc_1(); -drop procedure proc_1; create function func_1() returns int begin flush logs; return 1; end| ERROR 0A000: FLUSH is not allowed in stored function or trigger +create function func_1() returns int begin call proc_1(); return 1; end| select func_1(), func_1(), func_1() from dual; -ERROR 42000: FUNCTION test.func_1 does not exist +ERROR 0A000: FLUSH is not allowed in stored function or trigger drop function func_1; -ERROR 42000: FUNCTION test.func_1 does not exist +drop procedure proc_1; prepare abc from "flush logs"; execute abc; execute abc; @@ -1619,13 +1648,13 @@ create procedure proc_1() flush status; call proc_1(); call proc_1(); call proc_1(); -drop procedure proc_1; create function func_1() returns int begin flush status; return 1; end| ERROR 0A000: FLUSH is not allowed in stored function or trigger +create function func_1() returns int begin call proc_1(); return 1; end| select func_1(), func_1(), func_1() from dual; -ERROR 42000: FUNCTION test.func_1 does not exist +ERROR 0A000: FLUSH is not allowed in stored function or trigger drop function func_1; -ERROR 42000: FUNCTION test.func_1 does not exist +drop procedure proc_1; prepare abc from "flush status"; execute abc; execute abc; @@ -1635,39 +1664,39 @@ create procedure proc_1() flush slave; call proc_1(); call proc_1(); call proc_1(); -drop procedure proc_1; create function func_1() returns int begin flush slave; return 1; end| ERROR 0A000: FLUSH is not allowed in stored function or trigger +create function func_1() returns int begin call proc_1(); return 1; end| select func_1(), func_1(), func_1() from dual; -ERROR 42000: FUNCTION test.func_1 does not exist +ERROR 0A000: FLUSH is not allowed in stored function or trigger drop function func_1; -ERROR 42000: FUNCTION test.func_1 does not exist +drop procedure proc_1; prepare abc from "flush slave"; execute abc; execute abc; execute abc; deallocate prepare abc; create procedure proc_1() flush master; -drop procedure proc_1; create function func_1() returns int begin flush master; return 1; end| ERROR 0A000: FLUSH is not allowed in stored function or trigger +create function func_1() returns int begin call proc_1(); return 1; end| select func_1(), func_1(), func_1() from dual; -ERROR 42000: FUNCTION test.func_1 does not exist +ERROR 0A000: FLUSH is not allowed in stored function or trigger drop function func_1; -ERROR 42000: FUNCTION test.func_1 does not exist +drop procedure proc_1; prepare abc from "flush master"; deallocate prepare abc; create procedure proc_1() flush des_key_file; call proc_1(); call proc_1(); call proc_1(); -drop procedure proc_1; create function func_1() returns int begin flush des_key_file; return 1; end| ERROR 0A000: FLUSH is not allowed in stored function or trigger +create function func_1() returns int begin call proc_1(); return 1; end| select func_1(), func_1(), func_1() from dual; -ERROR 42000: FUNCTION test.func_1 does not exist +ERROR 0A000: FLUSH is not allowed in stored function or trigger drop function func_1; -ERROR 42000: FUNCTION test.func_1 does not exist +drop procedure proc_1; prepare abc from "flush des_key_file"; execute abc; execute abc; @@ -1677,13 +1706,13 @@ create procedure proc_1() flush user_resources; call proc_1(); call proc_1(); call proc_1(); -drop procedure proc_1; create function func_1() returns int begin flush user_resources; return 1; end| ERROR 0A000: FLUSH is not allowed in stored function or trigger +create function func_1() returns int begin call proc_1(); return 1; end| select func_1(), func_1(), func_1() from dual; -ERROR 42000: FUNCTION test.func_1 does not exist +ERROR 0A000: FLUSH is not allowed in stored function or trigger drop function func_1; -ERROR 42000: FUNCTION test.func_1 does not exist +drop procedure proc_1; prepare abc from "flush user_resources"; execute abc; execute abc; @@ -1763,18 +1792,6 @@ Db Name Definer Type Execute at Interval value Interval field Starts Ends Status execute abc; Db Name Definer Type Execute at Interval value Interval field Starts Ends Status deallocate prepare abc; -create procedure proc_1() show scheduler status; -drop procedure proc_1; -create function func_1() returns int begin show scheduler status; return 1; end| -ERROR 0A000: Not allowed to return a result set from a function -select func_1(), func_1(), func_1() from dual; -ERROR 42000: FUNCTION test.func_1 does not exist -drop function func_1; -ERROR 42000: FUNCTION test.func_1 does not exist -prepare abc from "show scheduler status"; -ERROR HY000: This command is not supported in the prepared statement protocol yet -deallocate prepare abc; -ERROR HY000: Unknown prepared statement handler (abc) given to DEALLOCATE PREPARE drop procedure if exists a; create procedure a() select 42; create procedure proc_1(a char(2)) show create procedure a; @@ -1952,11 +1969,11 @@ ERROR HY000: No paths allowed for shared library drop procedure proc_1; create procedure proc_1() install plugin my_plug soname 'some_plugin.so'; call proc_1(); -ERROR HY000: Can't open shared library '/work/mysql-5.1-runtime/mysql-test/lib/mysql/some_plugin.so' (errno: 0 cannot open shared object file: No such file or directory) +ERROR HY000: Can't open shared library call proc_1(); -ERROR HY000: Can't open shared library '/work/mysql-5.1-runtime/mysql-test/lib/mysql/some_plugin.so' (errno: 22 cannot open shared object file: No such file or directory) +ERROR HY000: Can't open shared library call proc_1(); -ERROR HY000: Can't open shared library '/work/mysql-5.1-runtime/mysql-test/lib/mysql/some_plugin.so' (errno: 22 cannot open shared object file: No such file or directory) +ERROR HY000: Can't open shared library drop procedure proc_1; create function func_1() returns int begin install plugin my_plug soname '/tmp/plugin'; return 1; end| ERROR HY000: Explicit or implicit commit is not allowed in stored function or trigger. @@ -2086,7 +2103,7 @@ drop user pstest_xyz@localhost; deallocate prepare abc; drop event if exists xyz; create function func_1() returns int begin create event xyz on schedule at now() do select 123; return 1; end| -ERROR 0A000: Not allowed to return a result set from a function +ERROR HY000: Recursivity of EVENT DDL statements is forbidden when body is present select func_1(), func_1(), func_1() from dual; ERROR 42000: FUNCTION test.func_1 does not exist drop function func_1; diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index 8c4a76c78a9..1dd1778d68e 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -23,7 +23,6 @@ ndb_autodiscover2 : BUG#18952 2006-02-16 jmiller Needs to be fixed w.r.t #ndb_binlog_ignore_db : BUG#21279 2006-07-25 ingo Randomly throws a warning ndb_load : BUG#17233 2006-05-04 tomas failed load data from infile causes mysqld dbug_assert, binlog not flushed partition_03ndb : BUG#16385 2006-03-24 mikael Partitions: crash when updating a range partitioned NDB table -ps : BUG#21524 2006-08-08 pgalbraith 'ps' test fails in --ps-protocol test AMD64 bit ps_7ndb : BUG#18950 2006-02-16 jmiller create table like does not obtain LOCK_open rpl_ndb_2innodb : BUG#19227 2006-04-20 pekka pk delete apparently not replicated rpl_ndb_2myisam : BUG#19227 Seems to pass currently diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index ac79dbc3434..902d8fcd9d8 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -354,14 +354,14 @@ create table t1 (a int, b int); insert into t1 (a, b) values (1,1), (1,2), (2,1), (2,2); prepare stmt from "explain select * from t1 where t1.a=2 and t1.a=t1.b and t1.b > 1 + ?"; ---replace_column 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - set @v=5; -execute stmt using @v; --replace_column 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - +execute stmt using @v; set @v=0; -execute stmt using @v; --replace_column 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - +execute stmt using @v; set @v=5; +--replace_column 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - execute stmt using @v; drop table t1; deallocate prepare stmt; @@ -1447,13 +1447,15 @@ create procedure proc_1() reset query cache; call proc_1(); call proc_1(); call proc_1(); -drop procedure proc_1; delimiter |; +--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG create function func_1() returns int begin reset query cache; return 1; end| +create function func_1() returns int begin call proc_1(); return 1; end| delimiter ;| --error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG select func_1(), func_1(), func_1() from dual; drop function func_1; +drop procedure proc_1; prepare abc from "reset query cache"; execute abc; execute abc; @@ -1462,13 +1464,15 @@ deallocate prepare abc; create procedure proc_1() reset master; -drop procedure proc_1; delimiter |; +--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG create function func_1() returns int begin reset master; return 1; end| +create function func_1() returns int begin call proc_1(); return 1; end| delimiter ;| --error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG select func_1(), func_1(), func_1() from dual; drop function func_1; +drop procedure proc_1; prepare abc from "reset master"; execute abc; execute abc; @@ -1480,13 +1484,15 @@ create procedure proc_1() reset slave; call proc_1(); call proc_1(); call proc_1(); -drop procedure proc_1; delimiter |; +--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG create function func_1() returns int begin reset slave; return 1; end| +create function func_1() returns int begin call proc_1(); return 1; end| delimiter ;| --error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG select func_1(), func_1(), func_1() from dual; drop function func_1; +drop procedure proc_1; prepare abc from "reset slave"; execute abc; execute abc; @@ -1527,15 +1533,15 @@ call proc_1(); call proc_1(); call proc_1(); call proc_1(); -drop procedure proc_1; delimiter |; --error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG create function func_1() returns int begin flush hosts; return 1; end| +create function func_1() returns int begin call proc_1(); return 1; end| delimiter ;| ---error ER_SP_DOES_NOT_EXIST +--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG select func_1(), func_1(), func_1() from dual; ---error ER_SP_DOES_NOT_EXIST drop function func_1; +drop procedure proc_1; prepare abc from "flush hosts"; execute abc; execute abc; @@ -1547,15 +1553,15 @@ create procedure proc_1() flush privileges; call proc_1(); call proc_1(); call proc_1(); -drop procedure proc_1; delimiter |; --error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG create function func_1() returns int begin flush privileges; return 1; end| +create function func_1() returns int begin call proc_1(); return 1; end| delimiter ;| ---error ER_SP_DOES_NOT_EXIST +--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG select func_1(), func_1(), func_1() from dual; ---error ER_SP_DOES_NOT_EXIST drop function func_1; +drop procedure proc_1; prepare abc from "flush privileges"; deallocate prepare abc; @@ -1567,11 +1573,15 @@ call proc_1(); unlock tables; call proc_1(); unlock tables; -drop procedure proc_1; delimiter |; --error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG create function func_1() returns int begin flush tables with read lock; return 1; end| +create function func_1() returns int begin call proc_1(); return 1; end| delimiter ;| +--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG +select func_1(), func_1(), func_1() from dual; +drop function func_1; +drop procedure proc_1; prepare abc from "flush tables with read lock"; execute abc; execute abc; @@ -1584,15 +1594,15 @@ create procedure proc_1() flush tables; call proc_1(); call proc_1(); call proc_1(); -drop procedure proc_1; delimiter |; --error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG create function func_1() returns int begin flush tables; return 1; end| +create function func_1() returns int begin call proc_1(); return 1; end| delimiter ;| ---error ER_SP_DOES_NOT_EXIST +--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG select func_1(), func_1(), func_1() from dual; ---error ER_SP_DOES_NOT_EXIST drop function func_1; +drop procedure proc_1; prepare abc from "flush tables"; execute abc; execute abc; @@ -1622,15 +1632,15 @@ select Host, User from mysql.user limit 0; select Host, Db from mysql.host limit 0; show open tables from mysql; flush tables; -drop procedure proc_1; delimiter |; --error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG create function func_1() returns int begin flush tables; return 1; end| +create function func_1() returns int begin call proc_1(); return 1; end| delimiter ;| ---error ER_SP_DOES_NOT_EXIST +--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG select func_1(), func_1(), func_1() from dual; ---error ER_SP_DOES_NOT_EXIST drop function func_1; +drop procedure proc_1; flush tables; select Host, User from mysql.user limit 0; select Host, Db from mysql.host limit 0; @@ -1659,15 +1669,15 @@ create procedure proc_1() flush logs; call proc_1(); call proc_1(); call proc_1(); -drop procedure proc_1; delimiter |; --error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG create function func_1() returns int begin flush logs; return 1; end| +create function func_1() returns int begin call proc_1(); return 1; end| delimiter ;| ---error ER_SP_DOES_NOT_EXIST +--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG select func_1(), func_1(), func_1() from dual; ---error ER_SP_DOES_NOT_EXIST drop function func_1; +drop procedure proc_1; prepare abc from "flush logs"; execute abc; execute abc; @@ -1679,15 +1689,15 @@ create procedure proc_1() flush status; call proc_1(); call proc_1(); call proc_1(); -drop procedure proc_1; delimiter |; --error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG create function func_1() returns int begin flush status; return 1; end| +create function func_1() returns int begin call proc_1(); return 1; end| delimiter ;| ---error ER_SP_DOES_NOT_EXIST +--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG select func_1(), func_1(), func_1() from dual; ---error ER_SP_DOES_NOT_EXIST drop function func_1; +drop procedure proc_1; prepare abc from "flush status"; execute abc; execute abc; @@ -1699,15 +1709,15 @@ create procedure proc_1() flush slave; call proc_1(); call proc_1(); call proc_1(); -drop procedure proc_1; delimiter |; --error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG create function func_1() returns int begin flush slave; return 1; end| +create function func_1() returns int begin call proc_1(); return 1; end| delimiter ;| ---error ER_SP_DOES_NOT_EXIST +--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG select func_1(), func_1(), func_1() from dual; ---error ER_SP_DOES_NOT_EXIST drop function func_1; +drop procedure proc_1; prepare abc from "flush slave"; execute abc; execute abc; @@ -1716,15 +1726,15 @@ deallocate prepare abc; create procedure proc_1() flush master; -drop procedure proc_1; delimiter |; --error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG create function func_1() returns int begin flush master; return 1; end| +create function func_1() returns int begin call proc_1(); return 1; end| delimiter ;| ---error ER_SP_DOES_NOT_EXIST +--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG select func_1(), func_1(), func_1() from dual; ---error ER_SP_DOES_NOT_EXIST drop function func_1; +drop procedure proc_1; prepare abc from "flush master"; deallocate prepare abc; @@ -1733,15 +1743,15 @@ create procedure proc_1() flush des_key_file; call proc_1(); call proc_1(); call proc_1(); -drop procedure proc_1; delimiter |; --error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG create function func_1() returns int begin flush des_key_file; return 1; end| +create function func_1() returns int begin call proc_1(); return 1; end| delimiter ;| ---error ER_SP_DOES_NOT_EXIST +--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG select func_1(), func_1(), func_1() from dual; ---error ER_SP_DOES_NOT_EXIST drop function func_1; +drop procedure proc_1; prepare abc from "flush des_key_file"; execute abc; execute abc; @@ -1753,15 +1763,15 @@ create procedure proc_1() flush user_resources; call proc_1(); call proc_1(); call proc_1(); -drop procedure proc_1; delimiter |; --error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG create function func_1() returns int begin flush user_resources; return 1; end| +create function func_1() returns int begin call proc_1(); return 1; end| delimiter ;| ---error ER_SP_DOES_NOT_EXIST +--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG select func_1(), func_1(), func_1() from dual; ---error ER_SP_DOES_NOT_EXIST drop function func_1; +drop procedure proc_1; prepare abc from "flush user_resources"; execute abc; execute abc; @@ -1865,22 +1875,6 @@ execute abc; deallocate prepare abc; -create procedure proc_1() show scheduler status; -drop procedure proc_1; -delimiter |; ---error ER_SP_NO_RETSET -create function func_1() returns int begin show scheduler status; return 1; end| -delimiter ;| ---error ER_SP_DOES_NOT_EXIST -select func_1(), func_1(), func_1() from dual; ---error ER_SP_DOES_NOT_EXIST -drop function func_1; ---error ER_UNSUPPORTED_PS -prepare abc from "show scheduler status"; ---error ER_UNKNOWN_STMT_HANDLER -deallocate prepare abc; - - --disable_warnings drop procedure if exists a; --enable_warnings @@ -1997,10 +1991,13 @@ call proc_1(); call proc_1(); drop procedure proc_1; create procedure proc_1() install plugin my_plug soname 'some_plugin.so'; +--replace_regex /(Can\'t open shared library).*$/\1/ --error ER_CANT_OPEN_LIBRARY call proc_1(); +--replace_regex /(Can\'t open shared library).*$/\1/ --error ER_CANT_OPEN_LIBRARY call proc_1(); +--replace_regex /(Can\'t open shared library).*$/\1/ --error ER_CANT_OPEN_LIBRARY call proc_1(); drop procedure proc_1; @@ -2150,7 +2147,7 @@ drop event if exists xyz; #drop event xyz; #drop procedure proc_1; delimiter |; ---error ER_SP_NO_RETSET +--error ER_EVENT_RECURSIVITY_FORBIDDEN create function func_1() returns int begin create event xyz on schedule at now() do select 123; return 1; end| delimiter ;| --error ER_SP_DOES_NOT_EXIST From 2f0689e2541e98a65a84c9a03fb4736f88ffa28d Mon Sep 17 00:00:00 2001 From: "joerg@trift2." <> Date: Wed, 11 Oct 2006 15:32:32 +0200 Subject: [PATCH 16/60] Override "read-only" permissions when doing "make distcheck". --- Docs/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Docs/Makefile.am b/Docs/Makefile.am index 40e987bb254..09291132e73 100644 --- a/Docs/Makefile.am +++ b/Docs/Makefile.am @@ -66,7 +66,7 @@ INSTALL-BINARY: mysql.info $(GT) # It is not in BitKeeper, but is downloaded from intranet by Bootstrap. dist-hook: if [ -e $(srcdir)/manual.chm ] ; then \ - cp $(srcdir)/manual.chm $(distdir); \ + cp -f $(srcdir)/manual.chm $(distdir); \ fi # Don't update the files from bitkeeper From 61a5ddbd1964c40bea1cafb422549255e9fdd695 Mon Sep 17 00:00:00 2001 From: "istruewing@chilla.local" <> Date: Wed, 11 Oct 2006 16:36:24 +0200 Subject: [PATCH 17/60] Bug#21627 - Warnings in build of myisampack Added proper casts No test case. myisampack must be tested manually. --- storage/myisam/myisampack.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/storage/myisam/myisampack.c b/storage/myisam/myisampack.c index 556d0f46145..be68ffbdc5a 100644 --- a/storage/myisam/myisampack.c +++ b/storage/myisam/myisampack.c @@ -1965,7 +1965,7 @@ static char *bindigits(ulonglong value, uint bits) DBUG_ASSERT(idx < sizeof(digits)); while (idx) - *(ptr++)= '0' + ((value >> (--idx)) & 1); + *(ptr++)= '0' + ((char) (value >> (--idx)) & (char) 1); *ptr= '\0'; return digits; } @@ -1995,7 +1995,7 @@ static char *hexdigits(ulonglong value) DBUG_ASSERT(idx < sizeof(digits)); while (idx) { - if ((*(ptr++)= '0' + ((value >> (4 * (--idx))) & 0xf)) > '9') + if ((*(ptr++)= '0' + ((char) (value >> (4 * (--idx))) & (char) 0xf)) > '9') *(ptr - 1)+= 'a' - '9' - 1; } *ptr= '\0'; @@ -2284,7 +2284,7 @@ static my_off_t write_huff_tree(HUFF_TREE *huff_tree, uint trees) errors++; break; } - idx+= code & 1; + idx+= (uint) code & 1; if (idx >= length) { VOID(fflush(stdout)); From 62b2044279e3142a8947b9bb44af8b40a963dc1e Mon Sep 17 00:00:00 2001 From: "svoj@mysql.com/april.(none)" <> Date: Wed, 11 Oct 2006 20:34:20 +0500 Subject: [PATCH 18/60] BUG#22562 - REPAIR TABLE .. USE_FRM causes server crash on Windows and server hangs on Linux If REPAIR TABLE ... USE_FRM is issued for table that is located in different than default database server crash could happen. In reopen_name_locked_table take database name from table_list (user specified or default database) instead of from thd (default database). Affects 4.1 only. --- mysql-test/r/repair.result | 7 +++++++ mysql-test/t/repair.test | 10 ++++++++++ sql/sql_base.cc | 2 +- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/repair.result b/mysql-test/r/repair.result index c069824e9f0..815305013be 100644 --- a/mysql-test/r/repair.result +++ b/mysql-test/r/repair.result @@ -48,3 +48,10 @@ Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_par t1 1 a 1 a A 5 NULL NULL YES BTREE SET myisam_repair_threads=@@global.myisam_repair_threads; DROP TABLE t1; +CREATE TABLE t1(a INT); +USE mysql; +REPAIR TABLE test.t1 USE_FRM; +Table Op Msg_type Msg_text +test.t1 repair status OK +USE test; +DROP TABLE t1; diff --git a/mysql-test/t/repair.test b/mysql-test/t/repair.test index f086d5b0c2a..91d03ce3082 100644 --- a/mysql-test/t/repair.test +++ b/mysql-test/t/repair.test @@ -45,4 +45,14 @@ SHOW INDEX FROM t1; SET myisam_repair_threads=@@global.myisam_repair_threads; DROP TABLE t1; +# +# BUG#22562 - REPAIR TABLE .. USE_FRM causes server crash on Windows and +# server hangs on Linux +# +CREATE TABLE t1(a INT); +USE mysql; +REPAIR TABLE test.t1 USE_FRM; +USE test; +DROP TABLE t1; + # End of 4.1 tests diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 0a9529d6067..561d75a765e 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -821,7 +821,7 @@ TABLE *reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) if (!(table = table_list->table)) DBUG_RETURN(0); - char* db = thd->db ? thd->db : table_list->db; + char *db= table_list->db; char* table_name = table_list->real_name; char key[MAX_DBKEY_LENGTH]; uint key_length; From ea8a646dc0fcf2cd36475e932f29348ce04dc120 Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Wed, 11 Oct 2006 19:44:12 +0400 Subject: [PATCH 19/60] Bug#22138: Unhandled NULL caused server crash The Cached_item_decimal::cmp() method wasn't checking for null pointer returned from the val_decimal() of the item being cached. This leads to server crash. The Cached_item_decimal::cmp() method now check for null values. --- mysql-test/r/type_decimal.result | 11 +++++++++++ mysql-test/t/type_decimal.test | 9 +++++++++ sql/item_buff.cc | 12 +++++++++--- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/type_decimal.result b/mysql-test/r/type_decimal.result index 8b2c08065e0..c9c42d18d68 100644 --- a/mysql-test/r/type_decimal.result +++ b/mysql-test/r/type_decimal.result @@ -779,3 +779,14 @@ select f1 from t1 where f1 in (select f1 from t1); f1 40 drop table t1; +create table t1 as +select from_days(s) as date,t +from (select 1 as s,'t' as t union select null, null ) as sub1; +select group_concat(t) from t1 group by week(date)/10; +group_concat(t) +t +Warnings: +Warning 1292 Truncated incorrect datetime value: '0000-00-00' +Warning 1292 Truncated incorrect datetime value: '0000-00-00' +Warning 1292 Truncated incorrect datetime value: '0000-00-00' +drop table t1; diff --git a/mysql-test/t/type_decimal.test b/mysql-test/t/type_decimal.test index 441d750004e..4fdb0c8458f 100644 --- a/mysql-test/t/type_decimal.test +++ b/mysql-test/t/type_decimal.test @@ -385,3 +385,12 @@ insert into t1 values (40); flush tables; select f1 from t1 where f1 in (select f1 from t1); drop table t1; + +# +# Bug#22183: Unhandled NULL caused server crash +# +create table t1 as + select from_days(s) as date,t + from (select 1 as s,'t' as t union select null, null ) as sub1; +select group_concat(t) from t1 group by week(date)/10; +drop table t1; diff --git a/sql/item_buff.cc b/sql/item_buff.cc index 1661f04a4ae..37f9ca7ce6c 100644 --- a/sql/item_buff.cc +++ b/sql/item_buff.cc @@ -132,11 +132,17 @@ bool Cached_item_decimal::cmp() { my_decimal tmp; my_decimal *ptmp= item->val_decimal(&tmp); - if (null_value != item->null_value || my_decimal_cmp(&value, ptmp)) + if (null_value != item->null_value || + (!item->null_value && my_decimal_cmp(&value, ptmp))) { null_value= item->null_value; - my_decimal2decimal(ptmp, &value); - return TRUE; + /* Save only not null values */ + if (!null_value) + { + my_decimal2decimal(ptmp, &value); + return TRUE; + } + return FALSE; } return FALSE; } From f1e021763bc17362293387acff50c0096df7ea4a Mon Sep 17 00:00:00 2001 From: "joerg@trift2." <> Date: Thu, 12 Oct 2006 18:25:27 +0200 Subject: [PATCH 20/60] Various tool fixes, necessary to build 5.1.12-beta on Netware. --- netware/BUILD/compile-AUTOTOOLS | 6 +++++- netware/BUILD/compile-netware-START | 1 - netware/BUILD/mwenv | 2 +- netware/BUILD/nwbootstrap | 17 +++++++++-------- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/netware/BUILD/compile-AUTOTOOLS b/netware/BUILD/compile-AUTOTOOLS index 20e506aa683..c93fb1b1b28 100755 --- a/netware/BUILD/compile-AUTOTOOLS +++ b/netware/BUILD/compile-AUTOTOOLS @@ -5,8 +5,11 @@ # stop on errors set -e +sed -e "s/^DIST_COMMON/#DIST_COMMON/g" storage/ndb/Makefile.am > storage/ndb/Makefile.am.$$ +mv storage/ndb/Makefile.am.$$ storage/ndb/Makefile.am -for package in . ./storage/innobase +# for package in . ./storage/innobase +for package in . do (cd $package rm -rf config.cache autom4te.cache @@ -14,6 +17,7 @@ do autoheader libtoolize --force aclocal +# automake --verbose --add-missing --force-missing automake --add-missing --force-missing autoconf) done diff --git a/netware/BUILD/compile-netware-START b/netware/BUILD/compile-netware-START index 95b222994d3..8f828f34bd1 100755 --- a/netware/BUILD/compile-netware-START +++ b/netware/BUILD/compile-netware-START @@ -24,5 +24,4 @@ base_configs=" \ --prefix=N:/mysql \ --without-mysqlmanager \ --without-man \ - --without-csv-storage-engine \ " diff --git a/netware/BUILD/mwenv b/netware/BUILD/mwenv index d8d53293b2c..f43092492bb 100755 --- a/netware/BUILD/mwenv +++ b/netware/BUILD/mwenv @@ -8,7 +8,7 @@ fi # The base path(in wineformat) where compilers, includes and # libraries are installed -if test -z $MYDEV +if test -z "$MYDEV" then # the default is "F:/mydev" export MYDEV="F:/mydev" diff --git a/netware/BUILD/nwbootstrap b/netware/BUILD/nwbootstrap index 22e1569e7ca..563d316ea56 100755 --- a/netware/BUILD/nwbootstrap +++ b/netware/BUILD/nwbootstrap @@ -151,17 +151,18 @@ echo "making files writable..." cd $target_dir chmod -R u+rw,g+rw . -# edit the mvenv file -echo "updating the mwenv environment file..." +## # edit the mvenv file +## echo "updating the mwenv environment file..." mwenv="./netware/BUILD/mwenv" -mv -f $mwenv $mwenv.org -sed -e "s;WINE_BUILD_DIR;$wine_build_dir;g" \ - -e "s;BUILD_DIR;$build_dir;g" \ - -e "s;VERSION;$version;g" $mwenv.org > $mwenv -chmod +rwx $mwenv +## mv -f $mwenv $mwenv.org +## sed -e "s;WINE_BUILD_DIR;$wine_build_dir;g" \ +## -e "s;BUILD_DIR;$build_dir;g" \ +## -e "s;VERSION;$version;g" $mwenv.org > $mwenv +## chmod +rwx $mwenv PWD=`pwd` -SRC_DIR=`grep "^export MYDEV=" $mwenv | cut -d'=' -f2 | \ +# This line will catch the default value only, let's hope it is correct +SRC_DIR=`grep "^ *export MYDEV=" $mwenv | cut -d'=' -f2 | \ sed -e 's;";;g' -e "s;^;echo ;g" -e "s;$;/\`basename $PWD\`;g" | /bin/sh` From 712c4cdbe3d1da8c7e6f5bd140d7ac8082c453f9 Mon Sep 17 00:00:00 2001 From: "kent@mysql.com/c-584072d5.010-2112-6f72651.cust.bredbandsbolaget.se" <> Date: Thu, 12 Oct 2006 18:26:17 +0200 Subject: [PATCH 21/60] mysql.spec.sh: Corrected path to mysql-test directory Removed man1/mysql_explain_log.1 --- support-files/mysql.spec.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/support-files/mysql.spec.sh b/support-files/mysql.spec.sh index 29b19d86e89..b2069dc309d 100644 --- a/support-files/mysql.spec.sh +++ b/support-files/mysql.spec.sh @@ -327,7 +327,7 @@ then cp -fp mysql-debug-%{mysql_version}/config.log "$MYSQL_DEBUGCONFLOG_DEST" fi -(cd mysql-debug-%{mysql_version} ; \ +(cd mysql-debug-%{mysql_version}/mysql-test ; \ ./mysql-test-run.pl --comment=debug --skip-rpl --skip-ndbcluster --force ; \ true) @@ -357,7 +357,7 @@ then cp -fp mysql-release-%{mysql_version}/config.log "$MYSQL_CONFLOG_DEST" fi -(cd mysql-release-%{mysql_version} ; \ +(cd mysql-release-%{mysql_version}/mysql-test ; \ ./mysql-test-run.pl --comment=normal --force ; \ ./mysql-test-run.pl --comment=ps --ps-protocol --force ; \ ./mysql-test-run.pl --comment=normal+rowrepl --mysqld=--binlog-format=row --force ; \ @@ -522,7 +522,6 @@ fi %doc %attr(644, root, man) %{_mandir}/man1/myisamchk.1* %doc %attr(644, root, man) %{_mandir}/man1/myisamlog.1* %doc %attr(644, root, man) %{_mandir}/man1/myisampack.1* -%doc %attr(644, root, man) %{_mandir}/man1/mysql_explain_log.1* %doc %attr(644, root, man) %{_mandir}/man8/mysqld.8* %doc %attr(644, root, man) %{_mandir}/man1/mysqld_multi.1* %doc %attr(644, root, man) %{_mandir}/man1/mysqld_safe.1* From 8a7413f704c49b60509975bef5204d7c9b55e3d3 Mon Sep 17 00:00:00 2001 From: "kent@mysql.com/c-584072d5.010-2112-6f72651.cust.bredbandsbolaget.se" <> Date: Thu, 12 Oct 2006 19:43:47 +0200 Subject: [PATCH 22/60] thr_alarm.c, thr_lock.c, my_global.h: Avoid multiple define of DBUG_OFF (bug#21749) --- include/my_global.h | 9 +++++++-- mysys/thr_alarm.c | 3 +-- mysys/thr_lock.c | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/include/my_global.h b/include/my_global.h index 39947735f3c..a7ec41068b3 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -614,12 +614,17 @@ C_MODE_END #define _STATIC_VARARGS(X) X #define _PC(X) X +/* The DBUG_ON flag always takes precedence over default DBUG_OFF */ #if defined(DBUG_ON) && defined(DBUG_OFF) #undef DBUG_OFF #endif -#if defined(_lint) && !defined(DBUG_OFF) -#define DBUG_OFF +/* We might be forced to turn debug off, if not turned off already */ +#if (defined(FORCE_DBUG_OFF) || defined(_lint)) && !defined(DBUG_OFF) +# define DBUG_OFF +# ifdef DBUG_ON +# undef DBUG_ON +# endif #endif #include diff --git a/mysys/thr_alarm.c b/mysys/thr_alarm.c index dcb41837d96..c55cc32b47d 100644 --- a/mysys/thr_alarm.c +++ b/mysys/thr_alarm.c @@ -15,8 +15,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* To avoid problems with alarms in debug code, we disable DBUG here */ -#undef DBUG_OFF -#define DBUG_OFF +#define FORCE_DBUG_OFF #include #if defined(THREAD) && !defined(DONT_USE_THR_ALARM) diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index 36cb83ae754..a2c42ee326c 100644 --- a/mysys/thr_lock.c +++ b/mysys/thr_lock.c @@ -72,7 +72,7 @@ multiple read locks. */ #if !defined(MAIN) && !defined(DBUG_OFF) && !defined(EXTRA_DEBUG) -#define DBUG_OFF +#define FORCE_DBUG_OFF #endif #include "mysys_priv.h" From 72bec39c850dd73f79c7822fcf25b35c12825403 Mon Sep 17 00:00:00 2001 From: "joerg@trift2." <> Date: Thu, 12 Oct 2006 22:42:43 +0200 Subject: [PATCH 23/60] unittest/mytap/tap.c : Do not (try to) set a signal handler action unless a signal is defined on the platform. --- unittest/mytap/tap.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/unittest/mytap/tap.c b/unittest/mytap/tap.c index 17ec51863d9..54292f3b828 100644 --- a/unittest/mytap/tap.c +++ b/unittest/mytap/tap.c @@ -140,11 +140,19 @@ static signal_entry install_signal[]= { { SIGABRT, handle_core_signal }, { SIGFPE, handle_core_signal }, { SIGSEGV, handle_core_signal }, - { SIGBUS, handle_core_signal }, - { SIGXCPU, handle_core_signal }, - { SIGXFSZ, handle_core_signal }, - { SIGSYS, handle_core_signal }, - { SIGTRAP, handle_core_signal } + { SIGBUS, handle_core_signal } +#ifdef SIGXCPU + , { SIGXCPU, handle_core_signal } +#endif +#ifdef SIGXCPU + , { SIGXFSZ, handle_core_signal } +#endif +#ifdef SIGXCPU + , { SIGSYS, handle_core_signal } +#endif +#ifdef SIGXCPU + , { SIGTRAP, handle_core_signal } +#endif }; void From f0bb52ab13dc0f2d9f1a7e674f0ca19f8b3b5089 Mon Sep 17 00:00:00 2001 From: "kent@mysql.com/c-584072d5.010-2112-6f72651.cust.bredbandsbolaget.se" <> Date: Fri, 13 Oct 2006 03:32:38 +0200 Subject: [PATCH 24/60] log_event.cc, sql_class.cc: VisualStudio needs cast from (byte*) to (const char*), 3rd arg to _db_dump_() --- sql/log_event.cc | 4 ++-- sql/sql_class.cc | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sql/log_event.cc b/sql/log_event.cc index 4a6346bf57c..d99fb9da8f8 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -7129,8 +7129,8 @@ int Update_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO *rli, row_start, &m_cols, row_end, &m_master_reclength, table->write_set, UPDATE_ROWS_EVENT); - DBUG_DUMP("record[0]", table->record[0], table->s->reclength); - DBUG_DUMP("m_after_image", m_after_image, table->s->reclength); + DBUG_DUMP("record[0]", (const char *)table->record[0], table->s->reclength); + DBUG_DUMP("m_after_image", (const char *)m_after_image, table->s->reclength); /* diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 3fd0e621422..5da8d27a887 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -2632,10 +2632,10 @@ int THD::binlog_update_row(TABLE* table, bool is_trans, my_size_t const after_size= pack_row(table, cols, after_row, after_record); - DBUG_DUMP("before_record", before_record, table->s->reclength); - DBUG_DUMP("after_record", after_record, table->s->reclength); - DBUG_DUMP("before_row", before_row, before_size); - DBUG_DUMP("after_row", after_row, after_size); + DBUG_DUMP("before_record", (const char *)before_record, table->s->reclength); + DBUG_DUMP("after_record", (const char *)after_record, table->s->reclength); + DBUG_DUMP("before_row", (const char *)before_row, before_size); + DBUG_DUMP("after_row", (const char *)after_row, after_size); Rows_log_event* const ev= binlog_prepare_pending_rows_event(table, server_id, cols, colcnt, From d0bd8a8a4194dfd5896cebcbc9ee49f936a8f8fe Mon Sep 17 00:00:00 2001 From: "jonas@perch.ndb.mysql.com" <> Date: Fri, 13 Oct 2006 10:11:18 +0200 Subject: [PATCH 25/60] ndb - fix memleak (due to incorrect merge of bug#21941) recommit into release-clone --- storage/ndb/src/ndbapi/NdbScanOperation.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/ndb/src/ndbapi/NdbScanOperation.cpp b/storage/ndb/src/ndbapi/NdbScanOperation.cpp index 4c39d4ee342..2d47f79ee09 100644 --- a/storage/ndb/src/ndbapi/NdbScanOperation.cpp +++ b/storage/ndb/src/ndbapi/NdbScanOperation.cpp @@ -701,7 +701,7 @@ void NdbScanOperation::close(bool forceSend, bool releaseOp) theNdbCon = NULL; m_transConnection = NULL; - if (tTransCon) + if (tTransCon && releaseOp) { NdbIndexScanOperation* tOp = (NdbIndexScanOperation*)this; @@ -716,7 +716,7 @@ void NdbScanOperation::close(bool forceSend, bool releaseOp) &tTransCon->m_theLastScanOperation, tOp); } - else if (releaseOp) + else { ret = tTransCon->releaseScanOperation(&tTransCon->m_firstExecutedScanOp, 0, tOp); From 76bc3e2eb57f2938c2a75142231d153c1fb0bc04 Mon Sep 17 00:00:00 2001 From: "holyfoot/hf@mysql.com/deer.(none)" <> Date: Fri, 13 Oct 2006 19:36:19 +0500 Subject: [PATCH 26/60] merging --- mysql-test/r/type_enum.result | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/type_enum.result b/mysql-test/r/type_enum.result index da913df4dd3..b7db1d95587 100644 --- a/mysql-test/r/type_enum.result +++ b/mysql-test/r/type_enum.result @@ -1750,7 +1750,7 @@ alter table t1 add f2 enum(0xFFFF); show create table t1; Table Create Table t1 CREATE TABLE `t1` ( - `f1` int(11) default NULL, - `f2` enum('') default NULL + `f1` int(11) DEFAULT NULL, + `f2` enum('') DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; From 2b474898dc133885fb883847cee6a5370cf3c12e Mon Sep 17 00:00:00 2001 From: "evgen@moonbone.local" <> Date: Fri, 13 Oct 2006 21:59:52 +0400 Subject: [PATCH 27/60] Bug#14959: ALTER TABLE isn't able to rename a view The mysql_alter_table() was able to rename only a table. The view/table renaming code is moved from the function rename_tables to the new function called do_rename(). The mysql_alter_table() function calls it when it needs to rename a view. --- mysql-test/r/rename.result | 10 ++ mysql-test/t/rename.test | 13 +++ sql/mysql_priv.h | 3 + sql/sql_rename.cc | 202 +++++++++++++++++++++++-------------- sql/sql_table.cc | 48 ++++++++- 5 files changed, 200 insertions(+), 76 deletions(-) diff --git a/mysql-test/r/rename.result b/mysql-test/r/rename.result index 76c0f4422fe..345270231ef 100644 --- a/mysql-test/r/rename.result +++ b/mysql-test/r/rename.result @@ -54,3 +54,13 @@ Tables_in_test t2 t4 drop table t2, t4; +create table t1(f1 int); +create view v1 as select * from t1; +alter table v1 rename to v2; +alter table v1 rename to v2; +ERROR 42S02: Table 'test.v1' doesn't exist +rename table v2 to v1; +rename table v2 to v1; +ERROR 42S01: Table 'v1' already exists +drop view v1; +drop table t1; diff --git a/mysql-test/t/rename.test b/mysql-test/t/rename.test index 86e4b6eed0a..054b1bd3403 100644 --- a/mysql-test/t/rename.test +++ b/mysql-test/t/rename.test @@ -72,4 +72,17 @@ disconnect con2; disconnect con1; connection default; +# +# Bug#14959: ALTER TABLE isn't able to rename a view +# +create table t1(f1 int); +create view v1 as select * from t1; +alter table v1 rename to v2; +--error 1146 +alter table v1 rename to v2; +rename table v2 to v1; +--error 1050 +rename table v2 to v1; +drop view v1; +drop table t1; # End of 4.1 tests diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index ff04022b110..1cee013852e 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -648,6 +648,9 @@ int quick_rm_table(enum db_type base,const char *db, const char *table_name); void close_cached_table(THD *thd, TABLE *table); bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list); +bool do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, + char *new_table_name, char *new_table_alias, + bool skip_error); bool mysql_change_db(THD *thd,const char *name,bool no_access_check); void mysql_parse(THD *thd,char *inBuf,uint length); bool mysql_test_parse_for_slave(THD *thd,char *inBuf,uint length); diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index 74951029de9..1a4f1d82cb5 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -125,95 +125,147 @@ static TABLE_LIST *reverse_table_list(TABLE_LIST *table_list) } +/* + Rename a single table or a view + + SYNPOSIS + do_rename() + thd Thread handle + ren_table A table/view to be renamed + new_db The database to which the table to be moved to + new_table_name The new table/view name + new_table_alias The new table/view alias + skip_error Whether to skip error + + DESCRIPTION + Rename a single table or a view. + + RETURN + false Ok + true rename failed +*/ + +bool +do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name, + char *new_table_alias, bool skip_error) +{ + int rc= 1; + char name[FN_REFLEN]; + const char *new_alias, *old_alias; + frm_type_enum frm_type; + db_type table_type; + + DBUG_ENTER("do_rename"); + + if (lower_case_table_names == 2) + { + old_alias= ren_table->alias; + new_alias= new_table_alias; + } + else + { + old_alias= ren_table->table_name; + new_alias= new_table_name; + } + sprintf(name,"%s/%s/%s%s",mysql_data_home, + new_db, new_alias, reg_ext); + unpack_filename(name, name); + if (!access(name,F_OK)) + { + my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias); + DBUG_RETURN(ren_table); // This can't be skipped + } + sprintf(name,"%s/%s/%s%s",mysql_data_home, + ren_table->db, old_alias, + reg_ext); + unpack_filename(name, name); + + frm_type= mysql_frm_type(thd, name, &table_type); + switch (frm_type) + { + case FRMTYPE_TABLE: + { + if (table_type == DB_TYPE_UNKNOWN) + my_error(ER_FILE_NOT_FOUND, MYF(0), name, my_errno); + else + { + if (!(rc= mysql_rename_table(table_type, ren_table->db, old_alias, + new_db, new_alias))) + { + if ((rc= Table_triggers_list::change_table_name(thd, ren_table->db, + old_alias, + new_db, + new_alias))) + { + /* + We've succeeded in renaming table's .frm and in updating + corresponding handler data, but have failed to update table's + triggers appropriately. So let us revert operations on .frm + and handler's data and report about failure to rename table. + */ + (void) mysql_rename_table(table_type, new_db, new_alias, + ren_table->db, old_alias); + } + } + } + break; + } + case FRMTYPE_VIEW: + /* change of schema is not allowed */ + if (strcmp(ren_table->db, new_db)) + my_error(ER_FORBID_SCHEMA_CHANGE, MYF(0), ren_table->db, + new_db); + else + rc= mysql_rename_view(thd, new_alias, ren_table); + break; + default: + DBUG_ASSERT(0); // should never happen + case FRMTYPE_ERROR: + my_error(ER_FILE_NOT_FOUND, MYF(0), name, my_errno); + break; + } + if (rc && !skip_error) + DBUG_RETURN(1); + + DBUG_RETURN(0); + +} /* Rename all tables in list; Return pointer to wrong entry if something goes wrong. Note that the table_list may be empty! */ +/* + Rename tables/views in the list + + SYNPOSIS + rename_tables() + thd Thread handle + table_list List of tables to rename + skip_error Whether to skip errors + + DESCRIPTION + Take a table/view name from and odd list element and rename it to a + the name taken from list element+1. Note that the table_list may be + empty. + + RETURN + false Ok + true rename failed +*/ + static TABLE_LIST * rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error) { - TABLE_LIST *ren_table,*new_table; - frm_type_enum frm_type; - db_type table_type; + TABLE_LIST *ren_table,*new_table, *tmp_table; DBUG_ENTER("rename_tables"); for (ren_table= table_list; ren_table; ren_table= new_table->next_local) { - int rc= 1; - char name[FN_REFLEN]; - const char *new_alias, *old_alias; - new_table= ren_table->next_local; - if (lower_case_table_names == 2) - { - old_alias= ren_table->alias; - new_alias= new_table->alias; - } - else - { - old_alias= ren_table->table_name; - new_alias= new_table->table_name; - } - sprintf(name,"%s/%s/%s%s",mysql_data_home, - new_table->db, new_alias, reg_ext); - unpack_filename(name, name); - if (!access(name,F_OK)) - { - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias); - DBUG_RETURN(ren_table); // This can't be skipped - } - sprintf(name,"%s/%s/%s%s",mysql_data_home, - ren_table->db, old_alias, - reg_ext); - unpack_filename(name, name); - - frm_type= mysql_frm_type(thd, name, &table_type); - switch (frm_type) - { - case FRMTYPE_TABLE: - { - if (table_type == DB_TYPE_UNKNOWN) - my_error(ER_FILE_NOT_FOUND, MYF(0), name, my_errno); - else - { - if (!(rc= mysql_rename_table(table_type, ren_table->db, old_alias, - new_table->db, new_alias))) - { - if ((rc= Table_triggers_list::change_table_name(thd, ren_table->db, - old_alias, - new_table->db, - new_alias))) - { - /* - We've succeeded in renaming table's .frm and in updating - corresponding handler data, but have failed to update table's - triggers appropriately. So let us revert operations on .frm - and handler's data and report about failure to rename table. - */ - (void) mysql_rename_table(table_type, new_table->db, new_alias, - ren_table->db, old_alias); - } - } - } - break; - } - case FRMTYPE_VIEW: - /* change of schema is not allowed */ - if (strcmp(ren_table->db, new_table->db)) - my_error(ER_FORBID_SCHEMA_CHANGE, MYF(0), ren_table->db, - new_table->db); - else - rc= mysql_rename_view(thd, new_alias, ren_table); - break; - default: - DBUG_ASSERT(0); // should never happen - case FRMTYPE_ERROR: - my_error(ER_FILE_NOT_FOUND, MYF(0), name, my_errno); - break; - } - if (rc && !skip_error) + if (do_rename(thd, ren_table, new_table->db, new_table->table_name, + new_table->alias, skip_error)) DBUG_RETURN(ren_table); } DBUG_RETURN(0); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index a340c5ffc98..9ce5084c34f 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -3154,9 +3154,10 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, ha_rows copied,deleted; ulonglong next_insert_id; uint db_create_options, used_fields; - enum db_type old_db_type,new_db_type; + enum db_type old_db_type, new_db_type, table_type; bool need_copy_table; bool no_table_reopen= FALSE, varchar= FALSE; + frm_type_enum frm_type; DBUG_ENTER("mysql_alter_table"); thd->proc_info="init"; @@ -3174,6 +3175,51 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, if (alter_info->tablespace_op != NO_TABLESPACE_OP) DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list, alter_info->tablespace_op)); + sprintf(new_name_buff,"%s/%s/%s%s",mysql_data_home, db, table_name, reg_ext); + unpack_filename(new_name_buff, new_name_buff); + if (lower_case_table_names != 2) + my_casedn_str(files_charset_info, new_name_buff); + frm_type= mysql_frm_type(thd, new_name_buff, &table_type); + /* Rename a view */ + if (frm_type == FRMTYPE_VIEW && !(alter_info->flags & ~ALTER_RENAME)) + { + /* + Avoid problems with a rename on a table that we have locked or + if the user is trying to to do this in a transcation context + */ + + if (thd->locked_tables || thd->active_transaction()) + { + my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, + ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); + DBUG_RETURN(1); + } + + if (wait_if_global_read_lock(thd,0,1)) + DBUG_RETURN(1); + VOID(pthread_mutex_lock(&LOCK_open)); + if (lock_table_names(thd, table_list)) + goto view_err; + + error=0; + if (!do_rename(thd, table_list, new_db, new_name, new_name, 1)) + { + if (mysql_bin_log.is_open()) + { + thd->clear_error(); + Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); + mysql_bin_log.write(&qinfo); + } + send_ok(thd); + } + + unlock_table_names(thd, table_list, (TABLE_LIST*) 0); + +view_err: + pthread_mutex_unlock(&LOCK_open); + start_waiting_global_read_lock(thd); + DBUG_RETURN(error); + } if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ))) DBUG_RETURN(TRUE); From 0b7132eaf1033d96d492312d2163516922686db1 Mon Sep 17 00:00:00 2001 From: "kent@mysql.com/c-584072d5.010-2112-6f72651.cust.bredbandsbolaget.se" <> Date: Fri, 13 Oct 2006 21:48:27 +0200 Subject: [PATCH 28/60] CMakeLists.txt: Added mysql_upgrade target to Windows CMake build --- client/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 26cc36c7f6f..b2b734a48f4 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -65,6 +65,9 @@ TARGET_LINK_LIBRARIES(mysqldump mysqlclient mysys dbug yassl taocrypt zlib wsock ADD_EXECUTABLE(mysqlimport mysqlimport.c) TARGET_LINK_LIBRARIES(mysqlimport mysqlclient mysys dbug yassl taocrypt zlib wsock32) +ADD_EXECUTABLE(mysql_upgrade mysql_upgrade.c) +TARGET_LINK_LIBRARIES(mysql_upgrade mysqlclient mysys dbug yassl taocrypt zlib wsock32) + ADD_EXECUTABLE(mysqlshow mysqlshow.c) TARGET_LINK_LIBRARIES(mysqlshow mysqlclient mysys dbug yassl taocrypt zlib wsock32) From dcfefed95ad9c33b8b712e48071fa2c51c9ec82a Mon Sep 17 00:00:00 2001 From: "kent@mysql.com/c-4e4072d5.010-2112-6f72651.cust.bredbandsbolaget.se" <> Date: Sat, 14 Oct 2006 20:03:49 +0200 Subject: [PATCH 29/60] Makefile.am: Configure flags makes "lex_hash.h" differ, don't distribute pregenerated file, build it after configure (bug#18888). --- sql/Makefile.am | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sql/Makefile.am b/sql/Makefile.am index 38a99aaef88..97e00471a6e 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -27,7 +27,7 @@ INCLUDES = @ZLIB_INCLUDES@ \ WRAPLIBS= @WRAPLIBS@ SUBDIRS = share libexec_PROGRAMS = mysqld -EXTRA_PROGRAMS = gen_lex_hash +noinst_PROGRAMS = gen_lex_hash bin_PROGRAMS = mysql_tzinfo_to_sql gen_lex_hash_LDFLAGS = @NOINST_LDFLAGS@ LDADD = $(top_builddir)/vio/libvio.a \ @@ -120,8 +120,9 @@ DEFS = -DMYSQL_SERVER \ -DLIBDIR="\"$(MYSQLLIBdir)\"" \ @DEFS@ -BUILT_SOURCES = sql_yacc.cc sql_yacc.h lex_hash.h -EXTRA_DIST = udf_example.c udf_example.def $(BUILT_SOURCES) \ +BUILT_DIST_SRC = sql_yacc.cc sql_yacc.h +BUILT_SOURCES = $(BUILT_DIST_SRC) lex_hash.h +EXTRA_DIST = udf_example.c udf_example.def $(BUILT_DIST_SRC) \ nt_servc.cc nt_servc.h message.mc CMakeLists.txt \ udf_example.c udf_example.def CLEANFILES = lex_hash.h sql_yacc.cc sql_yacc.h sql_yacc.output @@ -157,11 +158,10 @@ sql_yacc.o: sql_yacc.cc sql_yacc.h $(HEADERS) @echo "If it fails, re-run configure with --with-low-memory" $(CXXCOMPILE) $(LM_CFLAGS) -c $< -# This generates lex_hash.h -# NOTE Built sources should depend on their sources not the tool -# this avoid the rebuild of the built files in a source dist -lex_hash.h: gen_lex_hash.cc lex.h - $(MAKE) $(AM_MAKEFLAGS) gen_lex_hash$(EXEEXT) +# FIXME seems like now "lex_hash.h" differs depending on configure +# flags, so can't pregenerate and include in source TAR. Revert to +# dist pregenerated if this changes, so the file doesn't differ. +lex_hash.h: gen_lex_hash$(EXEEXT) ./gen_lex_hash$(EXEEXT) > $@ # the following three should eventually be moved out of this directory From 8692bb53a33bf29415d1d01681bafef4ddb00e5e Mon Sep 17 00:00:00 2001 From: "kent@mysql.com/c-794072d5.010-2112-6f72651.cust.bredbandsbolaget.se" <> Date: Mon, 16 Oct 2006 02:07:50 +0200 Subject: [PATCH 30/60] make_binary_distribution.sh: Split copy of result files to avoid shell limit. Added copy of mysql-test/std_data/ndb_backup5{0,1}. configure.in: Look for dlopen() even if --with-mysqld-ldflags constains "-static", as this is not the same as the flag to "ld", it just informs "libtool" to link static with libraries created part of the build, even if there exists shared versions. --- configure.in | 4 ++-- scripts/make_binary_distribution.sh | 13 +++++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/configure.in b/configure.in index fbd12e2707e..78bf470f8fb 100644 --- a/configure.in +++ b/configure.in @@ -1441,9 +1441,9 @@ fi # dlopen, dlerror case "$with_mysqld_ldflags " in - *"-static "*) + *"-all-static "*) # No need to check for dlopen when mysqld is linked with - # -all-static or -static as it won't be able to load any functions. + # -all-static as it won't be able to load any functions. # NOTE! It would be better if it was possible to test if dlopen # can be used, but a good way to test it couldn't be found diff --git a/scripts/make_binary_distribution.sh b/scripts/make_binary_distribution.sh index c254a77e805..81e7f22c1b8 100644 --- a/scripts/make_binary_distribution.sh +++ b/scripts/make_binary_distribution.sh @@ -93,6 +93,7 @@ mkdir $BASE $BASE/bin $BASE/docs \ $BASE/include $BASE/lib $BASE/support-files $BASE/share $BASE/scripts \ $BASE/mysql-test $BASE/mysql-test/t $BASE/mysql-test/r \ $BASE/mysql-test/include $BASE/mysql-test/std_data $BASE/mysql-test/lib \ + $BASE/mysql-test/std_data/ndb_backup50 $BASE/mysql-test/std_data/ndb_backup51 \ $BASE/mysql-test/extra \ $BASE/mysql-test/extra/binlog_tests $BASE/mysql-test/extra/rpl_tests @@ -249,12 +250,20 @@ $CP mysql-test/std_data/*.dat mysql-test/std_data/*.frm \ mysql-test/std_data/des_key_file mysql-test/std_data/*.*001 \ mysql-test/std_data/*.cnf \ $BASE/mysql-test/std_data +$CP mysql-test/std_data/ndb_backup50/*.Data \ + mysql-test/std_data/ndb_backup50/*.ctl \ + mysql-test/std_data/ndb_backup50/*.log \ + $BASE/mysql-test/std_data/ndb_backup50 +$CP mysql-test/std_data/ndb_backup51/*.Data \ + mysql-test/std_data/ndb_backup51/*.ctl \ + mysql-test/std_data/ndb_backup51/*.log \ + $BASE/mysql-test/std_data/ndb_backup51 $CP mysql-test/t/*.test $BASE/mysql-test/t $CP mysql-test/t/*.imtest mysql-test/t/*.disabled $BASE/mysql-test/t $CP mysql-test/t/*.opt mysql-test/t/*.slave-mi $BASE/mysql-test/t $CP mysql-test/t/*.sh mysql-test/t/*.sql $BASE/mysql-test/t -$CP mysql-test/r/*.result mysql-test/r/*.require \ - $BASE/mysql-test/r +$CP mysql-test/r/*.result $BASE/mysql-test/r +$CP mysql-test/r/*.require $BASE/mysql-test/r $CP mysql-test/extra/binlog_tests/*.test $BASE/mysql-test/extra/binlog_tests $CP mysql-test/extra/rpl_tests/*.test $BASE/mysql-test/extra/rpl_tests From 115616381dd2238d4889271357d7cae228ae09fa Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@macbook.gmz" <> Date: Mon, 16 Oct 2006 13:10:25 +0300 Subject: [PATCH 31/60] BUG#14019 : group by converts literal string to column name When resolving unqualified name references MySQL was not checking what is the item type for the reference. Thus e.g a string literal item that has by convention a name equal to its string value will also work as a reference to a SELECT list item or a table field. Fixed by allowing only Item_ref or Item_field to referenced by (unqualified) name. --- mysql-test/r/func_gconcat.result | 5 ---- mysql-test/r/group_by.result | 45 ++++++++++++++++++++++++++++++++ mysql-test/t/func_gconcat.test | 1 - mysql-test/t/group_by.test | 23 ++++++++++++++++ sql/sql_base.cc | 11 ++++++-- 5 files changed, 77 insertions(+), 8 deletions(-) diff --git a/mysql-test/r/func_gconcat.result b/mysql-test/r/func_gconcat.result index db0125b7d4f..d4a46bfd79f 100644 --- a/mysql-test/r/func_gconcat.result +++ b/mysql-test/r/func_gconcat.result @@ -74,11 +74,6 @@ grp group_concat(c order by 1) 1 a 2 b,c 3 C,D,d,d,D,E -select grp,group_concat(c order by "c") from t1 group by grp; -grp group_concat(c order by "c") -1 a -2 b,c -3 C,D,d,d,D,E select grp,group_concat(distinct c order by c) from t1 group by grp; grp group_concat(distinct c order by c) 1 a diff --git a/mysql-test/r/group_by.result b/mysql-test/r/group_by.result index 4ad28091164..61b73dc7005 100644 --- a/mysql-test/r/group_by.result +++ b/mysql-test/r/group_by.result @@ -773,3 +773,48 @@ select sql_buffer_result max(f1)+1 from t1; max(f1)+1 3 drop table t1; +CREATE TABLE t1(a INT); +INSERT INTO t1 VALUES (1),(2); +SELECT a FROM t1 GROUP BY 'a'; +a +1 +SELECT a FROM t1 GROUP BY "a"; +a +1 +SELECT a FROM t1 GROUP BY `a`; +a +1 +2 +set sql_mode=ANSI_QUOTES; +SELECT a FROM t1 GROUP BY "a"; +a +1 +2 +SELECT a FROM t1 GROUP BY 'a'; +a +1 +SELECT a FROM t1 GROUP BY `a`; +a +1 +2 +set sql_mode=''; +SELECT a FROM t1 HAVING 'a' > 1; +a +SELECT a FROM t1 HAVING "a" > 1; +a +SELECT a FROM t1 HAVING `a` > 1; +a +2 +SELECT a FROM t1 ORDER BY 'a' DESC; +a +1 +2 +SELECT a FROM t1 ORDER BY "a" DESC; +a +1 +2 +SELECT a FROM t1 ORDER BY `a` DESC; +a +2 +1 +DROP TABLE t1; diff --git a/mysql-test/t/func_gconcat.test b/mysql-test/t/func_gconcat.test index 8f50690dd8b..d51d88d50ef 100644 --- a/mysql-test/t/func_gconcat.test +++ b/mysql-test/t/func_gconcat.test @@ -32,7 +32,6 @@ select grp,group_concat(d order by a desc) from t1 group by grp; select grp,group_concat(a order by a,d+c-ascii(c)-a) from t1 group by grp; select grp,group_concat(a order by d+c-ascii(c),a) from t1 group by grp; select grp,group_concat(c order by 1) from t1 group by grp; -select grp,group_concat(c order by "c") from t1 group by grp; select grp,group_concat(distinct c order by c) from t1 group by grp; select grp,group_concat(distinct c order by c desc) from t1 group by grp; explain extended select grp,group_concat(distinct c order by c desc) from t1 group by grp; diff --git a/mysql-test/t/group_by.test b/mysql-test/t/group_by.test index f14fab2d30e..064d46aa0c0 100644 --- a/mysql-test/t/group_by.test +++ b/mysql-test/t/group_by.test @@ -610,4 +610,27 @@ select sql_buffer_result max(f1) is null from t1; select sql_buffer_result max(f1)+1 from t1; drop table t1; +# +# BUG#14019-4.1-opt +# +CREATE TABLE t1(a INT); INSERT INTO t1 VALUES (1),(2); + +SELECT a FROM t1 GROUP BY 'a'; +SELECT a FROM t1 GROUP BY "a"; +SELECT a FROM t1 GROUP BY `a`; + +set sql_mode=ANSI_QUOTES; +SELECT a FROM t1 GROUP BY "a"; +SELECT a FROM t1 GROUP BY 'a'; +SELECT a FROM t1 GROUP BY `a`; +set sql_mode=''; + +SELECT a FROM t1 HAVING 'a' > 1; +SELECT a FROM t1 HAVING "a" > 1; +SELECT a FROM t1 HAVING `a` > 1; + +SELECT a FROM t1 ORDER BY 'a' DESC; +SELECT a FROM t1 ORDER BY "a" DESC; +SELECT a FROM t1 ORDER BY `a` DESC; +DROP TABLE t1; # End of 4.1 tests diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 0a9529d6067..40adf5e1f15 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2284,12 +2284,19 @@ find_item_in_list(Item *find, List &items, uint *counter, const char *field_name=0; const char *table_name=0; bool found_unaliased_non_uniq= 0; + /* + true if the item that we search for is a valid name reference + (and not an item that happens to have a name). + */ + bool is_ref_by_name= 0; uint unaliased_counter; LINT_INIT(unaliased_counter); *unaliased= FALSE; - if (find->type() == Item::FIELD_ITEM || find->type() == Item::REF_ITEM) + is_ref_by_name= (find->type() == Item::FIELD_ITEM || + find->type() == Item::REF_ITEM); + if (is_ref_by_name) { field_name= ((Item_ident*) find)->field_name; table_name= ((Item_ident*) find)->table_name; @@ -2401,7 +2408,7 @@ find_item_in_list(Item *find, List &items, uint *counter, } } else if (!table_name && (item->eq(find,0) || - find->name && item->name && + is_ref_by_name && find->name && item->name && !my_strcasecmp(system_charset_info, item->name,find->name))) { From 6b10eebe2c7d79dbf6df173b422cdbf3779bd123 Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@macbook.gmz" <> Date: Mon, 16 Oct 2006 16:27:06 +0300 Subject: [PATCH 32/60] Merge update of the fix for bug 14019 in 5.0 --- mysql-test/r/group_by.result | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mysql-test/r/group_by.result b/mysql-test/r/group_by.result index 679106bd71b..7d1e8832069 100644 --- a/mysql-test/r/group_by.result +++ b/mysql-test/r/group_by.result @@ -802,8 +802,12 @@ a set sql_mode=''; SELECT a FROM t1 HAVING 'a' > 1; a +Warnings: +Warning 1292 Truncated incorrect DOUBLE value: 'a' SELECT a FROM t1 HAVING "a" > 1; a +Warnings: +Warning 1292 Truncated incorrect DOUBLE value: 'a' SELECT a FROM t1 HAVING `a` > 1; a 2 From a1310d84beaf367f6e085e7a1d9ad3aee4046836 Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@macbook.gmz" <> Date: Mon, 16 Oct 2006 18:09:58 +0300 Subject: [PATCH 33/60] Bug #22367: Optimizer uses ref join type instead of eq_ref for simple join on strings MySQL is setting the flag HA_END_SPACE_KEYS for all the keys that reference text or varchar columns with collation different than binary. This was done to handle correctly the situation where a lookup on such a key may return more than 1 row because of the presence of many rows that differ only by the amount of trailing space in the table's string column. Inserting such values however appears to violate the unique checks on INSERT/UPDATE. Thus that flag must not be set as it will prevent the optimizer from choosing a faster access method. This fix removes the setting of the HA_END_SPACE_KEYS flag. --- include/my_base.h | 7 ++++++- mysql-test/r/func_str.result | 4 ++-- mysql-test/r/merge.result | 2 +- mysql-test/r/select.result | 10 ++++++++++ mysql-test/r/subselect.result | 10 +++++----- mysql-test/t/select.test | 14 ++++++++++++++ 6 files changed, 38 insertions(+), 9 deletions(-) diff --git a/include/my_base.h b/include/my_base.h index d8a0e15ccbe..985a27a0c54 100644 --- a/include/my_base.h +++ b/include/my_base.h @@ -224,12 +224,17 @@ enum ha_base_keytype { /* poor old NISAM has 8-bit flags :-( */ #define HA_SORT_ALLOWS_SAME 128 /* Intern bit when sorting records */ #endif +#if MYSQL_VERSION_ID < 0x50200 /* Key has a part that can have end space. If this is an unique key we have to handle it differently from other unique keys as we can find many matching rows for one key (because end space are not compared) */ -#define HA_END_SPACE_KEY 4096 +#define HA_END_SPACE_KEY 0 /* was: 4096 */ +#else +#error HA_END_SPACE_KEY is obsolete, please remove it +#endif + /* These flags can be added to key-seg-flag */ diff --git a/mysql-test/r/func_str.result b/mysql-test/r/func_str.result index 00642e1a570..c2c12f8d291 100644 --- a/mysql-test/r/func_str.result +++ b/mysql-test/r/func_str.result @@ -1143,9 +1143,9 @@ EXPLAIN EXTENDED SELECT * FROM t1 INNER JOIN t2 ON code=id WHERE id='a12' AND (LENGTH(code)=5 OR code < 'a00'); id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 const PRIMARY PRIMARY 12 const 1 Using index 1 SIMPLE t1 ref code code 13 const 3 Using where; Using index -1 SIMPLE t2 ref PRIMARY PRIMARY 12 const 1 Using where; Using index Warnings: -Note 1003 select `test`.`t1`.`code` AS `code`,`test`.`t2`.`id` AS `id` from `test`.`t1` join `test`.`t2` where ((`test`.`t1`.`code` = _latin1'a12') and (`test`.`t2`.`id` = _latin1'a12') and (length(`test`.`t1`.`code`) = 5)) +Note 1003 select `test`.`t1`.`code` AS `code`,`test`.`t2`.`id` AS `id` from `test`.`t1` join `test`.`t2` where ((`test`.`t1`.`code` = _latin1'a12') and (length(`test`.`t1`.`code`) = 5)) DROP TABLE t1,t2; End of 5.0 tests diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index b8fdd24be74..5196462e97e 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -626,7 +626,7 @@ id select_type table type possible_keys key key_len ref rows Extra EXPLAIN SELECT * FROM t2 WHERE fileset_id = 2 AND file_code = '0000000115' LIMIT 1; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t2 ref PRIMARY,files PRIMARY 35 const,const 1 Using where +1 SIMPLE t2 const PRIMARY,files PRIMARY 35 const,const 1 DROP TABLE t2, t1; create table t1 (x int, y int, index xy(x, y)); create table t2 (x int, y int, index xy(x, y)); diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index 0c62d3f570f..5f059260b95 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -3517,3 +3517,13 @@ id a b c d e 2 NULL NULL NULL 2 40 2 NULL NULL NULL 2 50 DROP TABLE t1,t2,t3; +CREATE TABLE t1 (a int, b varchar(20) NOT NULL, PRIMARY KEY(a)); +CREATE TABLE t2 (a int, b varchar(20) NOT NULL, +PRIMARY KEY (a), UNIQUE KEY (b)); +INSERT INTO t1 VALUES (1,'a'),(2,'b'),(3,'c'); +INSERT INTO t2 VALUES (1,'a'),(2,'b'),(3,'c'); +EXPLAIN SELECT t1.a FROM t1 LEFT JOIN t2 ON t2.b=t1.b WHERE t1.a=3; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1 +1 SIMPLE t2 const b b 22 const 1 Using index +DROP TABLE t1,t2; diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 6bf85979002..abe840e96d9 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -363,12 +363,12 @@ INSERT INTO t8 (pseudo,email) VALUES ('joce1','test1'); INSERT INTO t8 (pseudo,email) VALUES ('2joce1','2test1'); EXPLAIN EXTENDED SELECT pseudo,(SELECT email FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce')) FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce'); id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t8 ref PRIMARY PRIMARY 37 const 1 Using where; Using index -4 SUBQUERY t8 ref PRIMARY PRIMARY 37 1 Using where; Using index -2 SUBQUERY t8 ref PRIMARY PRIMARY 37 const 1 Using where -3 SUBQUERY t8 ref PRIMARY PRIMARY 37 1 Using where; Using index +1 PRIMARY t8 const PRIMARY PRIMARY 37 const 1 Using index +4 SUBQUERY t8 const PRIMARY PRIMARY 37 1 Using index +2 SUBQUERY t8 const PRIMARY PRIMARY 37 const 1 +3 SUBQUERY t8 const PRIMARY PRIMARY 37 1 Using index Warnings: -Note 1003 select `test`.`t8`.`pseudo` AS `pseudo`,(select `test`.`t8`.`email` AS `email` from `test`.`t8` where (`test`.`t8`.`pseudo` = (select `test`.`t8`.`pseudo` AS `pseudo` from `test`.`t8` where (`test`.`t8`.`pseudo` = _latin1'joce')))) AS `(SELECT email FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce'))` from `test`.`t8` where (`test`.`t8`.`pseudo` = (select `test`.`t8`.`pseudo` AS `pseudo` from `test`.`t8` where (`test`.`t8`.`pseudo` = _latin1'joce'))) +Note 1003 select `test`.`t8`.`pseudo` AS `pseudo`,(select `test`.`t8`.`email` AS `email` from `test`.`t8` where 1) AS `(SELECT email FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce'))` from `test`.`t8` where 1 SELECT pseudo FROM t8 WHERE pseudo=(SELECT pseudo,email FROM t8 WHERE pseudo='joce'); ERROR 21000: Operand should contain 1 column(s) diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test index 36b3749b4d7..1a5a3d2b254 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -2998,3 +2998,17 @@ SELECT * FROM t1 LEFT JOIN t2 ON t2.b=t1.a INNER JOIN t3 ON t3.d=t1.id DROP TABLE t1,t2,t3; + +# +# Bug #22367: Optimizer uses ref join type instead of eq_ref for simple +# join on strings +# +CREATE TABLE t1 (a int, b varchar(20) NOT NULL, PRIMARY KEY(a)); +CREATE TABLE t2 (a int, b varchar(20) NOT NULL, + PRIMARY KEY (a), UNIQUE KEY (b)); +INSERT INTO t1 VALUES (1,'a'),(2,'b'),(3,'c'); +INSERT INTO t2 VALUES (1,'a'),(2,'b'),(3,'c'); + +EXPLAIN SELECT t1.a FROM t1 LEFT JOIN t2 ON t2.b=t1.b WHERE t1.a=3; + +DROP TABLE t1,t2; From 0e954d2c1a686c1aa8fccd929db7af116f5ba3de Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@macbook.gmz" <> Date: Mon, 16 Oct 2006 19:30:19 +0300 Subject: [PATCH 34/60] Bug #22342: No results returned for query using max and group by When using index for group by and range access the server isolates a set of ranges based on the conditions over the key parts of the index used. Then it uses only the ranges over the GROUP BY fields to jump from one group to another. Since the GROUP BY fields may form a prefix over the index, we may use only a prefix of the ranges produced by the range optimizer. Each range contains a notion on whether it includes its border values. The problem is that when using a range prefix, the last range is open because it assumes that there is a range on the next keypart. Thus when we use a prefix range as it is, it excludes all border values. The solution is when ignoring the suffix of the range conditions (to jump over the GROUP BY prefix only) the server must change the remaining intervals so they always contain their borders, e.g. if the whole range was : (1,-inf) <= (,) < (1, 3) we must make (1) <= () <= (1) because (a,b) < (c1,c2) means : a < c1 OR (a = c1 AND b < c2). --- mysql-test/r/group_min_max.result | 20 +++++++++++++++++ mysql-test/t/group_min_max.test | 16 +++++++++++++ sql/opt_range.cc | 37 +++++++++++++++++++++++++++++++ sql/opt_range.h | 2 ++ 4 files changed, 75 insertions(+) diff --git a/mysql-test/r/group_min_max.result b/mysql-test/r/group_min_max.result index fe6f7c4ca55..826e642ce88 100644 --- a/mysql-test/r/group_min_max.result +++ b/mysql-test/r/group_min_max.result @@ -2142,3 +2142,23 @@ t1; id2 id3 id5 id4 id3 id6 id5 id1 1 1 1 1 1 1 1 1 DROP TABLE t1,t2,t3,t4,t5,t6; +CREATE TABLE t1 (a int, b int, PRIMARY KEY (a,b), KEY b (b)); +INSERT INTO t1 VALUES (1,1),(1,2); +explain SELECT MAX(b), a FROM t1 WHERE b < 2 AND a = 1 GROUP BY a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range PRIMARY,b PRIMARY 8 NULL 2 Using where; Using index for group-by +SELECT MAX(b), a FROM t1 WHERE b < 2 AND a = 1 GROUP BY a; +MAX(b) a +1 1 +SELECT MIN(b), a FROM t1 WHERE b > 1 AND a = 1 GROUP BY a; +MIN(b) a +2 1 +CREATE TABLE t2 (a int, b int, c int, PRIMARY KEY (a,b,c)); +INSERT INTO t2 SELECT a,b,b FROM t1; +explain SELECT MIN(c) FROM t2 WHERE b = 2 and a = 1 and c > 1 GROUP BY a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 range PRIMARY PRIMARY 12 NULL 2 Using where; Using index for group-by +SELECT MIN(c) FROM t2 WHERE b = 2 and a = 1 and c > 1 GROUP BY a; +MIN(c) +2 +DROP TABLE t1,t2; diff --git a/mysql-test/t/group_min_max.test b/mysql-test/t/group_min_max.test index 5427727a8f4..9f3fb5ea51e 100644 --- a/mysql-test/t/group_min_max.test +++ b/mysql-test/t/group_min_max.test @@ -794,3 +794,19 @@ SELECT * FROM t1; DROP TABLE t1,t2,t3,t4,t5,t6; + +# +# Bug#22342: No results returned for query using max and group by +# +CREATE TABLE t1 (a int, b int, PRIMARY KEY (a,b), KEY b (b)); +INSERT INTO t1 VALUES (1,1),(1,2); + +explain SELECT MAX(b), a FROM t1 WHERE b < 2 AND a = 1 GROUP BY a; +SELECT MAX(b), a FROM t1 WHERE b < 2 AND a = 1 GROUP BY a; +SELECT MIN(b), a FROM t1 WHERE b > 1 AND a = 1 GROUP BY a; +CREATE TABLE t2 (a int, b int, c int, PRIMARY KEY (a,b,c)); +INSERT INTO t2 SELECT a,b,b FROM t1; +explain SELECT MIN(c) FROM t2 WHERE b = 2 and a = 1 and c > 1 GROUP BY a; +SELECT MIN(c) FROM t2 WHERE b = 2 and a = 1 and c > 1 GROUP BY a; + +DROP TABLE t1,t2; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 93566dbc281..ad0abb798d1 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -8374,6 +8374,7 @@ TRP_GROUP_MIN_MAX::make_quick(PARAM *param, bool retrieve_full_rows, quick->quick_prefix_select= NULL; quick->update_key_stat(); + quick->adjust_prefix_ranges(); DBUG_RETURN(quick); } @@ -8603,6 +8604,42 @@ bool QUICK_GROUP_MIN_MAX_SELECT::add_range(SEL_ARG *sel_range) } +/* + Opens the ranges if there are more conditions in quick_prefix_select than + the ones used for jumping through the prefixes. + + SYNOPSIS + QUICK_GROUP_MIN_MAX_SELECT::adjust_prefix_ranges() + + NOTES + quick_prefix_select is made over the conditions on the whole key. + It defines a number of ranges of length x. + However when jumping through the prefixes we use only the the first + few most significant keyparts in the range key. However if there + are more keyparts to follow the ones we are using we must make the + condition on the key inclusive (because x < "ab" means + x[0] < 'a' OR (x[0] == 'a' AND x[1] < 'b'). + To achive the above we must turn off the NEAR_MIN/NEAR_MAX +*/ +void QUICK_GROUP_MIN_MAX_SELECT::adjust_prefix_ranges () +{ + if (quick_prefix_select && + group_prefix_len < quick_prefix_select->max_used_key_length) + { + DYNAMIC_ARRAY *arr; + uint inx; + + for (inx= 0, arr= &quick_prefix_select->ranges; inx < arr->elements; inx++) + { + QUICK_RANGE *range; + + get_dynamic(arr, (gptr)&range, inx); + range->flag &= ~(NEAR_MIN | NEAR_MAX); + } + } +} + + /* Determine the total number and length of the keys that will be used for index lookup. diff --git a/sql/opt_range.h b/sql/opt_range.h index 9474f2d469f..784dd648ad2 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -294,6 +294,7 @@ protected: friend class QUICK_SELECT_DESC; friend class QUICK_INDEX_MERGE_SELECT; friend class QUICK_ROR_INTERSECT_SELECT; + friend class QUICK_GROUP_MIN_MAX_SELECT; DYNAMIC_ARRAY ranges; /* ordered array of range ptrs */ QUICK_RANGE **cur_range; /* current element in ranges */ @@ -642,6 +643,7 @@ public: ~QUICK_GROUP_MIN_MAX_SELECT(); bool add_range(SEL_ARG *sel_range); void update_key_stat(); + void adjust_prefix_ranges(); bool alloc_buffers(); int init(); int reset(); From c467be8d6e60c0216da0aca386a99f9fd688f5a4 Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Mon, 16 Oct 2006 14:25:28 -0700 Subject: [PATCH 35/60] Fixed bug #19579: at range analysis optimizer did not take into account predicates that become sargable after reading const tables. In some cases this resulted in choosing non-optimal execution plans. Now info of such potentially saragable predicates is saved in an array and after reading const tables we check whether this predicates has become saragable. --- mysql-test/r/select.result | 49 +++++++++++ mysql-test/t/select.test | 45 ++++++++++ sql/item_cmpfunc.cc | 2 + sql/sql_base.cc | 1 + sql/sql_lex.cc | 2 +- sql/sql_lex.h | 3 +- sql/sql_select.cc | 171 ++++++++++++++++++++++++++++++------- 7 files changed, 238 insertions(+), 35 deletions(-) diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index 9c35e1963e6..19492d418ff 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -3552,3 +3552,52 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 range PRIMARY PRIMARY 12 NULL 2 Using where 1 SIMPLE t2 ref PRIMARY PRIMARY 18 test.t1.fk 1 Using where DROP TABLE t1,t2; +CREATE TABLE t1(id int PRIMARY KEY, b int, e int); +CREATE TABLE t2(i int, a int, INDEX si(i), INDEX ai(a)); +CREATE TABLE t3(a int PRIMARY KEY, c char(4), INDEX ci(c)); +INSERT INTO t1 VALUES +(1,10,19), (2,20,22), (4,41,42), (9,93,95), (7, 77,79), +(6,63,67), (5,55,58), (3,38,39), (8,81,89); +INSERT INTO t2 VALUES +(21,210), (41,410), (82,820), (83,830), (84,840), +(65,650), (51,510), (37,370), (94,940), (76,760), +(22,220), (33,330), (40,400), (95,950), (38,380), +(67,670), (88,880), (57,570), (96,960), (97,970); +INSERT INTO t3 VALUES +(210,'bb'), (950,'ii'), (400,'ab'), (500,'ee'), (220,'gg'), +(440,'gg'), (310,'eg'), (380,'ee'), (840,'bb'), (830,'ff'), +(230,'aa'), (960,'ii'), (410,'aa'), (510,'ee'), (290,'bb'), +(450,'gg'), (320,'dd'), (390,'hh'), (850,'jj'), (860,'ff'); +EXPLAIN +SELECT t3.a FROM t1,t2 FORCE INDEX (si),t3 +WHERE t1.id = 8 AND t2.i BETWEEN t1.b AND t1.e AND +t3.a=t2.a AND t3.c IN ('bb','ee'); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1 +1 SIMPLE t2 range si si 5 NULL 4 Using where +1 SIMPLE t3 eq_ref PRIMARY,ci PRIMARY 4 test.t2.a 1 Using where +EXPLAIN +SELECT t3.a FROM t1,t2,t3 +WHERE t1.id = 8 AND t2.i BETWEEN t1.b AND t1.e AND +t3.a=t2.a AND t3.c IN ('bb','ee') ; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1 +1 SIMPLE t2 range si,ai si 5 NULL 4 Using where +1 SIMPLE t3 eq_ref PRIMARY,ci PRIMARY 4 test.t2.a 1 Using where +EXPLAIN +SELECT t3.a FROM t1,t2 FORCE INDEX (si),t3 +WHERE t1.id = 8 AND (t2.i=t1.b OR t2.i=t1.e) AND t3.a=t2.a AND +t3.c IN ('bb','ee'); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1 +1 SIMPLE t2 range si si 5 NULL 2 Using where +1 SIMPLE t3 eq_ref PRIMARY,ci PRIMARY 4 test.t2.a 1 Using where +EXPLAIN +SELECT t3.a FROM t1,t2,t3 +WHERE t1.id = 8 AND (t2.i=t1.b OR t2.i=t1.e) AND t3.a=t2.a AND +t3.c IN ('bb','ee'); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1 +1 SIMPLE t2 range si,ai si 5 NULL 2 Using where +1 SIMPLE t3 eq_ref PRIMARY,ci PRIMARY 4 test.t2.a 1 Using where +DROP TABLE t1,t2,t3; diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test index 166b709f9b6..a49872654b3 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -3007,6 +3007,8 @@ c7 int, c8 int, c9 int, fulltext key (`c1`)); select distinct match (`c1`) against ('z') , c2, c3, c4,c5, c6,c7, c8 from t1 where c9=1 order by c2, c2; drop table t1; + +# # Bug #22735: no equality propagation for BETWEEN and IN with STRING arguments # @@ -3033,3 +3035,46 @@ EXPLAIN SELECT t2.* WHERE t2.fk IN ('a','b') AND t2.pk=t1.fk; DROP TABLE t1,t2; + +# +# Bug #19579: predicates that become sargable after reading const tables +# are not taken into account by optimizer +# + +CREATE TABLE t1(id int PRIMARY KEY, b int, e int); +CREATE TABLE t2(i int, a int, INDEX si(i), INDEX ai(a)); +CREATE TABLE t3(a int PRIMARY KEY, c char(4), INDEX ci(c)); + +INSERT INTO t1 VALUES + (1,10,19), (2,20,22), (4,41,42), (9,93,95), (7, 77,79), + (6,63,67), (5,55,58), (3,38,39), (8,81,89); +INSERT INTO t2 VALUES + (21,210), (41,410), (82,820), (83,830), (84,840), + (65,650), (51,510), (37,370), (94,940), (76,760), + (22,220), (33,330), (40,400), (95,950), (38,380), + (67,670), (88,880), (57,570), (96,960), (97,970); +INSERT INTO t3 VALUES + (210,'bb'), (950,'ii'), (400,'ab'), (500,'ee'), (220,'gg'), + (440,'gg'), (310,'eg'), (380,'ee'), (840,'bb'), (830,'ff'), + (230,'aa'), (960,'ii'), (410,'aa'), (510,'ee'), (290,'bb'), + (450,'gg'), (320,'dd'), (390,'hh'), (850,'jj'), (860,'ff'); + +EXPLAIN +SELECT t3.a FROM t1,t2 FORCE INDEX (si),t3 + WHERE t1.id = 8 AND t2.i BETWEEN t1.b AND t1.e AND + t3.a=t2.a AND t3.c IN ('bb','ee'); +EXPLAIN +SELECT t3.a FROM t1,t2,t3 + WHERE t1.id = 8 AND t2.i BETWEEN t1.b AND t1.e AND + t3.a=t2.a AND t3.c IN ('bb','ee') ; + +EXPLAIN +SELECT t3.a FROM t1,t2 FORCE INDEX (si),t3 + WHERE t1.id = 8 AND (t2.i=t1.b OR t2.i=t1.e) AND t3.a=t2.a AND + t3.c IN ('bb','ee'); +EXPLAIN +SELECT t3.a FROM t1,t2,t3 + WHERE t1.id = 8 AND (t2.i=t1.b OR t2.i=t1.e) AND t3.a=t2.a AND + t3.c IN ('bb','ee'); + +DROP TABLE t1,t2,t3; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 780d70d51dc..9a400d60ae6 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1078,6 +1078,8 @@ bool Item_func_between::fix_fields(THD *thd, Item **ref) if (Item_func_opt_neg::fix_fields(thd, ref)) return 1; + thd->lex->current_select->between_count++; + /* not_null_tables_cache == union(T1(e),T1(e1),T1(e2)) */ if (pred_level && !negated) return 0; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 047fb8f3cff..0939fb3a47e 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -4906,6 +4906,7 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, thd->set_query_id=1; select_lex->cond_count= 0; + select_lex->between_count= 0; for (table= tables; table; table= table->next_local) { diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 788276ac654..af81960f9ef 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1138,7 +1138,7 @@ void st_select_lex::init_query() initialization is checked for failure. */ parent_lex->push_context(&context); - cond_count= with_wild= 0; + cond_count= between_count= with_wild= 0; conds_processed_with_permanent_arena= 0; ref_pointer_array= 0; select_n_having_items= 0; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index fdf14c691e9..a3173b73d6d 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -530,7 +530,8 @@ public: list during split_sum_func */ uint select_n_having_items; - uint cond_count; /* number of arguments of and/or/xor in where/having */ + uint cond_count; /* number of arguments of and/or/xor in where/having/on */ + uint between_count; /* number of between predicates in where/having/on */ enum_parsing_place parsing_place; /* where we are parsing expression */ bool with_sum_func; /* sum function indicator */ /* diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 3935029190b..d233e2824ea 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -35,14 +35,17 @@ const char *join_type_str[]={ "UNKNOWN","system","const","eq_ref","ref", "index_merge" }; +struct st_sargable_param; + static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array); static bool make_join_statistics(JOIN *join, TABLE_LIST *leaves, COND *conds, DYNAMIC_ARRAY *keyuse); static bool update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse, - JOIN_TAB *join_tab, + JOIN_TAB *join_tab, uint tables, COND *conds, COND_EQUAL *cond_equal, - table_map table_map, SELECT_LEX *select_lex); + table_map table_map, SELECT_LEX *select_lex, + st_sargable_param **sargables); static int sort_keyuse(KEYUSE *a,KEYUSE *b); static void set_position(JOIN *join,uint index,JOIN_TAB *table,KEYUSE *key); static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, @@ -2042,6 +2045,19 @@ static ha_rows get_quick_record_count(THD *thd, SQL_SELECT *select, DBUG_RETURN(HA_POS_ERROR); /* This shouldn't happend */ } +/* + This structure is used to collect info on potentially sargable + predicates in order to check whether they become sargable after + reading const tables. + We form a bitmap of indexes that can be used for sargable predicates. + Only such indexes are involved in range analysis. +*/ +typedef struct st_sargable_param +{ + Field *field; /* field against which to check sargability */ + Item **arg_value; /* values of potential keys for lookups */ + uint num_values; /* number of values in the above array */ +} SARGABLE_PARAM; /* Calculate the best possible join and initialize the join structure @@ -2064,6 +2080,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds, JOIN_TAB *stat,*stat_end,*s,**stat_ref; KEYUSE *keyuse,*start_keyuse; table_map outer_join=0; + SARGABLE_PARAM *sargables= 0; JOIN_TAB *stat_vector[MAX_TABLES+1]; DBUG_ENTER("make_join_statistics"); @@ -2185,7 +2202,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds, if (conds || outer_join) if (update_ref_and_keys(join->thd, keyuse_array, stat, join->tables, conds, join->cond_equal, - ~outer_join, join->select_lex)) + ~outer_join, join->select_lex, &sargables)) DBUG_RETURN(1); /* Read tables with 0 or 1 rows (system tables) */ @@ -2335,6 +2352,26 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds, } } while (join->const_table_map & found_ref && ref_changed); + /* + Update info on indexes that can be used for search lookups as + reading const tables may has added new sargable predicates. + */ + if (const_count && sargables) + { + for( ; sargables->field ; sargables++) + { + Field *field= sargables->field; + JOIN_TAB *stat= field->table->reginfo.join_tab; + key_map possible_keys= field->key_start; + possible_keys.intersect(field->table->keys_in_use_for_query); + bool is_const= 1; + for (uint i=0; i< sargables->num_values; i++) + is_const&= sargables->arg_value[i]->const_item(); + if (is_const) + stat[0].const_keys.merge(possible_keys); + } + } + /* Calc how many (possible) matched records in each table */ for (s=stat ; s < stat_end ; s++) @@ -2594,6 +2631,7 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end, eq_func True if we used =, <=> or IS NULL value Value used for comparison with field usable_tables Tables which can be used for key optimization + sargables IN/OUT Array of found sargable candidates NOTES If we are doing a NOT NULL comparison on a NOT NULL field in a outer join @@ -2605,8 +2643,8 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end, static void add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, - Field *field, bool eq_func, Item **value, uint num_values, - table_map usable_tables) + Field *field, bool eq_func, Item **value, uint num_values, + table_map usable_tables, SARGABLE_PARAM **sargables) { uint exists_optimize= 0; if (!(field->flags & PART_KEY_FLAG)) @@ -2662,6 +2700,19 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, is_const&= value[i]->const_item(); if (is_const) stat[0].const_keys.merge(possible_keys); + else if (!eq_func) + { + /* + Save info to be able check whether this predicate can be + considered as sargable for range analisis after reading const tables. + We do not save info about equalities as update_const_equal_items + will take care of updating info on keys from sargable equalities. + */ + (*sargables)--; + (*sargables)->field= field; + (*sargables)->arg_value= value; + (*sargables)->num_values= num_values; + } /* We can't always use indexes when comparing a string index to a number. cmp_type() is checked to allow compare of dates to numbers. @@ -2752,6 +2803,7 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, value Value used for comparison with field Is NULL for BETWEEN and IN usable_tables Tables which can be used for key optimization + sargables IN/OUT Array of found sargable candidates NOTES If field items f1 and f2 belong to the same multiple equality and @@ -2765,11 +2817,12 @@ static void add_key_equal_fields(KEY_FIELD **key_fields, uint and_level, Item_func *cond, Item_field *field_item, bool eq_func, Item **val, - uint num_values, table_map usable_tables) + uint num_values, table_map usable_tables, + SARGABLE_PARAM **sargables) { Field *field= field_item->field; add_key_field(key_fields, and_level, cond, field, - eq_func, val, num_values, usable_tables); + eq_func, val, num_values, usable_tables, sargables); Item_equal *item_equal= field_item->item_equal; if (item_equal) { @@ -2784,7 +2837,8 @@ add_key_equal_fields(KEY_FIELD **key_fields, uint and_level, if (!field->eq(item->field)) { add_key_field(key_fields, and_level, cond, item->field, - eq_func, val, num_values, usable_tables); + eq_func, val, num_values, usable_tables, + sargables); } } } @@ -2792,7 +2846,8 @@ add_key_equal_fields(KEY_FIELD **key_fields, uint and_level, static void add_key_fields(KEY_FIELD **key_fields,uint *and_level, - COND *cond, table_map usable_tables) + COND *cond, table_map usable_tables, + SARGABLE_PARAM **sargables) { if (cond->type() == Item_func::COND_ITEM) { @@ -2803,20 +2858,20 @@ add_key_fields(KEY_FIELD **key_fields,uint *and_level, { Item *item; while ((item=li++)) - add_key_fields(key_fields,and_level,item,usable_tables); + add_key_fields(key_fields,and_level,item,usable_tables,sargables); for (; org_key_fields != *key_fields ; org_key_fields++) org_key_fields->level= *and_level; } else { (*and_level)++; - add_key_fields(key_fields,and_level,li++,usable_tables); + add_key_fields(key_fields,and_level,li++,usable_tables,sargables); Item *item; while ((item=li++)) { KEY_FIELD *start_key_fields= *key_fields; (*and_level)++; - add_key_fields(key_fields,and_level,item,usable_tables); + add_key_fields(key_fields,and_level,item,usable_tables,sargables); *key_fields=merge_key_fields(org_key_fields,start_key_fields, *key_fields,++(*and_level)); } @@ -2847,9 +2902,9 @@ add_key_fields(KEY_FIELD **key_fields,uint *and_level, cond_func->argument_count() != 2); add_key_equal_fields(key_fields, *and_level, cond_func, (Item_field*) (cond_func->key_item()->real_item()), - 0, values, + 0, values, cond_func->argument_count()-1, - usable_tables); + usable_tables, sargables); } if (cond_func->functype() == Item_func::BETWEEN) { @@ -2863,7 +2918,8 @@ add_key_fields(KEY_FIELD **key_fields,uint *and_level, { field_item= (Item_field *) (cond_func->arguments()[i]->real_item()); add_key_equal_fields(key_fields, *and_level, cond_func, - field_item, 0, values, 1, usable_tables); + field_item, 0, values, 1, usable_tables, + sargables); } } } @@ -2880,7 +2936,8 @@ add_key_fields(KEY_FIELD **key_fields,uint *and_level, add_key_equal_fields(key_fields, *and_level, cond_func, (Item_field*) (cond_func->arguments()[0])->real_item(), equal_func, - cond_func->arguments()+1, 1, usable_tables); + cond_func->arguments()+1, 1, usable_tables, + sargables); } if (cond_func->arguments()[1]->real_item()->type() == Item::FIELD_ITEM && cond_func->functype() != Item_func::LIKE_FUNC && @@ -2889,7 +2946,8 @@ add_key_fields(KEY_FIELD **key_fields,uint *and_level, add_key_equal_fields(key_fields, *and_level, cond_func, (Item_field*) (cond_func->arguments()[1])->real_item(), equal_func, - cond_func->arguments(),1,usable_tables); + cond_func->arguments(),1,usable_tables, + sargables); } break; } @@ -2904,7 +2962,7 @@ add_key_fields(KEY_FIELD **key_fields,uint *and_level, add_key_equal_fields(key_fields, *and_level, cond_func, (Item_field*) (cond_func->arguments()[0])->real_item(), cond_func->functype() == Item_func::ISNULL_FUNC, - &tmp, 1, usable_tables); + &tmp, 1, usable_tables, sargables); } break; case Item_func::OPTIMIZE_EQUAL: @@ -2922,7 +2980,7 @@ add_key_fields(KEY_FIELD **key_fields,uint *and_level, while ((item= it++)) { add_key_field(key_fields, *and_level, cond_func, item->field, - TRUE, &const_item, 1, usable_tables); + TRUE, &const_item, 1, usable_tables, sargables); } } else @@ -2942,7 +3000,8 @@ add_key_fields(KEY_FIELD **key_fields,uint *and_level, if (!field->eq(item->field)) { add_key_field(key_fields, *and_level, cond_func, field, - TRUE, (Item **) &item, 1, usable_tables); + TRUE, (Item **) &item, 1, usable_tables, + sargables); } } it.rewind(); @@ -3093,6 +3152,7 @@ sort_keyuse(KEYUSE *a,KEYUSE *b) nested_join_table IN Nested join pseudo-table to process end INOUT End of the key field array and_level INOUT And-level + sargables IN/OUT Array of found sargable candidates DESCRIPTION This function populates KEY_FIELD array with entries generated from the @@ -3116,7 +3176,8 @@ sort_keyuse(KEYUSE *a,KEYUSE *b) */ static void add_key_fields_for_nj(TABLE_LIST *nested_join_table, - KEY_FIELD **end, uint *and_level) + KEY_FIELD **end, uint *and_level, + SARGABLE_PARAM **sargables) { List_iterator li(nested_join_table->nested_join->join_list); table_map tables= 0; @@ -3126,12 +3187,12 @@ static void add_key_fields_for_nj(TABLE_LIST *nested_join_table, while ((table= li++)) { if (table->nested_join) - add_key_fields_for_nj(table, end, and_level); + add_key_fields_for_nj(table, end, and_level, sargables); else if (!table->on_expr) tables |= table->table->map; } - add_key_fields(end, and_level, nested_join_table->on_expr, tables); + add_key_fields(end, and_level, nested_join_table->on_expr, tables, sargables); } @@ -3146,9 +3207,10 @@ static void add_key_fields_for_nj(TABLE_LIST *nested_join_table, tables Number of tables in join cond WHERE condition (note that the function analyzes join_tab[i]->on_expr too) - normal_tables tables not inner w.r.t some outer join (ones for which + normal_tables Tables not inner w.r.t some outer join (ones for which we can make ref access based the WHERE clause) select_lex current SELECT + sargables OUT Array of found sargable candidates RETURN 0 - OK @@ -3157,27 +3219,55 @@ static void add_key_fields_for_nj(TABLE_LIST *nested_join_table, static bool update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, - uint tables, COND *cond, COND_EQUAL *cond_equal, - table_map normal_tables, SELECT_LEX *select_lex) + uint tables, COND *cond, COND_EQUAL *cond_equal, + table_map normal_tables, SELECT_LEX *select_lex, + SARGABLE_PARAM **sargables) { uint and_level,i,found_eq_constant; KEY_FIELD *key_fields, *end, *field; + uint sz; uint m= 1; if (cond_equal && cond_equal->max_members) m= cond_equal->max_members; - - if (!(key_fields=(KEY_FIELD*) - thd->alloc(sizeof(key_fields[0])* - (thd->lex->current_select->cond_count+1)*2*m))) + + /* + We use the same piece of memory to store both KEY_FIELD + and SARGABLE_PARAM structure. + KEY_FIELD values are placed at the beginning this memory + while SARGABLE_PARAM values are put at the end. + All predicates that are used to fill arrays of KEY_FIELD + and SARGABLE_PARAM structures have at most 2 arguments + except BETWEEN predicates that have 3 arguments and + IN predicates. + This any predicate if it's not BETWEEN/IN can be used + directly to fill at most 2 array elements, either of KEY_FIELD + or SARGABLE_PARAM type. For a BETWEEN predicate 3 elements + can be filled as this predicate is considered as + saragable with respect to each of its argument. + An IN predicate can require at most 1 element as currently + it is considered as sargable only for its first argument. + Multiple equality can add elements that are filled after + substitution of field arguments by equal fields. There + can be not more than cond_equal->max_members such substitutions. + */ + sz= max(sizeof(KEY_FIELD),sizeof(SARGABLE_PARAM))* + (((thd->lex->current_select->cond_count+1)*2 + + thd->lex->current_select->between_count)*m+1); + if (!(key_fields=(KEY_FIELD*) thd->alloc(sz))) return TRUE; /* purecov: inspected */ and_level= 0; field= end= key_fields; + *sargables= (SARGABLE_PARAM *) key_fields + + (sz - sizeof((*sargables)[0].field))/sizeof(SARGABLE_PARAM); + /* set a barrier for the array of SARGABLE_PARAM */ + (*sargables)[0].field= 0; + if (my_init_dynamic_array(keyuse,sizeof(KEYUSE),20,64)) return TRUE; if (cond) { - add_key_fields(&end,&and_level,cond,normal_tables); + add_key_fields(&end,&and_level,cond,normal_tables,sargables); for (; field != end ; field++) { add_key_part(keyuse,field); @@ -3200,7 +3290,7 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, */ if (*join_tab[i].on_expr_ref) add_key_fields(&end,&and_level,*join_tab[i].on_expr_ref, - join_tab[i].table->map); + join_tab[i].table->map,sargables); } /* Process ON conditions for the nested joins */ @@ -3210,7 +3300,7 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, while ((table= li++)) { if (table->nested_join) - add_key_fields_for_nj(table, &end, &and_level); + add_key_fields_for_nj(table, &end, &and_level, sargables); } } @@ -7334,7 +7424,22 @@ static void update_const_equal_items(COND *cond, JOIN_TAB *tab) ((Item_cond*) cond)->functype() == Item_func::MULT_EQUAL_FUNC) { Item_equal *item_equal= (Item_equal *) cond; + bool contained_const= item_equal->get_const() != NULL; item_equal->update_const(); + if (!contained_const && item_equal->get_const()) + { + /* Update keys for range analysis */ + Item_equal_iterator it(*item_equal); + Item_field *item_field; + while ((item_field= it++)) + { + Field *field= item_field->field; + JOIN_TAB *stat= field->table->reginfo.join_tab; + key_map possible_keys= field->key_start; + possible_keys.intersect(field->table->keys_in_use_for_query); + stat[0].const_keys.merge(possible_keys); + } + } } } From 6439d381a0589f2004f0520a5ff5e4a63b0686bd Mon Sep 17 00:00:00 2001 From: "jonas@perch.ndb.mysql.com" <> Date: Mon, 16 Oct 2006 23:37:00 +0200 Subject: [PATCH 36/60] ndb - fix some ndb strict-alias problems found with gcc 4.0.2 on Itanium2 (ndb_dd_* failures) --- .../kernel/blocks/dbtup/DbtupDiskAlloc.cpp | 58 ++++++++++++------- storage/ndb/src/kernel/vm/Pool.hpp | 6 +- 2 files changed, 42 insertions(+), 22 deletions(-) diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupDiskAlloc.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupDiskAlloc.cpp index 56f7fb1dd1e..a055b18888b 100644 --- a/storage/ndb/src/kernel/blocks/dbtup/DbtupDiskAlloc.cpp +++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupDiskAlloc.cpp @@ -75,7 +75,7 @@ Dbtup::dump_disk_alloc(Dbtup::Disk_alloc_info & alloc) for(Uint32 i = 0; i ptr; + PagePtr ptr; ArrayPool *pool= (ArrayPool*)&m_global_page_pool; LocalDLList list(*pool, alloc.m_dirty_pages[i]); for(list.first(ptr); !ptr.isNull(); list.next(ptr)) @@ -262,7 +262,7 @@ Dbtup::update_extent_pos(Disk_alloc_info& alloc, } void -Dbtup::restart_setup_page(Disk_alloc_info& alloc, Ptr pagePtr) +Dbtup::restart_setup_page(Disk_alloc_info& alloc, PagePtr pagePtr) { /** * Link to extent, clear uncommitted_used_space @@ -344,12 +344,15 @@ Dbtup::disk_page_prealloc(Signal* signal, if (!alloc.m_dirty_pages[i].isEmpty()) { ptrI= alloc.m_dirty_pages[i].firstItem; - Ptr page; - m_global_page_pool.getPtr(page, ptrI); + Ptr gpage; + m_global_page_pool.getPtr(gpage, ptrI); - disk_page_prealloc_dirty_page(alloc, *(PagePtr*)&page, i, sz); - key->m_page_no= ((Page*)page.p)->m_page_no; - key->m_file_no= ((Page*)page.p)->m_file_no; + PagePtr tmp; + tmp.i = gpage.i; + tmp.p = reinterpret_cast(gpage.p); + disk_page_prealloc_dirty_page(alloc, tmp, i, sz); + key->m_page_no= tmp.p->m_page_no; + key->m_file_no= tmp.p->m_file_no; if (DBG_DISK) ndbout << " found dirty page " << *key << endl; return 0; // Page in memory @@ -547,7 +550,7 @@ Dbtup::disk_page_prealloc(Signal* signal, void Dbtup::disk_page_prealloc_dirty_page(Disk_alloc_info & alloc, - Ptr pagePtr, + PagePtr pagePtr, Uint32 old_idx, Uint32 sz) { ddassert(pagePtr.p->list_index == old_idx); @@ -638,7 +641,9 @@ Dbtup::disk_page_prealloc_callback(Signal* signal, fragPtr.i= req.p->m_frag_ptr_i; ptrCheckGuard(fragPtr, cnoOfFragrec, fragrecord); - Ptr pagePtr = *(Ptr*)&gpage; + PagePtr pagePtr; + pagePtr.i = gpage.i; + pagePtr.p = reinterpret_cast(gpage.p); if (unlikely(pagePtr.p->m_restart_seq != globalData.m_restart_seq)) { @@ -666,7 +671,9 @@ Dbtup::disk_page_prealloc_initial_callback(Signal*signal, Ptr gpage; m_global_page_pool.getPtr(gpage, page_id); - Ptr pagePtr = *(Ptr*)&gpage; + PagePtr pagePtr; + pagePtr.i = gpage.i; + pagePtr.p = reinterpret_cast(gpage.p); Ptr fragPtr; fragPtr.i= req.p->m_frag_ptr_i; @@ -705,7 +712,7 @@ void Dbtup::disk_page_prealloc_callback_common(Signal* signal, Ptr req, Ptr fragPtr, - Ptr pagePtr) + PagePtr pagePtr) { /** * 1) remove page request from Disk_alloc_info.m_page_requests @@ -736,7 +743,7 @@ Dbtup::disk_page_prealloc_callback_common(Signal* signal, */ ArrayPool *cheat_pool= (ArrayPool*)&m_global_page_pool; LocalDLList list(* cheat_pool, alloc.m_dirty_pages[new_idx]); - list.add(*(Ptr*)&pagePtr); + list.add(pagePtr); pagePtr.p->uncommitted_used_space = real_used; pagePtr.p->list_index = new_idx; @@ -765,7 +772,7 @@ Dbtup::disk_page_prealloc_callback_common(Signal* signal, } void -Dbtup::disk_page_set_dirty(Ptr pagePtr) +Dbtup::disk_page_set_dirty(PagePtr pagePtr) { Uint32 idx = pagePtr.p->list_index; if ((idx & 0x8000) == 0) @@ -833,7 +840,9 @@ Dbtup::disk_page_unmap_callback(Uint32 page_id, Uint32 dirty_count) { Ptr gpage; m_global_page_pool.getPtr(gpage, page_id); - PagePtr pagePtr= *(PagePtr*)&gpage; + PagePtr pagePtr; + pagePtr.i = gpage.i; + pagePtr.p = reinterpret_cast(gpage.p); Uint32 type = pagePtr.p->m_page_header.m_page_type; if (unlikely((type != File_formats::PT_Tup_fixsize_page && @@ -1028,10 +1037,13 @@ Dbtup::disk_page_abort_prealloc(Signal *signal, Fragrecord* fragPtrP, case -1: break; default: - Ptr page; - m_global_page_pool.getPtr(page, (Uint32)res); - disk_page_abort_prealloc_callback_1(signal, fragPtrP, *(PagePtr*)&page, - sz); + Ptr gpage; + m_global_page_pool.getPtr(gpage, (Uint32)res); + PagePtr pagePtr; + pagePtr.i = gpage.i; + pagePtr.p = reinterpret_cast(gpage.p); + + disk_page_abort_prealloc_callback_1(signal, fragPtrP, pagePtr, sz); } } @@ -1044,8 +1056,10 @@ Dbtup::disk_page_abort_prealloc_callback(Signal* signal, Ptr gpage; m_global_page_pool.getPtr(gpage, page_id); - PagePtr pagePtr= *(PagePtr*)&gpage; - + PagePtr pagePtr; + pagePtr.i = gpage.i; + pagePtr.p = reinterpret_cast(gpage.p); + Ptr tabPtr; tabPtr.i= pagePtr.p->m_table_id; ptrCheckGuard(tabPtr, cnoOfTablerec, tablerec); @@ -1308,7 +1322,9 @@ Dbtup::disk_restart_undo_callback(Signal* signal, jamEntry(); Ptr gpage; m_global_page_pool.getPtr(gpage, page_id); - Ptr pagePtr = *(Ptr*)&gpage; + PagePtr pagePtr; + pagePtr.i = gpage.i; + pagePtr.p = reinterpret_cast(gpage.p); Apply_undo* undo = &f_undo; diff --git a/storage/ndb/src/kernel/vm/Pool.hpp b/storage/ndb/src/kernel/vm/Pool.hpp index 70fcdf8b56a..fe3a50e127b 100644 --- a/storage/ndb/src/kernel/vm/Pool.hpp +++ b/storage/ndb/src/kernel/vm/Pool.hpp @@ -305,7 +305,11 @@ inline bool RecordPool::seize(Ptr & ptr) { - return m_pool.seize(*(Ptr*)&ptr); + Ptr tmp; + bool ret = m_pool.seize(tmp); + ptr.i = tmp.i; + ptr.p = static_cast(tmp.p); + return ret; } template From 52dacf7c9a33bcbf956ce971c013555469179821 Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@macbook.gmz" <> Date: Tue, 17 Oct 2006 12:06:06 +0300 Subject: [PATCH 37/60] merge changes becuase of the fix for bug 22367 --- mysql-test/r/select.result | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index 547b4c71895..9bf811a00a1 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -3538,19 +3538,19 @@ FROM t1 JOIN t2 ON t2.fk=t1.pk WHERE t2.fk < 'c' AND t2.pk=t1.fk; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 range PRIMARY PRIMARY 12 NULL 3 Using where -1 SIMPLE t2 ref PRIMARY PRIMARY 18 test.t1.fk 1 Using where +1 SIMPLE t2 eq_ref PRIMARY PRIMARY 18 test.t1.fk 1 Using where EXPLAIN SELECT t2.* FROM t1 JOIN t2 ON t2.fk=t1.pk WHERE t2.fk BETWEEN 'a' AND 'b' AND t2.pk=t1.fk; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 range PRIMARY PRIMARY 12 NULL 2 Using where -1 SIMPLE t2 ref PRIMARY PRIMARY 18 test.t1.fk 1 Using where +1 SIMPLE t2 eq_ref PRIMARY PRIMARY 18 test.t1.fk 1 Using where EXPLAIN SELECT t2.* FROM t1 JOIN t2 ON t2.fk=t1.pk WHERE t2.fk IN ('a','b') AND t2.pk=t1.fk; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 range PRIMARY PRIMARY 12 NULL 2 Using where -1 SIMPLE t2 ref PRIMARY PRIMARY 18 test.t1.fk 1 Using where +1 SIMPLE t2 eq_ref PRIMARY PRIMARY 18 test.t1.fk 1 Using where DROP TABLE t1,t2; CREATE TABLE t1 (a int, b varchar(20) NOT NULL, PRIMARY KEY(a)); CREATE TABLE t2 (a int, b varchar(20) NOT NULL, From 44e0587486e2f2a0d2245430092ab82b367cc3ba Mon Sep 17 00:00:00 2001 From: "jonas@perch.ndb.mysql.com" <> Date: Tue, 17 Oct 2006 13:34:00 +0200 Subject: [PATCH 38/60] ndb - ndb_restore_compat Update make_binary_distribution.sh Make sure all files needed for testing are included --- scripts/make_binary_distribution.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/make_binary_distribution.sh b/scripts/make_binary_distribution.sh index 81e7f22c1b8..b05c899cb2f 100644 --- a/scripts/make_binary_distribution.sh +++ b/scripts/make_binary_distribution.sh @@ -343,6 +343,8 @@ if [ x$NDBCLUSTER = x1 ]; then test -d $BASE/include/storage || mkdir $BASE/include/storage $CP -r $BASE/ndb-stage@pkgincludedir@/storage/ndb $BASE/include/storage/ $CP -r $BASE/ndb-stage@prefix@/mysql-test/ndb $BASE/mysql-test/. || exit 1 + $CP -r $BASE/ndb-stage@prefix@/mysql-test/std_data/ndb_backup50 $BASE/mysql-test/std_data/. || exit 1 + $CP -r $BASE/ndb-stage@prefix@/mysql-test/std_data/ndb_backup51 $BASE/mysql-test/std_data/. || exit 1 rm -rf $BASE/ndb-stage fi From 2f7b2611f78bf0158583a1eaaeaf249c5f65408b Mon Sep 17 00:00:00 2001 From: "jonas@perch.ndb.mysql.com" <> Date: Tue, 17 Oct 2006 15:05:31 +0200 Subject: [PATCH 39/60] ndb - remove duplicate solution to ndb_restore_compat make_binary_distribution problem --- scripts/make_binary_distribution.sh | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/scripts/make_binary_distribution.sh b/scripts/make_binary_distribution.sh index b05c899cb2f..9302745ceee 100644 --- a/scripts/make_binary_distribution.sh +++ b/scripts/make_binary_distribution.sh @@ -93,7 +93,6 @@ mkdir $BASE $BASE/bin $BASE/docs \ $BASE/include $BASE/lib $BASE/support-files $BASE/share $BASE/scripts \ $BASE/mysql-test $BASE/mysql-test/t $BASE/mysql-test/r \ $BASE/mysql-test/include $BASE/mysql-test/std_data $BASE/mysql-test/lib \ - $BASE/mysql-test/std_data/ndb_backup50 $BASE/mysql-test/std_data/ndb_backup51 \ $BASE/mysql-test/extra \ $BASE/mysql-test/extra/binlog_tests $BASE/mysql-test/extra/rpl_tests @@ -250,14 +249,6 @@ $CP mysql-test/std_data/*.dat mysql-test/std_data/*.frm \ mysql-test/std_data/des_key_file mysql-test/std_data/*.*001 \ mysql-test/std_data/*.cnf \ $BASE/mysql-test/std_data -$CP mysql-test/std_data/ndb_backup50/*.Data \ - mysql-test/std_data/ndb_backup50/*.ctl \ - mysql-test/std_data/ndb_backup50/*.log \ - $BASE/mysql-test/std_data/ndb_backup50 -$CP mysql-test/std_data/ndb_backup51/*.Data \ - mysql-test/std_data/ndb_backup51/*.ctl \ - mysql-test/std_data/ndb_backup51/*.log \ - $BASE/mysql-test/std_data/ndb_backup51 $CP mysql-test/t/*.test $BASE/mysql-test/t $CP mysql-test/t/*.imtest mysql-test/t/*.disabled $BASE/mysql-test/t $CP mysql-test/t/*.opt mysql-test/t/*.slave-mi $BASE/mysql-test/t @@ -335,8 +326,8 @@ fi # NDB Cluster if [ x$NDBCLUSTER = x1 ]; then - ( cd storage/ndb ; @MAKE@ DESTDIR=$BASE/ndb-stage install ) - ( cd mysql-test/ndb ; @MAKE@ DESTDIR=$BASE/ndb-stage install ) + ( cd storage/ndb ; @MAKE@ DESTDIR=$BASE/ndb-stage install ) + ( cd mysql-test ; @MAKE@ DESTDIR=$BASE/ndb-stage install ) $CP $BASE/ndb-stage@bindir@/* $BASE/bin/. $CP $BASE/ndb-stage@libexecdir@/* $BASE/bin/. $CP $BASE/ndb-stage@pkglibdir@/* $BASE/lib/. From f7b8937661e3c8ceb1e16a868c92585b2c66d01c Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@macbook.gmz" <> Date: Tue, 17 Oct 2006 16:20:26 +0300 Subject: [PATCH 40/60] Bug#21798: memory leak during query execution with subquery in column list using a function When executing dependent subqueries they are re-inited and re-exec() for each row of the outer context. The cause for the bug is that during subquery reinitialization/re-execution, the optimizer reallocates JOIN::join_tab, JOIN::table in make_simple_join() and the local variable in 'sortorder' in create_sort_index(), which is allocated by make_unireg_sortorder(). Care must be taken not to allocate anything into the thread's memory pool while re-initializing query plan structures between subquery re-executions. All such items mush be cached and reused because the thread's memory pool is freed at the end of the whole query. Note that they must be cached and reused even for queries that are not otherwise cacheable because otherwise it will grow the thread's memory pool every time a cacheable query is re-executed. We provide additional members to the JOIN structure to store references to the items that need to be cached. --- mysql-test/r/subselect.result | 26 +++++++++++++++++++++++ mysql-test/t/subselect.test | 26 +++++++++++++++++++++++ sql/mysql_priv.h | 3 ++- sql/sql_delete.cc | 2 +- sql/sql_select.cc | 40 ++++++++++++++++++++++++++++------- sql/sql_select.h | 15 +++++++++++++ sql/sql_table.cc | 2 +- sql/sql_update.cc | 2 +- 8 files changed, 104 insertions(+), 12 deletions(-) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 2e3ca9b71f0..8027981db38 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -3393,3 +3393,29 @@ id select_type table type possible_keys key key_len ref rows Extra 4 UNION t12 system NULL NULL NULL NULL 0 const row not found NULL UNION RESULT ALL NULL NULL NULL NULL NULL DROP TABLE t1; +CREATE TABLE t1 (a VARCHAR(250), b INT auto_increment, PRIMARY KEY (b)); +insert into t1 (a) values (FLOOR(rand() * 100)); +insert into t1 (a) select FLOOR(rand() * 100) from t1; +insert into t1 (a) select FLOOR(rand() * 100) from t1; +insert into t1 (a) select FLOOR(rand() * 100) from t1; +insert into t1 (a) select FLOOR(rand() * 100) from t1; +insert into t1 (a) select FLOOR(rand() * 100) from t1; +insert into t1 (a) select FLOOR(rand() * 100) from t1; +insert into t1 (a) select FLOOR(rand() * 100) from t1; +insert into t1 (a) select FLOOR(rand() * 100) from t1; +insert into t1 (a) select FLOOR(rand() * 100) from t1; +insert into t1 (a) select FLOOR(rand() * 100) from t1; +insert into t1 (a) select FLOOR(rand() * 100) from t1; +insert into t1 (a) select FLOOR(rand() * 100) from t1; +insert into t1 (a) select FLOOR(rand() * 100) from t1; +SELECT a, +(SELECT REPEAT(' ',250) FROM t1 i1 +WHERE i1.b=t1.a ORDER BY RAND() LIMIT 1) AS a +FROM t1 ORDER BY a LIMIT 5; +a a +0 NULL +0 NULL +0 NULL +0 NULL +0 NULL +DROP TABLE t1; diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 740cb08c5bd..eefec96203a 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -2306,3 +2306,29 @@ explain select * from t1 where not exists ((select t11.i from t1 t11) union (select t12.i from t1 t12)); DROP TABLE t1; + +# +# Bug#21798: memory leak during query execution with subquery in column +# list using a function +# +CREATE TABLE t1 (a VARCHAR(250), b INT auto_increment, PRIMARY KEY (b)); +insert into t1 (a) values (FLOOR(rand() * 100)); +insert into t1 (a) select FLOOR(rand() * 100) from t1; +insert into t1 (a) select FLOOR(rand() * 100) from t1; +insert into t1 (a) select FLOOR(rand() * 100) from t1; +insert into t1 (a) select FLOOR(rand() * 100) from t1; +insert into t1 (a) select FLOOR(rand() * 100) from t1; +insert into t1 (a) select FLOOR(rand() * 100) from t1; +insert into t1 (a) select FLOOR(rand() * 100) from t1; +insert into t1 (a) select FLOOR(rand() * 100) from t1; +insert into t1 (a) select FLOOR(rand() * 100) from t1; +insert into t1 (a) select FLOOR(rand() * 100) from t1; +insert into t1 (a) select FLOOR(rand() * 100) from t1; +insert into t1 (a) select FLOOR(rand() * 100) from t1; +insert into t1 (a) select FLOOR(rand() * 100) from t1; + +SELECT a, + (SELECT REPEAT(' ',250) FROM t1 i1 + WHERE i1.b=t1.a ORDER BY RAND() LIMIT 1) AS a +FROM t1 ORDER BY a LIMIT 5; +DROP TABLE t1; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 620992b8cc7..ab32ef1badf 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -708,7 +708,8 @@ bool mysql_xa_recover(THD *thd); bool check_simple_select(); -SORT_FIELD * make_unireg_sortorder(ORDER *order, uint *length); +SORT_FIELD * make_unireg_sortorder(ORDER *order, uint *length, + SORT_FIELD *sortorder); int setup_order(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, List &fields, List &all_fields, ORDER *order); int setup_group(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index e420022b8a1..33b46080696 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -167,7 +167,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, MYF(MY_FAE | MY_ZEROFILL)); if (!(sortorder= make_unireg_sortorder((ORDER*) order->first, - &length)) || + &length, NULL)) || (table->sort.found_records = filesort(thd, table, sortorder, length, select, HA_POS_ERROR, &examined_rows)) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 4d2b3cac254..8f169a9d390 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1574,6 +1574,7 @@ JOIN::exec() { DBUG_VOID_RETURN; } + sortorder= curr_join->sortorder; } thd->proc_info="Copying to group table"; @@ -1783,6 +1784,7 @@ JOIN::exec() (select_options & OPTION_FOUND_ROWS ? HA_POS_ERROR : unit->select_limit_cnt))) DBUG_VOID_RETURN; + sortorder= curr_join->sortorder; } } /* XXX: When can we have here thd->net.report_error not zero? */ @@ -4949,9 +4951,28 @@ make_simple_join(JOIN *join,TABLE *tmp_table) JOIN_TAB *join_tab; DBUG_ENTER("make_simple_join"); - if (!(tableptr=(TABLE**) join->thd->alloc(sizeof(TABLE*))) || - !(join_tab=(JOIN_TAB*) join->thd->alloc(sizeof(JOIN_TAB)))) - DBUG_RETURN(TRUE); + /* + Reuse TABLE * and JOIN_TAB if already allocated by a previous call + to this function through JOIN::exec (may happen for sub-queries). + */ + if (!join->table_cache) + { + if (!(join->table_cache= (TABLE**) join->thd->alloc(sizeof(TABLE*)))) + DBUG_RETURN(TRUE); /* purecov: inspected */ + if (join->tmp_join) + join->tmp_join->table_cache= join->table_cache; + } + if (!join->join_tab_cache) + { + if (!(join->join_tab_cache= + (JOIN_TAB*) join->thd->alloc(sizeof(JOIN_TAB)))) + DBUG_RETURN(TRUE); /* purecov: inspected */ + if (join->tmp_join) + join->tmp_join->join_tab_cache= join->join_tab_cache; + } + tableptr= join->table_cache; + join_tab= join->join_tab_cache; + join->join_tab=join_tab; join->table=tableptr; tableptr[0]=tmp_table; join->tables=1; @@ -11971,7 +11992,6 @@ static int create_sort_index(THD *thd, JOIN *join, ORDER *order, ha_rows filesort_limit, ha_rows select_limit) { - SORT_FIELD *sortorder; uint length; ha_rows examined_rows; TABLE *table; @@ -11987,7 +12007,8 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order, if (test_if_skip_sort_order(tab,order,select_limit,0)) DBUG_RETURN(0); - if (!(sortorder=make_unireg_sortorder(order,&length))) + if (!(join->sortorder= + make_unireg_sortorder(order,&length,join->sortorder))) goto err; /* purecov: inspected */ /* It's not fatal if the following alloc fails */ table->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE), @@ -12034,7 +12055,7 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order, if (table->s->tmp_table) table->file->info(HA_STATUS_VARIABLE); // Get record count - table->sort.found_records=filesort(thd, table,sortorder, length, + table->sort.found_records=filesort(thd, table,join->sortorder, length, select, filesort_limit, &examined_rows); tab->records= table->sort.found_records; // For SQL_CALC_ROWS if (select) @@ -12381,7 +12402,8 @@ err: } -SORT_FIELD *make_unireg_sortorder(ORDER *order, uint *length) +SORT_FIELD *make_unireg_sortorder(ORDER *order, uint *length, + SORT_FIELD *sortorder) { uint count; SORT_FIELD *sort,*pos; @@ -12390,7 +12412,9 @@ SORT_FIELD *make_unireg_sortorder(ORDER *order, uint *length) count=0; for (ORDER *tmp = order; tmp; tmp=tmp->next) count++; - pos=sort=(SORT_FIELD*) sql_alloc(sizeof(SORT_FIELD)*(count+1)); + if (!sortorder) + sortorder= (SORT_FIELD*) sql_alloc(sizeof(SORT_FIELD)*(count+1)); + pos=sort=sortorder; if (!pos) return 0; diff --git a/sql/sql_select.h b/sql/sql_select.h index d5a1bf82bc8..e1ae8677a6e 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -282,6 +282,18 @@ public: bool union_part; // this subselect is part of union bool optimized; // flag to avoid double optimization in EXPLAIN + /* + storage for caching buffers allocated during query execution. + These buffers allocations need to be cached as the thread memory pool is + cleared only at the end of the execution of the whole query and not caching + allocations that occur in repetition at execution time will result in + excessive memory usage. + */ + SORT_FIELD *sortorder; // make_unireg_sortorder() + TABLE **table_cache; // make_simple_join() + JOIN_TAB *join_tab_cache; // make_simple_join() + /* end of allocation caching storage */ + JOIN(THD *thd_arg, List &fields_arg, ulonglong select_options_arg, select_result *result_arg) :fields_list(fields_arg) @@ -307,6 +319,9 @@ public: examined_rows= 0; exec_tmp_table1= 0; exec_tmp_table2= 0; + sortorder= 0; + table_cache= 0; + join_tab_cache= 0; thd= thd_arg; sum_funcs= sum_funcs2= 0; procedure= 0; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index e065a32c2e8..08c7e03693e 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4044,7 +4044,7 @@ copy_data_between_tables(TABLE *from,TABLE *to, if (thd->lex->select_lex.setup_ref_array(thd, order_num) || setup_order(thd, thd->lex->select_lex.ref_pointer_array, &tables, fields, all_fields, order) || - !(sortorder=make_unireg_sortorder(order, &length)) || + !(sortorder=make_unireg_sortorder(order, &length, NULL)) || (from->sort.found_records = filesort(thd, from, sortorder, length, (SQL_SELECT *) 0, HA_POS_ERROR, &examined_rows)) == diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 84b22c56cf9..4a6249410a7 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -311,7 +311,7 @@ int mysql_update(THD *thd, table->sort.io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE), MYF(MY_FAE | MY_ZEROFILL)); - if (!(sortorder=make_unireg_sortorder(order, &length)) || + if (!(sortorder=make_unireg_sortorder(order, &length, NULL)) || (table->sort.found_records = filesort(thd, table, sortorder, length, select, limit, &examined_rows)) From 8e7f0fe7850219d8e448c5ac8f8eee182a403ec6 Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@macbook.gmz" <> Date: Tue, 17 Oct 2006 19:22:13 +0300 Subject: [PATCH 41/60] rename of the new members introduced in the fix for bug 21798 --- sql/sql_select.cc | 16 ++++++++-------- sql/sql_select.h | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 2e959560f2f..4da47e87393 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -4956,23 +4956,23 @@ make_simple_join(JOIN *join,TABLE *tmp_table) Reuse TABLE * and JOIN_TAB if already allocated by a previous call to this function through JOIN::exec (may happen for sub-queries). */ - if (!join->table_cache) + if (!join->table_reexec) { - if (!(join->table_cache= (TABLE**) join->thd->alloc(sizeof(TABLE*)))) + if (!(join->table_reexec= (TABLE**) join->thd->alloc(sizeof(TABLE*)))) DBUG_RETURN(TRUE); /* purecov: inspected */ if (join->tmp_join) - join->tmp_join->table_cache= join->table_cache; + join->tmp_join->table_reexec= join->table_reexec; } - if (!join->join_tab_cache) + if (!join->join_tab_reexec) { - if (!(join->join_tab_cache= + if (!(join->join_tab_reexec= (JOIN_TAB*) join->thd->alloc(sizeof(JOIN_TAB)))) DBUG_RETURN(TRUE); /* purecov: inspected */ if (join->tmp_join) - join->tmp_join->join_tab_cache= join->join_tab_cache; + join->tmp_join->join_tab_reexec= join->join_tab_reexec; } - tableptr= join->table_cache; - join_tab= join->join_tab_cache; + tableptr= join->table_reexec; + join_tab= join->join_tab_reexec; join->join_tab=join_tab; join->table=tableptr; tableptr[0]=tmp_table; diff --git a/sql/sql_select.h b/sql/sql_select.h index e1ae8677a6e..30b8f834ddf 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -290,8 +290,8 @@ public: excessive memory usage. */ SORT_FIELD *sortorder; // make_unireg_sortorder() - TABLE **table_cache; // make_simple_join() - JOIN_TAB *join_tab_cache; // make_simple_join() + TABLE **table_reexec; // make_simple_join() + JOIN_TAB *join_tab_reexec; // make_simple_join() /* end of allocation caching storage */ JOIN(THD *thd_arg, List &fields_arg, ulonglong select_options_arg, @@ -320,8 +320,8 @@ public: exec_tmp_table1= 0; exec_tmp_table2= 0; sortorder= 0; - table_cache= 0; - join_tab_cache= 0; + table_reexec= 0; + join_tab_reexec= 0; thd= thd_arg; sum_funcs= sum_funcs2= 0; procedure= 0; From 0c5a2865f7cfad5db9574fa6678cc51d4b1d0ba3 Mon Sep 17 00:00:00 2001 From: "evgen@sunlight.local" <> Date: Wed, 18 Oct 2006 00:14:14 +0400 Subject: [PATCH 42/60] sql_rename.cc: Cleanup of fix for bug#14959. --- sql/sql_rename.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index 1a4f1d82cb5..c87a8696bbc 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -173,7 +173,7 @@ do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name, if (!access(name,F_OK)) { my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias); - DBUG_RETURN(ren_table); // This can't be skipped + DBUG_RETURN(1); // This can't be skipped } sprintf(name,"%s/%s/%s%s",mysql_data_home, ren_table->db, old_alias, From 5bce99a90b15940675f35d716b120d744c53c8e9 Mon Sep 17 00:00:00 2001 From: "jonas@perch.ndb.mysql.com" <> Date: Wed, 18 Oct 2006 10:07:02 +0200 Subject: [PATCH 43/60] ndb - Fix uninit variable, causing problems with auto-increment on rhas3-x86 (only found on this platform, really weird) --- .../src/kernel/blocks/dbtup/DbtupExecQuery.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp index 3118164badd..d9710cc2549 100644 --- a/storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp +++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp @@ -1039,22 +1039,19 @@ int Dbtup::handleUpdateReq(Signal* signal, tup_version= (tup_version + 1) & ZTUP_VERSION_MASK; operPtrP->tupVersion= tup_version; - int retValue; if (!req_struct->interpreted_exec) { jam(); - retValue= updateAttributes(req_struct, - &cinBuffer[0], - req_struct->attrinfo_len); + int retValue = updateAttributes(req_struct, + &cinBuffer[0], + req_struct->attrinfo_len); + if (unlikely(retValue == -1)) + goto error; } else { jam(); if (unlikely(interpreterStartLab(signal, req_struct) == -1)) return -1; } - if (retValue == -1) { - goto error; - } - if (regTabPtr->need_shrink()) { shrink_tuple(req_struct, sizes+2, regTabPtr, disk); @@ -1073,7 +1070,7 @@ int Dbtup::handleUpdateReq(Signal* signal, jam(); setChecksum(req_struct->m_tuple_ptr, regTabPtr); } - return retValue; + return 0; error: tupkeyErrorLab(signal); From 87a9d23487f8316552b91d8ead7e66c3d2b39e60 Mon Sep 17 00:00:00 2001 From: "kent@mysql.com/c-544072d5.010-2112-6f72651.cust.bredbandsbolaget.se" <> Date: Wed, 18 Oct 2006 12:35:45 +0200 Subject: [PATCH 44/60] mysql.spec.sh: Aligned test flags to TAR builds --- support-files/mysql.spec.sh | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/support-files/mysql.spec.sh b/support-files/mysql.spec.sh index b2069dc309d..60548210b7d 100644 --- a/support-files/mysql.spec.sh +++ b/support-files/mysql.spec.sh @@ -357,12 +357,13 @@ then cp -fp mysql-release-%{mysql_version}/config.log "$MYSQL_CONFLOG_DEST" fi -(cd mysql-release-%{mysql_version}/mysql-test ; \ - ./mysql-test-run.pl --comment=normal --force ; \ - ./mysql-test-run.pl --comment=ps --ps-protocol --force ; \ - ./mysql-test-run.pl --comment=normal+rowrepl --mysqld=--binlog-format=row --force ; \ - ./mysql-test-run.pl --comment=ps+rowrepl --ps-protocol --mysqld=--binlog-format=row --force ; \ - true) +cd mysql-release-%{mysql_version}/mysql-test +./mysql-test-run.pl --comment=normal --force --skip-ndbcluster --timer || true +./mysql-test-run.pl --comment=ps --ps-protocol --force --skip-ndbcluster --timer || true +./mysql-test-run.pl --comment=normal+rowrepl --mysqld=--binlog-format=row --force --skip-ndbcluster --timer || true +./mysql-test-run.pl --comment=ps+rowrepl+NDB --ps-protocol --mysqld=--binlog-format=row --force --timer || true +./mysql-test-run.pl --comment=NDB --with-ndbcluster-only --force --timer || true +cd ../.. ############################################################################## From b8e31d05f122f34d0b6488c617a5a16106063be9 Mon Sep 17 00:00:00 2001 From: "holyfoot/hf@mysql.com/deer.(none)" <> Date: Wed, 18 Oct 2006 17:03:37 +0500 Subject: [PATCH 45/60] bug #23369 (Embedded library can't be linked) Problem is that some files moved to storage/*/ still are dependent on sql/ code (usually use members of THD structure) that can get different being compiled with another #define-s Code added to recompile these for the embedded server --- config/ac-macros/plugins.m4 | 38 ++++++++++++++++++++++++++++++++++--- libmysqld/Makefile.am | 14 ++++++++++++-- storage/federated/plug.in | 1 + storage/heap/plug.in | 1 + storage/innobase/plug.in | 1 + storage/myisam/plug.in | 1 + storage/myisammrg/plug.in | 1 + 7 files changed, 52 insertions(+), 5 deletions(-) diff --git a/config/ac-macros/plugins.m4 b/config/ac-macros/plugins.m4 index 87f057e696a..0d5a5b46114 100644 --- a/config/ac-macros/plugins.m4 +++ b/config/ac-macros/plugins.m4 @@ -253,6 +253,29 @@ AC_DEFUN([MYSQL_PLUGIN_ACTIONS],[ ]) ]) +dnl --------------------------------------------------------------------------- +dnl Macro: MYSQL_PLUGIN_DEPENDS_ON_MYSQL_INTERNALS +dnl +dnl SYNOPSIS +dnl MYSQL_PLUGIN_DEPENDS_ON_MYSQL_INTERNALS([name],[file name]) +dnl +dnl DESCRIPTION +dnl Some modules in plugins keep dependance on structures +dnl declared in sql/ (THD class usually) +dnl That has to be fixed in the future, but until then +dnl we have to recompile these modules when we want to +dnl to compile server parts with the different #defines +dnl Normally it happens when we compile the embedded server +dnl Thus one should mark such files in his handler using this macro +dnl (currently only one such a file per plugin is supported) +dnl +dnl --------------------------------------------------------------------------- + +AC_DEFUN([MYSQL_PLUGIN_DEPENDS_ON_MYSQL_INTERNALS],[ + MYSQL_REQUIRE_PLUGIN([$1]) + m4_define([MYSQL_PLUGIN_DEPENDS_ON_MYSQL_INTERNALS_]AS_TR_CPP([$1]), [$2]) +]) + dnl --------------------------------------------------------------------------- dnl Macro: MYSQL_CONFIGURE_PLUGINS dnl @@ -282,6 +305,9 @@ AC_DEFUN([MYSQL_CONFIGURE_PLUGINS],[ AC_SUBST([mysql_pg_dirs]) AC_SUBST([mysql_se_unittest_dirs]) AC_SUBST([mysql_pg_unittest_dirs]) + AC_SUBST([condition_dependent_plugin_modules]) + AC_SUBST([condition_dependent_plugin_links]) + AC_SUBST([condition_dependent_plugin_includes]) ]) ]) ]) @@ -307,6 +333,7 @@ AC_DEFUN([_MYSQL_EMIT_CHECK_PLUGIN],[ [MYSQL_PLUGIN_DYNAMIC_]AS_TR_CPP([$1]), [MYSQL_PLUGIN_MANDATORY_]AS_TR_CPP([$1]), [MYSQL_PLUGIN_DISABLED_]AS_TR_CPP([$1]), + [MYSQL_PLUGIN_DEPENDS_ON_MYSQL_INTERNALS_]AS_TR_CPP([$1]), [MYSQL_PLUGIN_ACTIONS_]AS_TR_CPP([$1]) ) ]) @@ -318,9 +345,9 @@ AC_DEFUN([__MYSQL_EMIT_CHECK_PLUGIN],[ AC_MSG_CHECKING([whether to use ]$3) mysql_use_plugin_dir="" m4_ifdef([$10],[ - if test "X[$mysql_plugin_]$2" = Xyes -a \ - "X[$with_plugin_]$2" != Xno -o \ - "X[$with_plugin_]$2" = Xyes; then + if test "x[$mysql_plugin_]$2" = Xyes -a \ + "x[$with_plugin_]$2" != Xno -o \ + "x[$with_plugin_]$2" = Xyes; then AC_MSG_RESULT([error]) AC_MSG_ERROR([disabled]) fi @@ -346,6 +373,11 @@ AC_DEFUN([__MYSQL_EMIT_CHECK_PLUGIN],[ ]) AC_SUBST([plugin_]$2[_shared_target], "$8") AC_SUBST([plugin_]$2[_static_target], [""]) + m4_ifdef([$11],[ + condition_dependent_plugin_modules="$condition_dependent_plugin_modules m4_bregexp($11, [[^/]+$], [\&])" + condition_dependent_plugin_links="$condition_dependent_plugin_links $6/$11" + condition_dependent_plugin_includes="$condition_dependent_plugin_includes -I[\$(top_srcdir)]/$6/m4_bregexp($11, [^.+[/$]], [\&])" + ]) [with_plugin_]$2=yes AC_MSG_RESULT([plugin]) m4_ifdef([$6],[ diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am index fe19e08328d..0de37db63e2 100644 --- a/libmysqld/Makefile.am +++ b/libmysqld/Makefile.am @@ -32,7 +32,8 @@ INCLUDES= -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_srcdir)/sql -I$(top_srcdir)/sql/examples \ -I$(top_srcdir)/regex \ - $(openssl_includes) @ZLIB_INCLUDES@ + $(openssl_includes) @ZLIB_INCLUDES@ \ + @condition_dependent_plugin_includes@ noinst_LIBRARIES = libmysqld_int.a pkglib_LIBRARIES = libmysqld.a @@ -77,6 +78,8 @@ libmysqld_int_a_SOURCES= $(libmysqld_sources) $(libmysqlsources) $(sqlsources) libmysqld_a_SOURCES= sqlstoragesources = $(EXTRA_libmysqld_a_SOURCES) +storagesources = @condition_dependent_plugin_modules@ +storagesourceslinks = @condition_dependent_plugin_links@ # automake misses these sql_yacc.cc sql_yacc.h: $(top_srcdir)/sql/sql_yacc.yy @@ -170,12 +173,19 @@ link_sources: @LN_CP_F@ `find $(srcdir)/../sql -name "$$f"` "$$f"; \ done; \ fi; \ + if test -n "$(storagesources)" ; \ + then \ + rm -f $(storagesources); \ + for f in $(storagesourceslinks); do \ + @LN_CP_F@ $(top_srcdir)/$$f . ; \ + done; \ + fi; \ rm -f client_settings.h; \ @LN_CP_F@ $(top_srcdir)/libmysql/client_settings.h client_settings.h clean-local: - rm -f `echo $(sqlsources) $(libmysqlsources) $(sqlstoragesources) | sed "s;\.lo;.c;g"` \ + rm -f `echo $(sqlsources) $(libmysqlsources) $(sqlstoragesources) $(storagesources) | sed "s;\.lo;.c;g"` \ $(top_srcdir)/linked_libmysqld_sources; \ rm -f client_settings.h diff --git a/storage/federated/plug.in b/storage/federated/plug.in index 81c56cb672f..23b607d699b 100644 --- a/storage/federated/plug.in +++ b/storage/federated/plug.in @@ -2,3 +2,4 @@ MYSQL_STORAGE_ENGINE(federated,,[Federated Storage Engine], [Connects to tables on remote MySQL servers], [max,max-no-ndb]) MYSQL_PLUGIN_STATIC(federated, [libfederated.a]) MYSQL_PLUGIN_DYNAMIC(federated, [ha_federated.la]) +MYSQL_PLUGIN_DEPENDS_ON_MYSQL_INTERNALS(federated, [ha_federated.cc]) diff --git a/storage/heap/plug.in b/storage/heap/plug.in index 9e744b6ac0d..50f31c60f2b 100644 --- a/storage/heap/plug.in +++ b/storage/heap/plug.in @@ -3,4 +3,5 @@ MYSQL_STORAGE_ENGINE(heap,no, [Memory Storage Engine], MYSQL_PLUGIN_DIRECTORY(heap, [storage/heap]) MYSQL_PLUGIN_STATIC(heap, [libheap.a]) MYSQL_PLUGIN_MANDATORY(heap) dnl Memory tables +MYSQL_PLUGIN_DEPENDS_ON_MYSQL_INTERNALS(heap, [ha_heap.cc]) diff --git a/storage/innobase/plug.in b/storage/innobase/plug.in index 028937882b2..9c21a491d9f 100644 --- a/storage/innobase/plug.in +++ b/storage/innobase/plug.in @@ -68,4 +68,5 @@ MYSQL_PLUGIN_ACTIONS(innobase, [ storage/innobase/handler/Makefile storage/innobase/usr/Makefile) ]) +MYSQL_PLUGIN_DEPENDS_ON_MYSQL_INTERNALS(innobase, [handler/ha_innodb.cc]) diff --git a/storage/myisam/plug.in b/storage/myisam/plug.in index 3160752182d..051ec2d54aa 100644 --- a/storage/myisam/plug.in +++ b/storage/myisam/plug.in @@ -3,4 +3,5 @@ MYSQL_STORAGE_ENGINE(myisam,no, [MyISAM Storage Engine], MYSQL_PLUGIN_DIRECTORY(myisam, [storage/myisam]) MYSQL_PLUGIN_STATIC(myisam, [libmyisam.a]) MYSQL_PLUGIN_MANDATORY(myisam) dnl Default +MYSQL_PLUGIN_DEPENDS_ON_MYSQL_INTERNALS(myisam, [ha_myisam.cc]) diff --git a/storage/myisammrg/plug.in b/storage/myisammrg/plug.in index b4b2af8d984..1f94e07d881 100644 --- a/storage/myisammrg/plug.in +++ b/storage/myisammrg/plug.in @@ -3,3 +3,4 @@ MYSQL_STORAGE_ENGINE(myisammrg,no,[MyISAM MERGE Engine], MYSQL_PLUGIN_DIRECTORY(myisammrg,[storage/myisammrg]) MYSQL_PLUGIN_STATIC(myisammrg, [libmyisammrg.a]) MYSQL_PLUGIN_MANDATORY(myisammrg) +MYSQL_PLUGIN_DEPENDS_ON_MYSQL_INTERNALS(myisammrg, [ha_myisammrg.cc]) From a2e0059f3c1cf647b1feaf80780a917476ae4bba Mon Sep 17 00:00:00 2001 From: "svoj@mysql.com/april.(none)" <> Date: Wed, 18 Oct 2006 17:57:29 +0500 Subject: [PATCH 46/60] BUG#23175 - MYISAM crash/repair failed during repair Repair table could crash a server if there is not sufficient memory (myisam_sort_buffer_size) to operate. Affects not only repair, but also all statements that use create index by sort: repair by sort, parallel repair, bulk insert. Return an error if there is not sufficient memory to store at least one key per BUFFPEK. Also fixed memory leak if thr_find_all_keys returns an error. --- myisam/sort.c | 8 ++++++-- mysql-test/r/repair.result | 28 ++++++++++++++++++++++++++++ mysql-test/t/repair.test | 28 ++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 2 deletions(-) diff --git a/myisam/sort.c b/myisam/sort.c index 727840709da..154d50d4d39 100644 --- a/myisam/sort.c +++ b/myisam/sort.c @@ -148,7 +148,8 @@ int _create_index_by_sort(MI_SORT_PARAM *info,my_bool no_messages, skr=maxbuffer; if (memavl < sizeof(BUFFPEK)*(uint) maxbuffer || (keys=(memavl-sizeof(BUFFPEK)*(uint) maxbuffer)/ - (sort_length+sizeof(char*))) <= 1) + (sort_length+sizeof(char*))) <= 1 || + keys < (uint) maxbuffer) { mi_check_print_error(info->sort_info->param, "sort_buffer_size is to small"); @@ -363,7 +364,8 @@ pthread_handler_decl(thr_find_all_keys,arg) skr=maxbuffer; if (memavl < sizeof(BUFFPEK)*maxbuffer || (keys=(memavl-sizeof(BUFFPEK)*maxbuffer)/ - (sort_length+sizeof(char*))) <= 1) + (sort_length+sizeof(char*))) <= 1 || + keys < (uint) maxbuffer) { mi_check_print_error(sort_param->sort_info->param, "sort_buffer_size is to small"); @@ -497,6 +499,8 @@ int thr_write_keys(MI_SORT_PARAM *sort_param) if (!sinfo->sort_keys) { got_error=1; + my_free(mi_get_rec_buff_ptr(info, sinfo->rec_buff), + MYF(MY_ALLOW_ZERO_PTR)); continue; } if (!got_error) diff --git a/mysql-test/r/repair.result b/mysql-test/r/repair.result index c069824e9f0..8b3782ec2a4 100644 --- a/mysql-test/r/repair.result +++ b/mysql-test/r/repair.result @@ -48,3 +48,31 @@ Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_par t1 1 a 1 a A 5 NULL NULL YES BTREE SET myisam_repair_threads=@@global.myisam_repair_threads; DROP TABLE t1; +CREATE TABLE t1(a CHAR(255), KEY(a)); +SET myisam_sort_buffer_size=4096; +INSERT INTO t1 VALUES +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'); +SET myisam_repair_threads=2; +REPAIR TABLE t1; +Table Op Msg_type Msg_text +test.t1 repair error sort_buffer_size is to small +test.t1 repair warning Number of rows changed from 0 to 157 +test.t1 repair status OK +SET myisam_repair_threads=@@global.myisam_repair_threads; +SET myisam_sort_buffer_size=@@global.myisam_sort_buffer_size; +DROP TABLE t1; diff --git a/mysql-test/t/repair.test b/mysql-test/t/repair.test index f086d5b0c2a..b192d5c0a64 100644 --- a/mysql-test/t/repair.test +++ b/mysql-test/t/repair.test @@ -45,4 +45,32 @@ SHOW INDEX FROM t1; SET myisam_repair_threads=@@global.myisam_repair_threads; DROP TABLE t1; +# +# BUG#23175 - MYISAM crash/repair failed during repair +# +CREATE TABLE t1(a CHAR(255), KEY(a)); +SET myisam_sort_buffer_size=4096; +INSERT INTO t1 VALUES +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), +('0'),('0'),('0'),('0'),('0'),('0'),('0'); +SET myisam_repair_threads=2; +REPAIR TABLE t1; +SET myisam_repair_threads=@@global.myisam_repair_threads; +SET myisam_sort_buffer_size=@@global.myisam_sort_buffer_size; +DROP TABLE t1; + # End of 4.1 tests From fc351658059b6cb142e3dfd0b76a69fd691b1b36 Mon Sep 17 00:00:00 2001 From: "holyfoot/hf@mysql.com/deer.(none)" <> Date: Wed, 18 Oct 2006 18:48:38 +0500 Subject: [PATCH 47/60] letter's case fixed --- config/ac-macros/plugins.m4 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/ac-macros/plugins.m4 b/config/ac-macros/plugins.m4 index 0d5a5b46114..d471bae1f25 100644 --- a/config/ac-macros/plugins.m4 +++ b/config/ac-macros/plugins.m4 @@ -345,9 +345,9 @@ AC_DEFUN([__MYSQL_EMIT_CHECK_PLUGIN],[ AC_MSG_CHECKING([whether to use ]$3) mysql_use_plugin_dir="" m4_ifdef([$10],[ - if test "x[$mysql_plugin_]$2" = Xyes -a \ - "x[$with_plugin_]$2" != Xno -o \ - "x[$with_plugin_]$2" = Xyes; then + if test "X[$mysql_plugin_]$2" = Xyes -a \ + "X[$with_plugin_]$2" != Xno -o \ + "X[$with_plugin_]$2" = Xyes; then AC_MSG_RESULT([error]) AC_MSG_ERROR([disabled]) fi From 3a4a9521d84292f8df21cdd484ac10c62087bb7b Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Wed, 18 Oct 2006 17:24:33 -0700 Subject: [PATCH 48/60] Changed test case for bug 22342 to make it platform independent. --- mysql-test/r/group_min_max.result | 6 +++--- mysql-test/t/group_min_max.test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/group_min_max.result b/mysql-test/r/group_min_max.result index 826e642ce88..0304919baf6 100644 --- a/mysql-test/r/group_min_max.result +++ b/mysql-test/r/group_min_max.result @@ -2143,10 +2143,10 @@ id2 id3 id5 id4 id3 id6 id5 id1 1 1 1 1 1 1 1 1 DROP TABLE t1,t2,t3,t4,t5,t6; CREATE TABLE t1 (a int, b int, PRIMARY KEY (a,b), KEY b (b)); -INSERT INTO t1 VALUES (1,1),(1,2); +INSERT INTO t1 VALUES (1,1),(1,2),(1,0),(1,3); explain SELECT MAX(b), a FROM t1 WHERE b < 2 AND a = 1 GROUP BY a; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range PRIMARY,b PRIMARY 8 NULL 2 Using where; Using index for group-by +1 SIMPLE t1 range PRIMARY,b PRIMARY 8 NULL 1 Using where; Using index for group-by SELECT MAX(b), a FROM t1 WHERE b < 2 AND a = 1 GROUP BY a; MAX(b) a 1 1 @@ -2157,7 +2157,7 @@ CREATE TABLE t2 (a int, b int, c int, PRIMARY KEY (a,b,c)); INSERT INTO t2 SELECT a,b,b FROM t1; explain SELECT MIN(c) FROM t2 WHERE b = 2 and a = 1 and c > 1 GROUP BY a; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t2 range PRIMARY PRIMARY 12 NULL 2 Using where; Using index for group-by +1 SIMPLE t2 range PRIMARY PRIMARY 12 NULL 1 Using where; Using index for group-by SELECT MIN(c) FROM t2 WHERE b = 2 and a = 1 and c > 1 GROUP BY a; MIN(c) 2 diff --git a/mysql-test/t/group_min_max.test b/mysql-test/t/group_min_max.test index 9f3fb5ea51e..08f0f54df60 100644 --- a/mysql-test/t/group_min_max.test +++ b/mysql-test/t/group_min_max.test @@ -799,7 +799,7 @@ DROP TABLE t1,t2,t3,t4,t5,t6; # Bug#22342: No results returned for query using max and group by # CREATE TABLE t1 (a int, b int, PRIMARY KEY (a,b), KEY b (b)); -INSERT INTO t1 VALUES (1,1),(1,2); +INSERT INTO t1 VALUES (1,1),(1,2),(1,0),(1,3); explain SELECT MAX(b), a FROM t1 WHERE b < 2 AND a = 1 GROUP BY a; SELECT MAX(b), a FROM t1 WHERE b < 2 AND a = 1 GROUP BY a; From 1ddf03dab136e73489b46509c800a4a06f27c010 Mon Sep 17 00:00:00 2001 From: "kent@mysql.com/c-544072d5.010-2112-6f72651.cust.bredbandsbolaget.se" <> Date: Thu, 19 Oct 2006 04:38:16 +0200 Subject: [PATCH 49/60] Makefile.am: Quote variable in "for"-loop head, in case it can be empty. --- libmysqld/Makefile.am | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am index 0de37db63e2..81da3daabc1 100644 --- a/libmysqld/Makefile.am +++ b/libmysqld/Makefile.am @@ -158,11 +158,11 @@ endif # This is called from the toplevel makefile link_sources: set -x; \ - for f in $(sqlsources); do \ + for f in "$(sqlsources)"; do \ rm -f $$f; \ @LN_CP_F@ $(top_srcdir)/sql/$$f $$f; \ done; \ - for f in $(libmysqlsources); do \ + for f in "$(libmysqlsources)"; do \ rm -f $$f; \ @LN_CP_F@ $(top_srcdir)/libmysql/$$f $$f; \ done; \ @@ -176,7 +176,7 @@ link_sources: if test -n "$(storagesources)" ; \ then \ rm -f $(storagesources); \ - for f in $(storagesourceslinks); do \ + for f in "$(storagesourceslinks)"; do \ @LN_CP_F@ $(top_srcdir)/$$f . ; \ done; \ fi; \ From ff25d2b71a5ba75652c44e8c650fe2abd1a272f6 Mon Sep 17 00:00:00 2001 From: "holyfoot/hf@mysql.com/deer.(none)" <> Date: Thu, 19 Oct 2006 16:39:24 +0500 Subject: [PATCH 50/60] libmysqld/Makefile.am fixed --- config/ac-macros/plugins.m4 | 10 +++++----- libmysqld/Makefile.am | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/config/ac-macros/plugins.m4 b/config/ac-macros/plugins.m4 index d471bae1f25..b022ab67045 100644 --- a/config/ac-macros/plugins.m4 +++ b/config/ac-macros/plugins.m4 @@ -373,11 +373,6 @@ AC_DEFUN([__MYSQL_EMIT_CHECK_PLUGIN],[ ]) AC_SUBST([plugin_]$2[_shared_target], "$8") AC_SUBST([plugin_]$2[_static_target], [""]) - m4_ifdef([$11],[ - condition_dependent_plugin_modules="$condition_dependent_plugin_modules m4_bregexp($11, [[^/]+$], [\&])" - condition_dependent_plugin_links="$condition_dependent_plugin_links $6/$11" - condition_dependent_plugin_includes="$condition_dependent_plugin_includes -I[\$(top_srcdir)]/$6/m4_bregexp($11, [^.+[/$]], [\&])" - ]) [with_plugin_]$2=yes AC_MSG_RESULT([plugin]) m4_ifdef([$6],[ @@ -434,6 +429,11 @@ dnl Although this is "pretty", it breaks libmysqld build mysql_plugin_defs="$mysql_plugin_defs, [builtin_]$2[_plugin]" [with_plugin_]$2=yes AC_MSG_RESULT([yes]) + m4_ifdef([$11],[ + condition_dependent_plugin_modules="$condition_dependent_plugin_modules m4_bregexp($11, [[^/]+$], [\&])" + condition_dependent_plugin_links="$condition_dependent_plugin_links $6/$11" + condition_dependent_plugin_includes="$condition_dependent_plugin_includes -I[\$(top_srcdir)]/$6/m4_bregexp($11, [^.+[/$]], [\&])" + ]) fi m4_ifdef([$6],[ if test -n "$mysql_use_plugin_dir" ; then diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am index 81da3daabc1..0de37db63e2 100644 --- a/libmysqld/Makefile.am +++ b/libmysqld/Makefile.am @@ -158,11 +158,11 @@ endif # This is called from the toplevel makefile link_sources: set -x; \ - for f in "$(sqlsources)"; do \ + for f in $(sqlsources); do \ rm -f $$f; \ @LN_CP_F@ $(top_srcdir)/sql/$$f $$f; \ done; \ - for f in "$(libmysqlsources)"; do \ + for f in $(libmysqlsources); do \ rm -f $$f; \ @LN_CP_F@ $(top_srcdir)/libmysql/$$f $$f; \ done; \ @@ -176,7 +176,7 @@ link_sources: if test -n "$(storagesources)" ; \ then \ rm -f $(storagesources); \ - for f in "$(storagesourceslinks)"; do \ + for f in $(storagesourceslinks); do \ @LN_CP_F@ $(top_srcdir)/$$f . ; \ done; \ fi; \ From 9b3e7bd02fc0a8f4cc9cb7b2d1cecb1aaf106dc7 Mon Sep 17 00:00:00 2001 From: "istruewing@chilla.local" <> Date: Thu, 19 Oct 2006 13:42:26 +0200 Subject: [PATCH 51/60] Bug#21476 - Lost Database Connection During Query Backport from 5.1. Raised STACK_MIN_SIZE for Debian GNU/Linux Sid, Linux kernel 2.6.16, gcc version 3.3.6 (Debian 1:3.3.6-13), libc6-dbg 2.3.6.ds1-4, Pentium4 (x86), BUILD/compile-pentium-debug-max Raised about 100 Bytes above the required minimum. --- sql/mysql_priv.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 381f2a989b5..48d638bee9c 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -141,7 +141,7 @@ MY_LOCALE *my_locale_by_name(const char *name); Feel free to raise this by the smallest amount you can to get the "execution_constants" test to pass. */ -#define STACK_MIN_SIZE 9336 // Abort if less stack during eval. +#define STACK_MIN_SIZE 10788 // Abort if less stack during eval. #define STACK_MIN_SIZE_FOR_OPEN 1024*80 #define STACK_BUFF_ALLOC 256 // For stack overrun checks From afefa5d42f54028a69e11fd155a0faa4fcba7399 Mon Sep 17 00:00:00 2001 From: "svoj@mysql.com/april.(none)" <> Date: Thu, 19 Oct 2006 18:48:37 +0500 Subject: [PATCH 52/60] After merge fix. --- mysql-test/r/repair.result | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mysql-test/r/repair.result b/mysql-test/r/repair.result index 16d2a9069b5..54d53299743 100644 --- a/mysql-test/r/repair.result +++ b/mysql-test/r/repair.result @@ -78,6 +78,9 @@ INSERT INTO t1 VALUES ('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), ('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'),('0'), ('0'),('0'),('0'),('0'),('0'),('0'),('0'); +Warnings: +Error 1034 sort_buffer_size is to small +Error 1034 Number of rows changed from 0 to 157 SET myisam_repair_threads=2; REPAIR TABLE t1; Table Op Msg_type Msg_text From bef920e08e9cbd18a72e7cc7e33b1d03fd09e958 Mon Sep 17 00:00:00 2001 From: "andrey@example.com" <> Date: Thu, 19 Oct 2006 15:56:37 +0200 Subject: [PATCH 53/60] Fix small glitch during parsing of ALTER EVENT xyz with only COMMENT clause. Strangely it has manifestated itself only on two platforms. This is a fix for bug#23423 "Syntax error for "ALTER EVENT ... COMMENT ..." on just two platforms" --- sql/sql_yacc.yy | 1 + 1 file changed, 1 insertion(+) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index ab43acde2a5..ee7d9f20b94 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1421,6 +1421,7 @@ opt_ev_comment: /* empty */ { $$= 0; } | COMMENT_SYM TEXT_STRING_sys { Lex->comment= Lex->event_parse_data->comment= $2; + $$= 1; } ; From 26bf6a25c882a5d249cf37c8c65ff2e6b05a1252 Mon Sep 17 00:00:00 2001 From: "svoj@mysql.com/april.(none)" <> Date: Thu, 19 Oct 2006 20:29:12 +0500 Subject: [PATCH 54/60] After merge fix. --- mysql-test/r/repair.result | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/repair.result b/mysql-test/r/repair.result index e77d5c6bc92..1269c4c85a9 100644 --- a/mysql-test/r/repair.result +++ b/mysql-test/r/repair.result @@ -52,12 +52,6 @@ Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_par t1 1 a 1 a A 5 NULL NULL YES BTREE SET myisam_repair_threads=@@global.myisam_repair_threads; DROP TABLE t1; -DROP TABLE IF EXISTS tt1; -CREATE TEMPORARY TABLE tt1 (c1 INT); -REPAIR TABLE tt1 USE_FRM; -Table Op Msg_type Msg_text -tt1 repair error Cannot repair temporary table from .frm file -DROP TABLE tt1; CREATE TABLE t1(a INT); USE mysql; REPAIR TABLE test.t1 USE_FRM; @@ -96,3 +90,9 @@ test.t1 repair status OK SET myisam_repair_threads=@@global.myisam_repair_threads; SET myisam_sort_buffer_size=@@global.myisam_sort_buffer_size; DROP TABLE t1; +DROP TABLE IF EXISTS tt1; +CREATE TEMPORARY TABLE tt1 (c1 INT); +REPAIR TABLE tt1 USE_FRM; +Table Op Msg_type Msg_text +tt1 repair error Cannot repair temporary table from .frm file +DROP TABLE tt1; From a0c0bbd8ae0725bd51178cb55dad72ca4dc89904 Mon Sep 17 00:00:00 2001 From: "gkodinov@dl145s.mysql.com" <> Date: Fri, 20 Oct 2006 10:12:38 +0200 Subject: [PATCH 55/60] sql_rename.cc, sql_table.cc, type_decimal.result, mix2_myisam.result: merge fixes --- mysql-test/r/mix2_myisam.result | 16 ++++++++-------- mysql-test/r/type_decimal.result | 6 +++--- sql/sql_rename.cc | 9 +++++---- sql/sql_table.cc | 8 +++++--- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/mysql-test/r/mix2_myisam.result b/mysql-test/r/mix2_myisam.result index 9a7d71820f8..45b4784251a 100644 --- a/mysql-test/r/mix2_myisam.result +++ b/mysql-test/r/mix2_myisam.result @@ -1611,15 +1611,15 @@ i 10 select sql_big_result v,count(c) from t1 group by v limit 10; v count(c) a 1 -a 10 -b 10 -c 10 -d 10 -e 10 -f 10 -g 10 +a 10 +b 10 +c 10 +d 10 +e 10 +f 10 +g 10 h 10 -i 10 +i 10 select c,count(*) from t1 group by c limit 10; c count(*) a 1 diff --git a/mysql-test/r/type_decimal.result b/mysql-test/r/type_decimal.result index 0840872a7a9..dfbd6619436 100644 --- a/mysql-test/r/type_decimal.result +++ b/mysql-test/r/type_decimal.result @@ -786,7 +786,7 @@ select group_concat(t) from t1 group by week(date)/10; group_concat(t) t Warnings: -Warning 1292 Truncated incorrect datetime value: '0000-00-00' -Warning 1292 Truncated incorrect datetime value: '0000-00-00' -Warning 1292 Truncated incorrect datetime value: '0000-00-00' +Warning 1292 Incorrect datetime value: '0000-00-00' +Warning 1292 Incorrect datetime value: '0000-00-00' +Warning 1292 Incorrect datetime value: '0000-00-00' drop table t1; diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index 8b04345640b..bf2e2d506cd 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -165,7 +165,7 @@ do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name, else { old_alias= ren_table->table_name; - new_alias= new_table_table_name; + new_alias= new_table_name; } build_table_filename(name, sizeof(name), new_db, new_alias, reg_ext, 0); @@ -182,8 +182,10 @@ do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name, { case FRMTYPE_TABLE: { - if (!(rc= mysql_rename_table(table_type, ren_table->db, old_alias, - new_db, new_alias))) + if (!(rc= mysql_rename_table(ha_resolve_by_legacy_type(thd, + table_type), + ren_table->db, old_alias, + new_db, new_alias, 0))) { if ((rc= Table_triggers_list::change_table_name(thd, ren_table->db, old_alias, @@ -204,7 +206,6 @@ do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name, } } break; - } case FRMTYPE_VIEW: /* change of schema is not allowed */ if (strcmp(ren_table->db, new_db)) diff --git a/sql/sql_table.cc b/sql/sql_table.cc index d2f108cb8f2..c024ee8ddbe 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -5151,7 +5151,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, char reg_path[FN_REFLEN+1]; ha_rows copied,deleted; uint db_create_options, used_fields; - handlerton *old_db_type, *new_db_type, table_type; + handlerton *old_db_type, *new_db_type; + legacy_db_type table_type; HA_CREATE_INFO *create_info; frm_type_enum frm_type; uint need_copy_table= 0; @@ -5235,8 +5236,9 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, if (alter_info->tablespace_op != NO_TABLESPACE_OP) DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list, alter_info->tablespace_op)); - sprintf(new_name_buff,"%s/%s/%s%s",mysql_data_home, db, table_name, reg_ext); - unpack_filename(new_name_buff, new_name_buff); + strxnmov(new_name_buff, sizeof (new_name_buff) - 1, mysql_data_home, "/", db, + "/", table_name, reg_ext, NullS); + (void) unpack_filename(new_name_buff, new_name_buff); if (lower_case_table_names != 2) my_casedn_str(files_charset_info, new_name_buff); frm_type= mysql_frm_type(thd, new_name_buff, &table_type); From 60104e1132a5b382f0bc2fcb66a8a82728ac8eb8 Mon Sep 17 00:00:00 2001 From: "kent@mysql.com/c-544072d5.010-2112-6f72651.cust.bredbandsbolaget.se" <> Date: Fri, 20 Oct 2006 20:18:44 +0200 Subject: [PATCH 56/60] make_win_bin_dist: Aligned script with updated 5.0 version --- scripts/make_win_bin_dist | 403 ++++++++++++++++++++++++++++++-------- 1 file changed, 321 insertions(+), 82 deletions(-) diff --git a/scripts/make_win_bin_dist b/scripts/make_win_bin_dist index cebcccb56f4..b200e5428be 100755 --- a/scripts/make_win_bin_dist +++ b/scripts/make_win_bin_dist @@ -1,41 +1,227 @@ -#! /bin/sh +#!/bin/sh -NOINST_NAME=$1 +# Exit if failing to copy, we want exact specifications, not +# just "what happen to be built". +set -e -mkdir $NOINST_NAME -mkdir $NOINST_NAME/bin -cp client/release/*.exe $NOINST_NAME/bin/ -cp extra/release/*.exe $NOINST_NAME/bin/ -mv $NOINST_NAME/bin/comp_err.exe $NOINST_NAME/bin/comp-err.exe -cp storage/myisam/release/*.exe $NOINST_NAME/bin/ -cp server-tools/instance-manager/release/*.exe $NOINST_NAME/bin/ -cp tests/release/*.exe $NOINST_NAME/bin/ -cp libmysql/release/*.exe $NOINST_NAME/bin/ -cp libmysql/release/libmysql.dll $NOINST_NAME/bin/ +# ---------------------------------------------------------------------- +# Read first argument that is the base name of the resulting TAR file. +# See usage() function below for a description on the arguments. +# +# NOTE: We will read the rest of the command line later on. +# NOTE: Pattern matching with "{..,..}" can't be used, not portable. +# ---------------------------------------------------------------------- -cp sql/release/mysqld.exe $NOINST_NAME/bin/mysqld.exe -cp sql/debug/mysqld.exe $NOINST_NAME/bin/mysqld-debug.exe -# For Pro/Classic builds, do this instead: -# cp sql/release/mysqld.exe $NOINST_NAME/bin/mysqld-nt.exe -# cp sql/debug/mysqld.exe $NOINST_NAME/bin/mysqld-debug.exe +# FIXME FIXME "debug", own build or handled here? +# FIXME FIXME add way to copy from other builds executables -cp COPYING EXCEPTIONS-CLIENT $NOINST_NAME/ -cp -dpR win/data $NOINST_NAME/data -mkdir $NOINST_NAME/Docs -cp Docs/INSTALL-BINARY Docs/manual.chm ChangeLog COPYING $NOINST_NAME/Docs/ +usage() +{ + echo < Date: Mon, 23 Oct 2006 09:48:36 +0200 Subject: [PATCH 57/60] 5.1.12 - release clone ndb - revert fix bug#21052 as it's wrong, and induces bus-error on node crashes --- storage/ndb/src/mgmsrv/Services.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/storage/ndb/src/mgmsrv/Services.cpp b/storage/ndb/src/mgmsrv/Services.cpp index 4a84095aa9f..80dd040eb1b 100644 --- a/storage/ndb/src/mgmsrv/Services.cpp +++ b/storage/ndb/src/mgmsrv/Services.cpp @@ -1368,14 +1368,8 @@ Ndb_mgmd_event_service::log(int eventType, const Uint32* theData, NodeId nodeId) if (ndb_logevent_body[i].index_fn) val= (*(ndb_logevent_body[i].index_fn))(val); str.appfmt("%s=%d\n",ndb_logevent_body[i].token, val); - if(strcmp(ndb_logevent_body[i].token,"error") == 0) - { - int m_text_len= strlen(m_text); - snprintf(m_text+m_text_len, 4 , " - "); - ndb_error_string(theData[3], m_text+(m_text_len+3), sizeof(m_text)-m_text_len-3); - } } - + Vector copy; m_clients.lock(); for(i = m_clients.size() - 1; i >= 0; i--) From da721fe7aa87df01283b3550704f9b854aac9e7f Mon Sep 17 00:00:00 2001 From: "mmj@tiger.local[mmj]" <> Date: Tue, 24 Oct 2006 19:05:11 +0200 Subject: [PATCH 58/60] Bug #23427: incompatible ABI change in 5.0.26? Revert 1 June change enough to restore ABI compatibility with previous versions. --- include/mysql.h | 6 ------ libmysqld/lib_sql.cc | 1 - 2 files changed, 7 deletions(-) diff --git a/include/mysql.h b/include/mysql.h index ae4a8222c5b..8ef3f1273ec 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -293,12 +293,6 @@ typedef struct st_mysql /* needed for embedded server - no net buffer to store the 'info' */ char *info_buffer; #endif - /* - In embedded server it points to the statement that is processed - in the current query. We store some results directly in statement - fields then. - */ - struct st_mysql_stmt *current_stmt; } MYSQL; typedef struct st_mysql_res { diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc index 5ac2c163c4e..b5efd4a82af 100644 --- a/libmysqld/lib_sql.cc +++ b/libmysqld/lib_sql.cc @@ -100,7 +100,6 @@ emb_advanced_command(MYSQL *mysql, enum enum_server_command command, mysql->affected_rows= ~(my_ulonglong) 0; mysql->field_count= 0; net->last_errno= 0; - mysql->current_stmt= stmt; thd->store_globals(); // Fix if more than one connect /* From 0cbb309b1a5f70a586f3bb5497beeef0604e1f2b Mon Sep 17 00:00:00 2001 From: "jonas@perch.ndb.mysql.com" <> Date: Fri, 27 Oct 2006 16:32:59 +0200 Subject: [PATCH 59/60] ndb - valgrind Still leakage, make sure all unlinked operations are put back so they will be release (on failing blob operations, when AO_IgnoreError) --- ndb/src/ndbapi/NdbConnection.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/ndb/src/ndbapi/NdbConnection.cpp b/ndb/src/ndbapi/NdbConnection.cpp index e061740c9f6..d5fa8f77db2 100644 --- a/ndb/src/ndbapi/NdbConnection.cpp +++ b/ndb/src/ndbapi/NdbConnection.cpp @@ -379,7 +379,29 @@ NdbConnection::execute(ExecType aTypeOfExec, * operations, making postExecute impossible */ if (abortOption == AO_IgnoreError) + { + if (theCompletedFirstOp != NULL) + { + if (tCompletedFirstOp != NULL) + { + tCompletedLastOp->next(theCompletedFirstOp); + theCompletedFirstOp = tCompletedFirstOp; + } + } + else + { + theCompletedFirstOp = tCompletedFirstOp; + theCompletedLastOp = tCompletedLastOp; + } + if (tPrepOp != NULL && tRestOp != NULL) { + if (theFirstOpInList == NULL) + theFirstOpInList = tRestOp; + else + theLastOpInList->next(tRestOp); + theLastOpInList = tLastOp; + } DBUG_RETURN(-1); + } } #ifdef ndb_api_crash_on_complex_blob_abort From c6308d652dac6790199170f3ccc9a8fb0f610a4e Mon Sep 17 00:00:00 2001 From: "jonas@perch.ndb.mysql.com" <> Date: Tue, 31 Oct 2006 16:09:31 +0100 Subject: [PATCH 60/60] ndb - ndb_multi spurious failure remove timeing dependant part of test --- mysql-test/r/ndb_multi.result | 3 --- mysql-test/t/ndb_multi.test | 1 - 2 files changed, 4 deletions(-) diff --git a/mysql-test/r/ndb_multi.result b/mysql-test/r/ndb_multi.result index dc80b0ca1ce..5d75722982f 100644 --- a/mysql-test/r/ndb_multi.result +++ b/mysql-test/r/ndb_multi.result @@ -28,9 +28,6 @@ Handler_discover 0 drop table t1; create table t1 (a int) engine=ndbcluster; insert into t1 value (2); -select * from t1; -a -2 flush table t1; select * from t1; a diff --git a/mysql-test/t/ndb_multi.test b/mysql-test/t/ndb_multi.test index 5fb964facdf..3bc735b60d4 100644 --- a/mysql-test/t/ndb_multi.test +++ b/mysql-test/t/ndb_multi.test @@ -37,7 +37,6 @@ drop table t1; create table t1 (a int) engine=ndbcluster; insert into t1 value (2); connection server1; -select * from t1; flush table t1; select * from t1;