From 272bb6b5856a7f033e6826ba5a96b1ca52aabb12 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 8 Oct 2007 12:46:38 +0500 Subject: [PATCH 01/19] Bug#27580 SPACE() function collation bug? Problem: when character_set_connection=utf8, mixing SPACE() with a non-Unicode column (e.g. for concat) produced "illegal mix of collations" error. Fix: Item_string() corresponding to space character is now created using "ASCII" repertoire. Previously it was incorrectly created using "UNICODE" repertoure, which didn't allow to convert results of SPACE() to a non-Unicode character set. mysql-test/include/ctype_common.inc: - Adding test for bug#27580 - Restoring previous values of character_set_client and character_set_results, because ctype_common.inc now changes them when doing "set names utf8" in the test for bug#27580 mysql-test/r/ctype_big5.result: Adding test mysql-test/r/ctype_cp1250_ch.result: Adding test mysql-test/r/ctype_euckr.result: Adding test mysql-test/r/ctype_gb2312.result: Adding test mysql-test/r/ctype_gbk.result: Adding test mysql-test/r/ctype_uca.result: Adding test mysql-test/r/ctype_ucs.result: Adding test mysql-test/t/ctype_cp1250_ch.test: Adding test mysql-test/t/ctype_ucs.test: Adding test sql/item_create.cc: Item for SQL function SPACE() is now created with ASCII repertoire, to allow automatic conversion from UTF8 to column's character set e.g. for CONCAT(). --- mysql-test/include/ctype_common.inc | 16 +++ mysql-test/r/ctype_big5.result | 19 ++++ mysql-test/r/ctype_cp1250_ch.result | 152 ++++++++++++++++++++++++++++ mysql-test/r/ctype_euckr.result | 19 ++++ mysql-test/r/ctype_gb2312.result | 19 ++++ mysql-test/r/ctype_gbk.result | 19 ++++ mysql-test/r/ctype_uca.result | 19 ++++ mysql-test/r/ctype_ucs.result | 76 ++++++++++++++ mysql-test/t/ctype_cp1250_ch.test | 10 ++ mysql-test/t/ctype_ucs.test | 4 + sql/item_create.cc | 4 +- 11 files changed, 355 insertions(+), 2 deletions(-) diff --git a/mysql-test/include/ctype_common.inc b/mysql-test/include/ctype_common.inc index 202c508a9c9..b9bd7869967 100644 --- a/mysql-test/include/ctype_common.inc +++ b/mysql-test/include/ctype_common.inc @@ -13,6 +13,8 @@ SET @safe_character_set_server= @@character_set_server; SET @safe_collation_server= @@collation_server; +SET @safe_character_set_client= @@character_set_client; +SET @safe_character_set_results= @@character_set_results; SET character_set_server= @test_character_set; SET collation_server= @test_collation; CREATE DATABASE d1; @@ -51,8 +53,22 @@ SELECT c1 as want1result from t1 where c1 like 'locatio%'; SELECT c1 as want1result from t1 where c1 like 'location%'; DROP TABLE t1; +# +# Bug#27580 SPACE() function collation bug? +# +set names utf8; +create table t1 ( + name varchar(10), + level smallint unsigned); +show create table t1; +insert into t1 values ('string',1); +select concat(name,space(level)), concat(name, repeat(' ',level)) from t1; +drop table t1; + DROP DATABASE d1; # Restore settings USE test; SET character_set_server= @safe_character_set_server; SET collation_server= @safe_collation_server; +SET character_set_client= @safe_character_set_client; +SET character_set_results= @safe_character_set_results; diff --git a/mysql-test/r/ctype_big5.result b/mysql-test/r/ctype_big5.result index 6d318a445f5..75b231dbe60 100644 --- a/mysql-test/r/ctype_big5.result +++ b/mysql-test/r/ctype_big5.result @@ -3,6 +3,8 @@ SET @test_character_set= 'big5'; SET @test_collation= 'big5_chinese_ci'; SET @safe_character_set_server= @@character_set_server; SET @safe_collation_server= @@collation_server; +SET @safe_character_set_client= @@character_set_client; +SET @safe_character_set_results= @@character_set_results; SET character_set_server= @test_character_set; SET collation_server= @test_collation; CREATE DATABASE d1; @@ -52,10 +54,27 @@ SELECT c1 as want1result from t1 where c1 like 'location%'; want1result location DROP TABLE t1; +set names utf8; +create table t1 ( +name varchar(10), +level smallint unsigned); +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `name` varchar(10) default NULL, + `level` smallint(5) unsigned default NULL +) ENGINE=MyISAM DEFAULT CHARSET=big5 +insert into t1 values ('string',1); +select concat(name,space(level)), concat(name, repeat(' ',level)) from t1; +concat(name,space(level)) concat(name, repeat(' ',level)) +string string +drop table t1; DROP DATABASE d1; USE test; SET character_set_server= @safe_character_set_server; SET collation_server= @safe_collation_server; +SET character_set_client= @safe_character_set_client; +SET character_set_results= @safe_character_set_results; SET NAMES big5; SET collation_connection='big5_chinese_ci'; create table t1 select repeat('a',4000) a; diff --git a/mysql-test/r/ctype_cp1250_ch.result b/mysql-test/r/ctype_cp1250_ch.result index 73f415732cd..3f22933e260 100644 --- a/mysql-test/r/ctype_cp1250_ch.result +++ b/mysql-test/r/ctype_cp1250_ch.result @@ -2,6 +2,158 @@ DROP TABLE IF EXISTS t1; SHOW COLLATION LIKE 'cp1250_czech_cs'; Collation Charset Id Default Compiled Sortlen cp1250_czech_cs cp1250 34 Yes 2 +SET @test_character_set= 'cp1250'; +SET @test_collation= 'cp1250_general_ci'; +SET @safe_character_set_server= @@character_set_server; +SET @safe_collation_server= @@collation_server; +SET @safe_character_set_client= @@character_set_client; +SET @safe_character_set_results= @@character_set_results; +SET character_set_server= @test_character_set; +SET collation_server= @test_collation; +CREATE DATABASE d1; +USE d1; +CREATE TABLE t1 (c CHAR(10), KEY(c)); +SHOW FULL COLUMNS FROM t1; +Field Type Collation Null Key Default Extra Privileges Comment +c char(10) cp1250_general_ci YES MUL NULL +INSERT INTO t1 VALUES ('aaa'),('aaaa'),('aaaaa'); +SELECT c as want3results FROM t1 WHERE c LIKE 'aaa%'; +want3results +aaa +aaaa +aaaaa +DROP TABLE t1; +CREATE TABLE t1 (c1 varchar(15), KEY c1 (c1(2))); +SHOW FULL COLUMNS FROM t1; +Field Type Collation Null Key Default Extra Privileges Comment +c1 varchar(15) cp1250_general_ci YES MUL NULL +INSERT INTO t1 VALUES ('location'),('loberge'),('lotre'),('boabab'); +SELECT c1 as want3results from t1 where c1 like 'l%'; +want3results +location +loberge +lotre +SELECT c1 as want3results from t1 where c1 like 'lo%'; +want3results +location +loberge +lotre +SELECT c1 as want1result from t1 where c1 like 'loc%'; +want1result +location +SELECT c1 as want1result from t1 where c1 like 'loca%'; +want1result +location +SELECT c1 as want1result from t1 where c1 like 'locat%'; +want1result +location +SELECT c1 as want1result from t1 where c1 like 'locati%'; +want1result +location +SELECT c1 as want1result from t1 where c1 like 'locatio%'; +want1result +location +SELECT c1 as want1result from t1 where c1 like 'location%'; +want1result +location +DROP TABLE t1; +set names utf8; +create table t1 ( +name varchar(10), +level smallint unsigned); +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `name` varchar(10) default NULL, + `level` smallint(5) unsigned default NULL +) ENGINE=MyISAM DEFAULT CHARSET=cp1250 +insert into t1 values ('string',1); +select concat(name,space(level)), concat(name, repeat(' ',level)) from t1; +concat(name,space(level)) concat(name, repeat(' ',level)) +string string +drop table t1; +DROP DATABASE d1; +USE test; +SET character_set_server= @safe_character_set_server; +SET collation_server= @safe_collation_server; +SET character_set_client= @safe_character_set_client; +SET character_set_results= @safe_character_set_results; +SET @test_character_set= 'cp1250'; +SET @test_collation= 'cp1250_czech_cs'; +SET @safe_character_set_server= @@character_set_server; +SET @safe_collation_server= @@collation_server; +SET @safe_character_set_client= @@character_set_client; +SET @safe_character_set_results= @@character_set_results; +SET character_set_server= @test_character_set; +SET collation_server= @test_collation; +CREATE DATABASE d1; +USE d1; +CREATE TABLE t1 (c CHAR(10), KEY(c)); +SHOW FULL COLUMNS FROM t1; +Field Type Collation Null Key Default Extra Privileges Comment +c char(10) cp1250_czech_cs YES MUL NULL +INSERT INTO t1 VALUES ('aaa'),('aaaa'),('aaaaa'); +SELECT c as want3results FROM t1 WHERE c LIKE 'aaa%'; +want3results +aaa +aaaa +aaaaa +DROP TABLE t1; +CREATE TABLE t1 (c1 varchar(15), KEY c1 (c1(2))); +SHOW FULL COLUMNS FROM t1; +Field Type Collation Null Key Default Extra Privileges Comment +c1 varchar(15) cp1250_czech_cs YES MUL NULL +INSERT INTO t1 VALUES ('location'),('loberge'),('lotre'),('boabab'); +SELECT c1 as want3results from t1 where c1 like 'l%'; +want3results +location +loberge +lotre +SELECT c1 as want3results from t1 where c1 like 'lo%'; +want3results +location +loberge +lotre +SELECT c1 as want1result from t1 where c1 like 'loc%'; +want1result +location +SELECT c1 as want1result from t1 where c1 like 'loca%'; +want1result +location +SELECT c1 as want1result from t1 where c1 like 'locat%'; +want1result +location +SELECT c1 as want1result from t1 where c1 like 'locati%'; +want1result +location +SELECT c1 as want1result from t1 where c1 like 'locatio%'; +want1result +location +SELECT c1 as want1result from t1 where c1 like 'location%'; +want1result +location +DROP TABLE t1; +set names utf8; +create table t1 ( +name varchar(10), +level smallint unsigned); +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `name` varchar(10) collate cp1250_czech_cs default NULL, + `level` smallint(5) unsigned default NULL +) ENGINE=MyISAM DEFAULT CHARSET=cp1250 COLLATE=cp1250_czech_cs +insert into t1 values ('string',1); +select concat(name,space(level)), concat(name, repeat(' ',level)) from t1; +concat(name,space(level)) concat(name, repeat(' ',level)) +string string +drop table t1; +DROP DATABASE d1; +USE test; +SET character_set_server= @safe_character_set_server; +SET collation_server= @safe_collation_server; +SET character_set_client= @safe_character_set_client; +SET character_set_results= @safe_character_set_results; CREATE TABLE t1 (a char(16)) character set cp1250 collate cp1250_czech_cs; INSERT INTO t1 VALUES (''); SELECT a, length(a), a='', a=' ', a=' ' FROM t1; diff --git a/mysql-test/r/ctype_euckr.result b/mysql-test/r/ctype_euckr.result index 57e3e2ed8f8..3409c278847 100644 --- a/mysql-test/r/ctype_euckr.result +++ b/mysql-test/r/ctype_euckr.result @@ -3,6 +3,8 @@ SET @test_character_set= 'euckr'; SET @test_collation= 'euckr_korean_ci'; SET @safe_character_set_server= @@character_set_server; SET @safe_collation_server= @@collation_server; +SET @safe_character_set_client= @@character_set_client; +SET @safe_character_set_results= @@character_set_results; SET character_set_server= @test_character_set; SET collation_server= @test_collation; CREATE DATABASE d1; @@ -52,10 +54,27 @@ SELECT c1 as want1result from t1 where c1 like 'location%'; want1result location DROP TABLE t1; +set names utf8; +create table t1 ( +name varchar(10), +level smallint unsigned); +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `name` varchar(10) default NULL, + `level` smallint(5) unsigned default NULL +) ENGINE=MyISAM DEFAULT CHARSET=euckr +insert into t1 values ('string',1); +select concat(name,space(level)), concat(name, repeat(' ',level)) from t1; +concat(name,space(level)) concat(name, repeat(' ',level)) +string string +drop table t1; DROP DATABASE d1; USE test; SET character_set_server= @safe_character_set_server; SET collation_server= @safe_collation_server; +SET character_set_client= @safe_character_set_client; +SET character_set_results= @safe_character_set_results; SET NAMES euckr; SET collation_connection='euckr_korean_ci'; create table t1 select repeat('a',4000) a; diff --git a/mysql-test/r/ctype_gb2312.result b/mysql-test/r/ctype_gb2312.result index 314c336bab9..234201f28d0 100644 --- a/mysql-test/r/ctype_gb2312.result +++ b/mysql-test/r/ctype_gb2312.result @@ -3,6 +3,8 @@ SET @test_character_set= 'gb2312'; SET @test_collation= 'gb2312_chinese_ci'; SET @safe_character_set_server= @@character_set_server; SET @safe_collation_server= @@collation_server; +SET @safe_character_set_client= @@character_set_client; +SET @safe_character_set_results= @@character_set_results; SET character_set_server= @test_character_set; SET collation_server= @test_collation; CREATE DATABASE d1; @@ -52,10 +54,27 @@ SELECT c1 as want1result from t1 where c1 like 'location%'; want1result location DROP TABLE t1; +set names utf8; +create table t1 ( +name varchar(10), +level smallint unsigned); +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `name` varchar(10) default NULL, + `level` smallint(5) unsigned default NULL +) ENGINE=MyISAM DEFAULT CHARSET=gb2312 +insert into t1 values ('string',1); +select concat(name,space(level)), concat(name, repeat(' ',level)) from t1; +concat(name,space(level)) concat(name, repeat(' ',level)) +string string +drop table t1; DROP DATABASE d1; USE test; SET character_set_server= @safe_character_set_server; SET collation_server= @safe_collation_server; +SET character_set_client= @safe_character_set_client; +SET character_set_results= @safe_character_set_results; SET NAMES gb2312; SET collation_connection='gb2312_chinese_ci'; create table t1 select repeat('a',4000) a; diff --git a/mysql-test/r/ctype_gbk.result b/mysql-test/r/ctype_gbk.result index 3f5d8b0d8c6..d78e3dd3097 100644 --- a/mysql-test/r/ctype_gbk.result +++ b/mysql-test/r/ctype_gbk.result @@ -3,6 +3,8 @@ SET @test_character_set= 'gbk'; SET @test_collation= 'gbk_chinese_ci'; SET @safe_character_set_server= @@character_set_server; SET @safe_collation_server= @@collation_server; +SET @safe_character_set_client= @@character_set_client; +SET @safe_character_set_results= @@character_set_results; SET character_set_server= @test_character_set; SET collation_server= @test_collation; CREATE DATABASE d1; @@ -52,10 +54,27 @@ SELECT c1 as want1result from t1 where c1 like 'location%'; want1result location DROP TABLE t1; +set names utf8; +create table t1 ( +name varchar(10), +level smallint unsigned); +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `name` varchar(10) default NULL, + `level` smallint(5) unsigned default NULL +) ENGINE=MyISAM DEFAULT CHARSET=gbk +insert into t1 values ('string',1); +select concat(name,space(level)), concat(name, repeat(' ',level)) from t1; +concat(name,space(level)) concat(name, repeat(' ',level)) +string string +drop table t1; DROP DATABASE d1; USE test; SET character_set_server= @safe_character_set_server; SET collation_server= @safe_collation_server; +SET character_set_client= @safe_character_set_client; +SET character_set_results= @safe_character_set_results; SET NAMES gbk; SET collation_connection='gbk_chinese_ci'; create table t1 select repeat('a',4000) a; diff --git a/mysql-test/r/ctype_uca.result b/mysql-test/r/ctype_uca.result index 889702e380c..4fe1eb29b19 100644 --- a/mysql-test/r/ctype_uca.result +++ b/mysql-test/r/ctype_uca.result @@ -2538,6 +2538,8 @@ SET @test_character_set= 'utf8'; SET @test_collation= 'utf8_swedish_ci'; SET @safe_character_set_server= @@character_set_server; SET @safe_collation_server= @@collation_server; +SET @safe_character_set_client= @@character_set_client; +SET @safe_character_set_results= @@character_set_results; SET character_set_server= @test_character_set; SET collation_server= @test_collation; CREATE DATABASE d1; @@ -2587,10 +2589,27 @@ SELECT c1 as want1result from t1 where c1 like 'location%'; want1result location DROP TABLE t1; +set names utf8; +create table t1 ( +name varchar(10), +level smallint unsigned); +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `name` varchar(10) collate utf8_swedish_ci default NULL, + `level` smallint(5) unsigned default NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_swedish_ci +insert into t1 values ('string',1); +select concat(name,space(level)), concat(name, repeat(' ',level)) from t1; +concat(name,space(level)) concat(name, repeat(' ',level)) +string string +drop table t1; DROP DATABASE d1; USE test; SET character_set_server= @safe_character_set_server; SET collation_server= @safe_collation_server; +SET character_set_client= @safe_character_set_client; +SET character_set_results= @safe_character_set_results; create table t1 (a varchar(1)) character set utf8 collate utf8_estonian_ci; insert into t1 values ('A'),('B'),('C'),('a'),('b'),('c'); select a, a regexp '[a]' from t1 order by binary a; diff --git a/mysql-test/r/ctype_ucs.result b/mysql-test/r/ctype_ucs.result index 023267c227c..2b58aebeafe 100644 --- a/mysql-test/r/ctype_ucs.result +++ b/mysql-test/r/ctype_ucs.result @@ -1,4 +1,80 @@ DROP TABLE IF EXISTS t1; +SET @test_character_set= 'ucs2'; +SET @test_collation= 'ucs2_general_ci'; +SET @safe_character_set_server= @@character_set_server; +SET @safe_collation_server= @@collation_server; +SET @safe_character_set_client= @@character_set_client; +SET @safe_character_set_results= @@character_set_results; +SET character_set_server= @test_character_set; +SET collation_server= @test_collation; +CREATE DATABASE d1; +USE d1; +CREATE TABLE t1 (c CHAR(10), KEY(c)); +SHOW FULL COLUMNS FROM t1; +Field Type Collation Null Key Default Extra Privileges Comment +c char(10) ucs2_general_ci YES MUL NULL +INSERT INTO t1 VALUES ('aaa'),('aaaa'),('aaaaa'); +SELECT c as want3results FROM t1 WHERE c LIKE 'aaa%'; +want3results +aaa +aaaa +aaaaa +DROP TABLE t1; +CREATE TABLE t1 (c1 varchar(15), KEY c1 (c1(2))); +SHOW FULL COLUMNS FROM t1; +Field Type Collation Null Key Default Extra Privileges Comment +c1 varchar(15) ucs2_general_ci YES MUL NULL +INSERT INTO t1 VALUES ('location'),('loberge'),('lotre'),('boabab'); +SELECT c1 as want3results from t1 where c1 like 'l%'; +want3results +location +loberge +lotre +SELECT c1 as want3results from t1 where c1 like 'lo%'; +want3results +location +loberge +lotre +SELECT c1 as want1result from t1 where c1 like 'loc%'; +want1result +location +SELECT c1 as want1result from t1 where c1 like 'loca%'; +want1result +location +SELECT c1 as want1result from t1 where c1 like 'locat%'; +want1result +location +SELECT c1 as want1result from t1 where c1 like 'locati%'; +want1result +location +SELECT c1 as want1result from t1 where c1 like 'locatio%'; +want1result +location +SELECT c1 as want1result from t1 where c1 like 'location%'; +want1result +location +DROP TABLE t1; +set names utf8; +create table t1 ( +name varchar(10), +level smallint unsigned); +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `name` varchar(10) default NULL, + `level` smallint(5) unsigned default NULL +) ENGINE=MyISAM DEFAULT CHARSET=ucs2 +insert into t1 values ('string',1); +select concat(name,space(level)), concat(name, repeat(' ',level)) from t1; +concat(name,space(level)) concat(name, repeat(' ',level)) +string string +drop table t1; +DROP DATABASE d1; +USE test; +SET character_set_server= @safe_character_set_server; +SET collation_server= @safe_collation_server; +SET character_set_client= @safe_character_set_client; +SET character_set_results= @safe_character_set_results; SET NAMES latin1; SET character_set_connection=ucs2; select 'a' = 'a', 'a' = 'a ', 'a ' = 'a'; diff --git a/mysql-test/t/ctype_cp1250_ch.test b/mysql-test/t/ctype_cp1250_ch.test index 86eb8c31d99..b3daa8a02a2 100644 --- a/mysql-test/t/ctype_cp1250_ch.test +++ b/mysql-test/t/ctype_cp1250_ch.test @@ -6,6 +6,16 @@ DROP TABLE IF EXISTS t1; SHOW COLLATION LIKE 'cp1250_czech_cs'; +SET @test_character_set= 'cp1250'; +SET @test_collation= 'cp1250_general_ci'; +-- source include/ctype_common.inc + +SET @test_character_set= 'cp1250'; +SET @test_collation= 'cp1250_czech_cs'; +-- source include/ctype_common.inc + + + # # Bugs: #8840: Empty string comparison and character set 'cp1250' # diff --git a/mysql-test/t/ctype_ucs.test b/mysql-test/t/ctype_ucs.test index bca3a9c3a96..17d9f71e316 100644 --- a/mysql-test/t/ctype_ucs.test +++ b/mysql-test/t/ctype_ucs.test @@ -4,6 +4,10 @@ DROP TABLE IF EXISTS t1; --enable_warnings +SET @test_character_set= 'ucs2'; +SET @test_collation= 'ucs2_general_ci'; +-- source include/ctype_common.inc + SET NAMES latin1; SET character_set_connection=ucs2; -- source include/endspace.inc diff --git a/sql/item_create.cc b/sql/item_create.cc index 561613032bc..60a17c21521 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -361,13 +361,13 @@ Item *create_func_space(Item *a) if (cs->mbminlen > 1) { uint dummy_errors; - sp= new Item_string("",0,cs); + sp= new Item_string("", 0, cs, DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII); if (sp) sp->str_value.copy(" ", 1, &my_charset_latin1, cs, &dummy_errors); } else { - sp= new Item_string(" ",1,cs); + sp= new Item_string(" ", 1, cs, DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII); } return sp ? new Item_func_repeat(sp, a) : 0; } From 53f762abfd26e432477016f00cd984e2c6c9e2d1 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 18 Dec 2007 12:29:50 +0100 Subject: [PATCH 02/19] Bug#32705 - myisam corruption: Key in wrong position at page 1024 with ucs2_bin Inserting strings with a common prefix into a table with characterset UCS2 corrupted the table. An efficient search method was used, which compares end space with ASCII blank. This doesn't work for character sets like UCS2, which do not encode blank like ASCII does. Use the less efficient search method _mi_seq_search() for charsets with mbminlen > 1. myisam/mi_open.c: Bug#32705 - myisam corruption: Key in wrong position at page 1024 with ucs2_bin Use _mi_seq_search() for charsets with mbminlen > 1. mysql-test/r/myisam.result: Bug#32705 - myisam corruption: Key in wrong position at page 1024 with ucs2_bin Added test result. mysql-test/t/myisam.test: Bug#32705 - myisam corruption: Key in wrong position at page 1024 with ucs2_bin Added test. --- myisam/mi_open.c | 11 ++++++++++- mysql-test/r/myisam.result | 10 ++++++++++ mysql-test/t/myisam.test | 13 +++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/myisam/mi_open.c b/myisam/mi_open.c index ec169ac8785..5314d6a9a6c 100644 --- a/myisam/mi_open.c +++ b/myisam/mi_open.c @@ -791,8 +791,17 @@ static void setup_key_functions(register MI_KEYDEF *keyinfo) keyinfo->get_key= _mi_get_pack_key; if (keyinfo->seg[0].flag & HA_PACK_KEY) { /* Prefix compression */ + /* + _mi_prefix_search() compares end-space against ASCII blank (' '). + It cannot be used for character sets, that do not encode the + blank character like ASCII does. UCS2 is an example. All + character sets with a fixed width > 1 or a mimimum width > 1 + cannot represent blank like ASCII does. In these cases we have + to use _mi_seq_search() for the search. + */ if (!keyinfo->seg->charset || use_strnxfrm(keyinfo->seg->charset) || - (keyinfo->seg->flag & HA_NULL_PART)) + (keyinfo->seg->flag & HA_NULL_PART) || + (keyinfo->seg->charset->mbminlen > 1)) keyinfo->bin_search=_mi_seq_search; else keyinfo->bin_search=_mi_prefix_search; diff --git a/mysql-test/r/myisam.result b/mysql-test/r/myisam.result index 56933f45fbf..4ff7441c02e 100644 --- a/mysql-test/r/myisam.result +++ b/mysql-test/r/myisam.result @@ -1839,4 +1839,14 @@ CHECK TABLE t1 EXTENDED; Table Op Msg_type Msg_text test.t1 check status OK DROP TABLE t1; +CREATE TABLE t1 ( +c1 CHAR(255) CHARACTER SET UCS2 COLLATE UCS2_BIN NOT NULL, +KEY(c1) +) ENGINE=MyISAM; +INSERT INTO t1 VALUES ('marshall\'s'); +INSERT INTO t1 VALUES ('marsh'); +CHECK TABLE t1 EXTENDED; +Table Op Msg_type Msg_text +test.t1 check status OK +DROP TABLE t1; End of 5.0 tests diff --git a/mysql-test/t/myisam.test b/mysql-test/t/myisam.test index 80c7a92c12f..b182c35514c 100644 --- a/mysql-test/t/myisam.test +++ b/mysql-test/t/myisam.test @@ -1193,4 +1193,17 @@ SET @@myisam_repair_threads=1; CHECK TABLE t1 EXTENDED; DROP TABLE t1; +# +# Bug#32705 - myisam corruption: Key in wrong position +# at page 1024 with ucs2_bin +# +CREATE TABLE t1 ( + c1 CHAR(255) CHARACTER SET UCS2 COLLATE UCS2_BIN NOT NULL, + KEY(c1) + ) ENGINE=MyISAM; +INSERT INTO t1 VALUES ('marshall\'s'); +INSERT INTO t1 VALUES ('marsh'); +CHECK TABLE t1 EXTENDED; +DROP TABLE t1; + --echo End of 5.0 tests From 4fe64cf67ee7c486082f64cb4e5af3855feeec51 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 16 Jan 2008 11:48:04 +0100 Subject: [PATCH 03/19] Bug#32705 - myisam corruption: Key in wrong position at page 1024 with ucs2_bin Post-pushbuild fix. Moved test from myisam.test to ctype_ucs2_def.test. UCS2 is not always available. mysql-test/r/ctype_ucs2_def.result: Bug#32705 - myisam corruption: Key in wrong position at page 1024 with ucs2_bin Moved test result from myisam.result to here. mysql-test/r/myisam.result: Bug#32705 - myisam corruption: Key in wrong position at page 1024 with ucs2_bin Moved test result from here to ctype_ucs2_def.result. mysql-test/t/ctype_ucs2_def.test: Bug#32705 - myisam corruption: Key in wrong position at page 1024 with ucs2_bin Moved test from myisam.test to here. UCS2 is not always available. mysql-test/t/myisam.test: Bug#32705 - myisam corruption: Key in wrong position at page 1024 with ucs2_bin Moved test from here to ctype_ucs2_def.test. UCS2 is not always available. --- mysql-test/r/ctype_ucs2_def.result | 10 ++++++++++ mysql-test/r/myisam.result | 10 ---------- mysql-test/t/ctype_ucs2_def.test | 13 +++++++++++++ mysql-test/t/myisam.test | 13 ------------- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/mysql-test/r/ctype_ucs2_def.result b/mysql-test/r/ctype_ucs2_def.result index d838c5d66b0..1bbb354798b 100644 --- a/mysql-test/r/ctype_ucs2_def.result +++ b/mysql-test/r/ctype_ucs2_def.result @@ -21,4 +21,14 @@ INSERT INTO t1 VALUES('A', 'A'), ('B', 'B'), ('C', 'C'); INSERT INTO t1 VALUES('A ', 'A '); ERROR 23000: Duplicate entry '' for key 1 DROP TABLE t1; +CREATE TABLE t1 ( +c1 CHAR(255) CHARACTER SET UCS2 COLLATE UCS2_BIN NOT NULL, +KEY(c1) +) ENGINE=MyISAM; +INSERT INTO t1 VALUES ('marshall\'s'); +INSERT INTO t1 VALUES ('marsh'); +CHECK TABLE t1 EXTENDED; +Table Op Msg_type Msg_text +test.t1 check status OK +DROP TABLE t1; End of 5.0 tests diff --git a/mysql-test/r/myisam.result b/mysql-test/r/myisam.result index 4ff7441c02e..56933f45fbf 100644 --- a/mysql-test/r/myisam.result +++ b/mysql-test/r/myisam.result @@ -1839,14 +1839,4 @@ CHECK TABLE t1 EXTENDED; Table Op Msg_type Msg_text test.t1 check status OK DROP TABLE t1; -CREATE TABLE t1 ( -c1 CHAR(255) CHARACTER SET UCS2 COLLATE UCS2_BIN NOT NULL, -KEY(c1) -) ENGINE=MyISAM; -INSERT INTO t1 VALUES ('marshall\'s'); -INSERT INTO t1 VALUES ('marsh'); -CHECK TABLE t1 EXTENDED; -Table Op Msg_type Msg_text -test.t1 check status OK -DROP TABLE t1; End of 5.0 tests diff --git a/mysql-test/t/ctype_ucs2_def.test b/mysql-test/t/ctype_ucs2_def.test index c80444daddd..b146dc63626 100644 --- a/mysql-test/t/ctype_ucs2_def.test +++ b/mysql-test/t/ctype_ucs2_def.test @@ -39,4 +39,17 @@ INSERT INTO t1 VALUES('A', 'A'), ('B', 'B'), ('C', 'C'); INSERT INTO t1 VALUES('A ', 'A '); DROP TABLE t1; +# +# Bug#32705 - myisam corruption: Key in wrong position +# at page 1024 with ucs2_bin +# +CREATE TABLE t1 ( + c1 CHAR(255) CHARACTER SET UCS2 COLLATE UCS2_BIN NOT NULL, + KEY(c1) + ) ENGINE=MyISAM; +INSERT INTO t1 VALUES ('marshall\'s'); +INSERT INTO t1 VALUES ('marsh'); +CHECK TABLE t1 EXTENDED; +DROP TABLE t1; + --echo End of 5.0 tests diff --git a/mysql-test/t/myisam.test b/mysql-test/t/myisam.test index b182c35514c..80c7a92c12f 100644 --- a/mysql-test/t/myisam.test +++ b/mysql-test/t/myisam.test @@ -1193,17 +1193,4 @@ SET @@myisam_repair_threads=1; CHECK TABLE t1 EXTENDED; DROP TABLE t1; -# -# Bug#32705 - myisam corruption: Key in wrong position -# at page 1024 with ucs2_bin -# -CREATE TABLE t1 ( - c1 CHAR(255) CHARACTER SET UCS2 COLLATE UCS2_BIN NOT NULL, - KEY(c1) - ) ENGINE=MyISAM; -INSERT INTO t1 VALUES ('marshall\'s'); -INSERT INTO t1 VALUES ('marsh'); -CHECK TABLE t1 EXTENDED; -DROP TABLE t1; - --echo End of 5.0 tests From 5177c97ec0e11b65227868d6cf98de3970262340 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 31 Jan 2008 18:51:20 +0400 Subject: [PATCH 04/19] BUG#22989 - START SLAVE causes Error on COM_REGISTER_SLAVE: 1105 'Wrong parameters to functi START SLAVE reports vague error when it fails to register on master: "Wrong parameters to function register_slave". If master failed to register slave because of too long 'report-host'/'report-user'/'report-password', return better error messages: "Failed to register slave: too long 'report-host'" "Failed to register slave: too long 'report-user'" "Failed to register slave; too long 'report-password'" No test case for this fix. sql/repl_failsafe.cc: Report descriptive error when master fails to register slave. --- sql/repl_failsafe.cc | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index 896315ec82f..f1826678c9f 100644 --- a/sql/repl_failsafe.cc +++ b/sql/repl_failsafe.cc @@ -109,11 +109,14 @@ void change_rpl_status(RPL_STATUS from_status, RPL_STATUS to_status) } -#define get_object(p, obj) \ +#define get_object(p, obj, msg) \ {\ uint len = (uint)*p++; \ if (p + len > p_end || len >= sizeof(obj)) \ + {\ + errmsg= msg;\ goto err; \ + }\ strmake(obj,(char*) p,len); \ p+= len; \ }\ @@ -158,6 +161,7 @@ int register_slave(THD* thd, uchar* packet, uint packet_length) int res; SLAVE_INFO *si; uchar *p= packet, *p_end= packet + packet_length; + const char *errmsg= "Wrong parameters to function register_slave"; if (check_access(thd, REPL_SLAVE_ACL, any_db,0,0,0,0)) return 1; @@ -166,9 +170,9 @@ int register_slave(THD* thd, uchar* packet, uint packet_length) thd->server_id= si->server_id= uint4korr(p); p+= 4; - get_object(p,si->host); - get_object(p,si->user); - get_object(p,si->password); + get_object(p,si->host, "Failed to register slave: too long 'report-host'"); + get_object(p,si->user, "Failed to register slave: too long 'report-user'"); + get_object(p,si->password, "Failed to register slave; too long 'report-password'"); if (p+10 > p_end) goto err; si->port= uint2korr(p); @@ -187,8 +191,7 @@ int register_slave(THD* thd, uchar* packet, uint packet_length) err: my_free((gptr) si, MYF(MY_WME)); - my_message(ER_UNKNOWN_ERROR, "Wrong parameters to function register_slave", - MYF(0)); + my_message(ER_UNKNOWN_ERROR, errmsg, MYF(0)); err2: return 1; } From 863b86db2485dddbfdbb8200531b7af265daa5db Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 12 Feb 2008 15:12:45 +0400 Subject: [PATCH 05/19] Fix for bug #33758: Got query result when using ORDER BY ASC, but empty result when using DESC Problem: fetching MyISAM keys we copy a key block pointer to the end of the key buffer. However, we don't take into account the pointer length calculatig the buffer size, that may leads to memory overwriting and in turn to unpredictable results. Fix: increase key buffer size by length of the key block pointer. Note: no simple test case. myisam/mi_open.c: Fix for bug #33758: Got query result when using ORDER BY ASC, but empty result when using DESC - increase possible maximum key length by size of the key block pointer, as it's copied into the key buffer in the get_key() MyISAM functions. --- myisam/mi_open.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/myisam/mi_open.c b/myisam/mi_open.c index 5314d6a9a6c..d4d8458a669 100644 --- a/myisam/mi_open.c +++ b/myisam/mi_open.c @@ -270,6 +270,9 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) if (share->options & HA_OPTION_COMPRESS_RECORD) share->base.max_key_length+=2; /* For safety */ + /* Add space for node pointer */ + share->base.max_key_length+= share->base.key_reflength; + if (!my_multi_malloc(MY_WME, &share,sizeof(*share), &share->state.rec_per_key_part,sizeof(long)*key_parts, From 55f6727b708d99562b48ba5391a43ec235375125 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 14 Feb 2008 16:27:01 +0400 Subject: [PATCH 06/19] BUG#33946 - Join on Federated tables with Unique index gives error 1430 from storage engine Federated may crash a server, return wrong result set, return "ERROR 1030 (HY000): Got error 1430 from storage engine" message when local (engine=federated) table has a key against nullable column. The problem was wrong implementation of function that creates WHERE clause for remote query from key. mysql-test/r/federated.result: A test case for BUG#33946. mysql-test/t/federated.test: A test case for BUG#33946. sql/ha_federated.cc: Fixed that federated adds " IS NULL " condition to a remote query, whereas "IS NOT NULL" requested by original query. Fixed that federated didn't check for end of key buffer, didn't setup key buffer pointer and remaining lenght of key buffer, didn't add " AND " between conditions in case original query has IS [NOT] NULL condition against nullable column. Fixed that federated wrongly shifts key buffer pointer by extra one byte when key part may be null (was: store_length + 1, now: store_length). --- mysql-test/r/federated.result | 24 ++++++++++++++++++++++++ mysql-test/t/federated.test | 21 +++++++++++++++++++++ sql/ha_federated.cc | 22 +++++++++++++++++++--- 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/federated.result b/mysql-test/r/federated.result index a005db4deac..3a934e7fe3c 100644 --- a/mysql-test/r/federated.result +++ b/mysql-test/r/federated.result @@ -2045,6 +2045,30 @@ select 1 from t1 order by a; drop table t1; drop table t1; drop view v1; +CREATE TABLE t1 (a INT, b INT, KEY(a,b)); +INSERT INTO t1 VALUES(NULL,1),(1,NULL),(NULL,NULL),(1,1),(2,2); +CREATE TABLE t1 (a INT, b INT, KEY(a,b)) ENGINE=federated +CONNECTION='mysql://root@127.0.0.1:SLAVE_PORT/test/t1'; +SELECT * FROM t1 WHERE a IS NULL; +a b +NULL NULL +NULL 1 +SELECT * FROM t1 WHERE a IS NOT NULL; +a b +1 NULL +1 1 +2 2 +SELECT * FROM t1 WHERE a=1 AND b=1; +a b +1 1 +SELECT * FROM t1 WHERE a IS NULL AND b=1; +a b +NULL 1 +SELECT * FROM t1 WHERE a IS NOT NULL AND b=1; +a b +1 1 +DROP TABLE t1; +DROP TABLE t1; DROP TABLE IF EXISTS federated.t1; DROP DATABASE IF EXISTS federated; DROP TABLE IF EXISTS federated.t1; diff --git a/mysql-test/t/federated.test b/mysql-test/t/federated.test index d4f22650a32..934db5cd68b 100644 --- a/mysql-test/t/federated.test +++ b/mysql-test/t/federated.test @@ -1716,5 +1716,26 @@ connection slave; drop table t1; drop view v1; +# +# BUG#33946 - Join on Federated tables with Unique index gives error 1430 +# from storage engine +# +connection slave; +CREATE TABLE t1 (a INT, b INT, KEY(a,b)); +INSERT INTO t1 VALUES(NULL,1),(1,NULL),(NULL,NULL),(1,1),(2,2); + +connection master; +--replace_result $SLAVE_MYPORT SLAVE_PORT +eval CREATE TABLE t1 (a INT, b INT, KEY(a,b)) ENGINE=federated +CONNECTION='mysql://root@127.0.0.1:$SLAVE_MYPORT/test/t1'; +SELECT * FROM t1 WHERE a IS NULL; +SELECT * FROM t1 WHERE a IS NOT NULL; +SELECT * FROM t1 WHERE a=1 AND b=1; +SELECT * FROM t1 WHERE a IS NULL AND b=1; +SELECT * FROM t1 WHERE a IS NOT NULL AND b=1; +DROP TABLE t1; + +connection slave; +DROP TABLE t1; source include/federated_cleanup.inc; diff --git a/sql/ha_federated.cc b/sql/ha_federated.cc index ac1e0962ffb..c0743bd6c9a 100644 --- a/sql/ha_federated.cc +++ b/sql/ha_federated.cc @@ -1094,10 +1094,20 @@ bool ha_federated::create_where_from_key(String *to, { if (*ptr++) { + /* + We got "IS [NOT] NULL" condition against nullable column. We + distinguish between "IS NOT NULL" and "IS NULL" by flag. For + "IS NULL", flag is set to HA_READ_KEY_EXACT. + */ if (emit_key_part_name(&tmp, key_part) || - tmp.append(FEDERATED_ISNULL)) + tmp.append(ranges[i]->flag == HA_READ_KEY_EXACT ? + FEDERATED_ISNULL : " IS NOT NULL ")) DBUG_RETURN(1); - continue; + /* + We need to adjust pointer and length to be prepared for next + key part. As well as check if this was last key part. + */ + goto prepare_for_next_key_part; } } @@ -1199,12 +1209,18 @@ bool ha_federated::create_where_from_key(String *to, if (tmp.append(FEDERATED_CLOSEPAREN)) DBUG_RETURN(1); +prepare_for_next_key_part: if (store_length >= length) break; DBUG_PRINT("info", ("remainder %d", remainder)); DBUG_ASSERT(remainder > 1); length-= store_length; - ptr+= store_length; + /* + For nullable columns, null-byte is already skipped before, that is + ptr was incremented by 1. Since store_length still counts null-byte, + we need to subtract 1 from store_length. + */ + ptr+= store_length - test(key_part->null_bit); if (tmp.append(FEDERATED_AND)) DBUG_RETURN(1); From 38354149c87dcb16a97c79de34ab48c80c10c499 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 15 Feb 2008 16:03:54 +0400 Subject: [PATCH 07/19] Fix for bug #33304: Test 'func_group' hangs on Mac OS X 10.4 PowerPC 64-bit Problem: SLEEP(0) never returns on 64-bit Mac OS X due to a bug in pthread_cond_timedwait(). Fix: when given a very short timeout just return immediately. sql/item_func.cc: Fix for bug #33304: Test 'func_group' hangs on Mac OS X 10.4 PowerPC 64-bit - if given a very short timeout (< 10 mcs) just return from SLEEP(). --- sql/item_func.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sql/item_func.cc b/sql/item_func.cc index 639e069d24e..e20b0762ea2 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3729,6 +3729,18 @@ longlong Item_func_sleep::val_int() DBUG_ASSERT(fixed == 1); double time= args[0]->val_real(); + /* + On 64-bit OSX pthread_cond_timedwait() waits forever + if passed abstime time has already been exceeded by + the system time. + When given a very short timeout (< 10 mcs) just return + immediately. + We assume that the lines between this test and the call + to pthread_cond_timedwait() will be executed in less than 0.00001 sec. + */ + if (time < 0.00001) + return 0; + set_timespec_nsec(abstime, (ulonglong)(time * ULL(1000000000))); pthread_cond_init(&cond, NULL); From f802cd51238c7842b789e49ba27d3c119ebb8ce3 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 19 Feb 2008 18:16:17 +0400 Subject: [PATCH 08/19] BUG#34289 - Incorrect NAME_CONST substitution in stored procedures breaks replication NAME_CONST() didn't replicate constant character set and collation correctly. With this fix NAME_CONST() inherits collation from the value argument. mysql-test/r/func_misc.result: A test case for BUG#34289. mysql-test/t/func_misc.test: A test case for BUG#34289. sql/item.cc: Inherit collation from value argument. --- mysql-test/r/func_misc.result | 3 +++ mysql-test/t/func_misc.test | 6 ++++++ sql/item.cc | 1 + 3 files changed, 10 insertions(+) diff --git a/mysql-test/r/func_misc.result b/mysql-test/r/func_misc.result index 4f6b6d3a0d8..fa9ca3539d6 100644 --- a/mysql-test/r/func_misc.result +++ b/mysql-test/r/func_misc.result @@ -225,4 +225,7 @@ select min(a) from t1 group by inet_ntoa(a); min(a) -2 drop table t1; +SELECT NAME_CONST('var', 'value') COLLATE latin1_general_cs; +NAME_CONST('var', 'value') COLLATE latin1_general_cs +value End of 5.0 tests diff --git a/mysql-test/t/func_misc.test b/mysql-test/t/func_misc.test index ccb59df5677..17a0dbe50a7 100644 --- a/mysql-test/t/func_misc.test +++ b/mysql-test/t/func_misc.test @@ -237,5 +237,11 @@ insert into t1 values (-1), (-2); select min(a) from t1 group by inet_ntoa(a); drop table t1; +# +# BUG#34289 - Incorrect NAME_CONST substitution in stored procedures breaks +# replication +# +SELECT NAME_CONST('var', 'value') COLLATE latin1_general_cs; + --echo End of 5.0 tests diff --git a/sql/item.cc b/sql/item.cc index ffb18054750..d0dc8201577 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1240,6 +1240,7 @@ bool Item_name_const::fix_fields(THD *thd, Item **ref) return TRUE; } set_name(item_name->ptr(), (uint) item_name->length(), system_charset_info); + collation.set(value_item->collation.collation, DERIVATION_IMPLICIT); max_length= value_item->max_length; decimals= value_item->decimals; fixed= 1; From fea2a5d83dea81e231bb14bc5535b60261c7b30d Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 22 Feb 2008 19:07:07 +0400 Subject: [PATCH 09/19] BUG#13861 - START SLAVE UNTIL may stop 1 evnt too late if log-slave-updates and circul repl Slave SQL thread may execute one extra event when there are events skipped by slave I/O thread (e.g. originated by the same server). Whereas it was requested not to do so by the UNTIL condition. This happens because we compare with the end position of previously executed event. This is fine when there are no skipped by slave I/O thread events, as end position of previous event equals to start position of to be executed event. Otherwise this position equals to start position of skipped event. This is fixed by: - reading the event to be executed before checking if the until condition is satisfied. - comparing the start position of the event to be executed. Since we do not have the start position available, we compute it by subtracting event length from end position (which is available). - if there are no events on the event queue at the slave sql starting time, that meet until condition, we stop immediately, as in this case we do not want to wait for next event. mysql-test/r/rpl_dual_pos_advance.result: A test case for BUG#13861. mysql-test/t/rpl_dual_pos_advance.test: A test case for BUG#13861. sql/log_event.cc: Store length of event. This is needed for further calculation of the beginning of event. sql/slave.cc: Slave SQL thread may execute one extra event when there are events skipped by slave I/O thread (e.g. originated by the same server). Whereas it was requested not to do so by the UNTIL condition. This happens because we compare with the end position of previously executed event. This is fine when there are no skipped by slave I/O thread events, as end position of previous event equals to start position of to be executed event. Otherwise this position equals to start position of skipped event. This is fixed by: - reading the event to be executed before checking if the until condition is satisfied. - comparing the start position of the event to be executed. Since we do not have the start position available, we compute it by subtracting event length from end position (which is available). - if there are no events on the event queue at the slave sql starting time, that meet until condition, we stop immediately, as in this case we do not want to wait for next event. sql/slave.h: Added master_log_pos parametr to is_until_satisfied(). mysql-test/t/rpl_dual_pos_advance-slave.opt: New BitKeeper file ``mysql-test/t/rpl_dual_pos_advance-slave.opt'' --- mysql-test/r/rpl_dual_pos_advance.result | 43 ++++++++++++- mysql-test/t/rpl_dual_pos_advance-slave.opt | 1 + mysql-test/t/rpl_dual_pos_advance.test | 56 +++++++++++------ sql/log_event.cc | 3 +- sql/slave.cc | 68 +++++++++++++-------- sql/slave.h | 2 +- 6 files changed, 128 insertions(+), 45 deletions(-) create mode 100644 mysql-test/t/rpl_dual_pos_advance-slave.opt diff --git a/mysql-test/r/rpl_dual_pos_advance.result b/mysql-test/r/rpl_dual_pos_advance.result index 257baa81b74..97ca8101c33 100644 --- a/mysql-test/r/rpl_dual_pos_advance.result +++ b/mysql-test/r/rpl_dual_pos_advance.result @@ -8,15 +8,56 @@ reset master; change master to master_host="127.0.0.1",master_port=SLAVE_PORT,master_user="root"; start slave; create table t1 (n int); +stop slave; +create table t2 (n int); +show tables; +Tables_in_test +t1 +t2 +create table t3 (n int) engine=innodb; +set @a=1; +insert into t3 values(@a); +begin; +insert into t3 values(2); +insert into t3 values(3); +commit; +insert into t3 values(4); +start slave until master_log_file="slave-bin.000001",master_log_pos=195; +Warnings: +Note 1278 It is recommended to use --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL; otherwise, you will get problems if you get an unexpected slave's mysqld restart +select master_pos_wait("slave-bin.000001",137); +master_pos_wait("slave-bin.000001",137) +0 +show tables; +Tables_in_test +t1 +t2 +start slave until master_log_file="slave-bin.000001",master_log_pos=438; +Warnings: +Note 1278 It is recommended to use --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL; otherwise, you will get problems if you get an unexpected slave's mysqld restart +select * from t3; +n +1 +start slave until master_log_file="slave-bin.000001",master_log_pos=663; +Warnings: +Note 1278 It is recommended to use --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL; otherwise, you will get problems if you get an unexpected slave's mysqld restart +select * from t3; +n +1 +2 +3 +start slave; create table t4 (n int); create table t5 (n int); create table t6 (n int); show tables; Tables_in_test t1 +t2 +t3 t4 t5 t6 stop slave; reset slave; -drop table t1,t4,t5,t6; +drop table t1,t2,t3,t4,t5,t6; diff --git a/mysql-test/t/rpl_dual_pos_advance-slave.opt b/mysql-test/t/rpl_dual_pos_advance-slave.opt new file mode 100644 index 00000000000..627becdbfb5 --- /dev/null +++ b/mysql-test/t/rpl_dual_pos_advance-slave.opt @@ -0,0 +1 @@ +--innodb diff --git a/mysql-test/t/rpl_dual_pos_advance.test b/mysql-test/t/rpl_dual_pos_advance.test index 518fa9df885..5e08284a2f2 100644 --- a/mysql-test/t/rpl_dual_pos_advance.test +++ b/mysql-test/t/rpl_dual_pos_advance.test @@ -7,6 +7,7 @@ # It also will test BUG#13861. source include/master-slave.inc; +source include/have_innodb.inc; # set up "dual head" @@ -30,45 +31,64 @@ save_master_pos; connection master; sync_with_master; -# Now test BUG#13861. This will be enabled when Guilhem fixes this -# bug. +# +# BUG#13861 - START SLAVE UNTIL may stop 1 evnt too late if +# log-slave-updates and circul repl +# +stop slave; -# stop slave +create table t2 (n int); # create one ignored event -# create table t2 (n int); # create one ignored event +save_master_pos; +connection slave; +sync_with_master; -# save_master_pos; -# connection slave; -# sync_with_master; +connection slave; -# connection slave; +show tables; -# show tables; +save_master_pos; -# save_master_pos; +create table t3 (n int) engine=innodb; +set @a=1; +insert into t3 values(@a); +begin; +insert into t3 values(2); +insert into t3 values(3); +commit; +insert into t3 values(4); -# create table t3 (n int); -# connection master; +connection master; # bug is that START SLAVE UNTIL may stop too late, we test that by # asking it to stop before creation of t3. -# start slave until master_log_file="slave-bin.000001",master_log_pos=195; +start slave until master_log_file="slave-bin.000001",master_log_pos=195; # wait until it's started (the position below is the start of "CREATE # TABLE t2") (otherwise wait_for_slave_to_stop may return at once) -# select master_pos_wait("slave-bin.000001",137); +select master_pos_wait("slave-bin.000001",137); -# wait_for_slave_to_stop; +wait_for_slave_to_stop; # then BUG#13861 causes t3 to show up below (because stopped too # late). -# show tables; +show tables; -# start slave; +# ensure that we do not break set @a=1; insert into t3 values(@a); +start slave until master_log_file="slave-bin.000001",master_log_pos=438; +wait_for_slave_to_stop; +select * from t3; + +# ensure that we do not break transaction +start slave until master_log_file="slave-bin.000001",master_log_pos=663; +wait_for_slave_to_stop; +select * from t3; + +start slave; # BUG#13023 is that Exec_master_log_pos may stay too low "forever": @@ -99,7 +119,7 @@ show tables; stop slave; reset slave; -drop table t1,t4,t5,t6; # add t2 and t3 later +drop table t1,t2,t3,t4,t5,t6; save_master_pos; connection slave; diff --git a/sql/log_event.cc b/sql/log_event.cc index a950094a018..8e14a873fc6 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -373,6 +373,7 @@ Log_event::Log_event(const char* buf, #endif when = uint4korr(buf); server_id = uint4korr(buf + SERVER_ID_OFFSET); + data_written= uint4korr(buf + EVENT_LEN_OFFSET); if (description_event->binlog_version==1) { log_pos= 0; @@ -405,7 +406,7 @@ Log_event::Log_event(const char* buf, binlog, so which will cause problems if the user uses this value in CHANGE MASTER). */ - log_pos+= uint4korr(buf + EVENT_LEN_OFFSET); + log_pos+= data_written; } DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos)); diff --git a/sql/slave.cc b/sql/slave.cc index 8a3620080f2..500683e080d 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -3115,6 +3115,11 @@ int check_expected_error(THD* thd, RELAY_LOG_INFO* rli, int expected_error) Check if condition stated in UNTIL clause of START SLAVE is reached. SYNOPSYS st_relay_log_info::is_until_satisfied() + master_beg_pos position of the beginning of to be executed event + (not log_pos member of the event that points to the + beginning of the following event) + + DESCRIPTION Checks if UNTIL condition is reached. Uses caching result of last comparison of current log file name and target log file name. So cached @@ -3139,7 +3144,7 @@ int check_expected_error(THD* thd, RELAY_LOG_INFO* rli, int expected_error) false - condition not met */ -bool st_relay_log_info::is_until_satisfied() +bool st_relay_log_info::is_until_satisfied(my_off_t master_beg_pos) { const char *log_name; ulonglong log_pos; @@ -3149,7 +3154,7 @@ bool st_relay_log_info::is_until_satisfied() if (until_condition == UNTIL_MASTER_POS) { log_name= group_master_log_name; - log_pos= group_master_log_pos; + log_pos= master_beg_pos; } else { /* until_condition == UNTIL_RELAY_POS */ @@ -3228,28 +3233,6 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) wait for something for example inside of next_event(). */ pthread_mutex_lock(&rli->data_lock); - /* - This tests if the position of the end of the last previous executed event - hits the UNTIL barrier. - We would prefer to test if the position of the start (or possibly) end of - the to-be-read event hits the UNTIL barrier, this is different if there - was an event ignored by the I/O thread just before (BUG#13861 to be - fixed). - */ - if (rli->until_condition!=RELAY_LOG_INFO::UNTIL_NONE && - rli->is_until_satisfied()) - { - char buf[22]; - sql_print_information("Slave SQL thread stopped because it reached its" - " UNTIL position %s", llstr(rli->until_pos(), buf)); - /* - Setting abort_slave flag because we do not want additional message about - error in query execution to be printed. - */ - rli->abort_slave= 1; - pthread_mutex_unlock(&rli->data_lock); - return 1; - } Log_event * ev = next_event(rli); @@ -3266,6 +3249,27 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) int type_code = ev->get_type_code(); int exec_res; + /* + This tests if the position of the beginning of the current event + hits the UNTIL barrier. + */ + if (rli->until_condition != RELAY_LOG_INFO::UNTIL_NONE && + rli->is_until_satisfied((thd->options & OPTION_BEGIN || !ev->log_pos) ? + rli->group_master_log_pos : + ev->log_pos - ev->data_written)) + { + char buf[22]; + sql_print_information("Slave SQL thread stopped because it reached its" + " UNTIL position %s", llstr(rli->until_pos(), buf)); + /* + Setting abort_slave flag because we do not want additional message about + error in query execution to be printed. + */ + rli->abort_slave= 1; + pthread_mutex_unlock(&rli->data_lock); + delete ev; + return 1; + } /* Queries originating from this server must be skipped. Low-level events (Format_desc, Rotate, Stop) from this server @@ -3977,6 +3981,22 @@ Slave SQL thread aborted. Can't execute init_slave query"); } } + /* + First check until condition - probably there is nothing to execute. We + do not want to wait for next event in this case. + */ + pthread_mutex_lock(&rli->data_lock); + if (rli->until_condition != RELAY_LOG_INFO::UNTIL_NONE && + rli->is_until_satisfied(rli->group_master_log_pos)) + { + char buf[22]; + sql_print_information("Slave SQL thread stopped because it reached its" + " UNTIL position %s", llstr(rli->until_pos(), buf)); + pthread_mutex_unlock(&rli->data_lock); + goto err; + } + pthread_mutex_unlock(&rli->data_lock); + /* Read queries from the IO/THREAD until this thread is killed */ while (!sql_slave_killed(thd,rli)) diff --git a/sql/slave.h b/sql/slave.h index c61787cdf3b..da548e145d3 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -348,7 +348,7 @@ typedef struct st_relay_log_info void close_temporary_tables(); /* Check if UNTIL condition is satisfied. See slave.cc for more. */ - bool is_until_satisfied(); + bool is_until_satisfied(my_off_t master_beg_pos); inline ulonglong until_pos() { return ((until_condition == UNTIL_MASTER_POS) ? group_master_log_pos : From 8e4a3fcbc996e41b4d805a926479bfacf13a99dd Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 4 Mar 2008 16:13:08 +0400 Subject: [PATCH 10/19] Bug#23097 mysql can't insert korean on mysql prompt. Problem: libedit is a very pure-ASCII oriented library, and it is not aware of extended (0x80..0xFF) or even multi-byte characters. It considered such characters as non-printable and didn't allow to input them. Fix: make libedit think that all bytes >= 0x80 are printable. cmd-line-utils/libedit/el.h: Defining macro, a locale's isprint() replacement. We'll consider all 8bit values as printable characters. cmd-line-utils/libedit/key.c: Changing isprint() to el_isprint(). cmd-line-utils/libedit/map.c: Changing isprint() to el_isprint(). cmd-line-utils/libedit/read.c: Changing isprint() to el_isprint(). cmd-line-utils/libedit/refresh.c: Changing isprint() to el_isprint(). --- cmd-line-utils/libedit/el.h | 2 ++ cmd-line-utils/libedit/key.c | 4 ++-- cmd-line-utils/libedit/map.c | 2 +- cmd-line-utils/libedit/read.c | 2 +- cmd-line-utils/libedit/refresh.c | 6 +++--- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/cmd-line-utils/libedit/el.h b/cmd-line-utils/libedit/el.h index c4b6cff2186..d9379d7c8aa 100644 --- a/cmd-line-utils/libedit/el.h +++ b/cmd-line-utils/libedit/el.h @@ -136,6 +136,8 @@ struct editline { protected int el_editmode(EditLine *, int, const char **); +#define el_isprint(x) ((unsigned char) (x) < 0x80 ? isprint(x) : 1) + #ifdef DEBUG #define EL_ABORT(a) do { \ fprintf(el->el_errfile, "%s, %d: ", \ diff --git a/cmd-line-utils/libedit/key.c b/cmd-line-utils/libedit/key.c index 090a2684e92..35fcf0651b2 100644 --- a/cmd-line-utils/libedit/key.c +++ b/cmd-line-utils/libedit/key.c @@ -618,7 +618,7 @@ key__decode_char(char *buf, int cnt, int ch) } else if (ch == '\\') { buf[cnt++] = '\\'; buf[cnt] = '\\'; - } else if (ch == ' ' || (isprint(ch) && !isspace(ch))) { + } else if (ch == ' ' || (el_isprint(ch) && !isspace(ch))) { buf[cnt] = ch; } else { buf[cnt++] = '\\'; @@ -660,7 +660,7 @@ key__decode_str(const char *str, char *buf, const char *sep) } else if (*p == '^' || *p == '\\') { *b++ = '\\'; *b++ = *p; - } else if (*p == ' ' || (isprint((unsigned char) *p) && + } else if (*p == ' ' || (el_isprint((unsigned char) *p) && !isspace((unsigned char) *p))) { *b++ = *p; } else { diff --git a/cmd-line-utils/libedit/map.c b/cmd-line-utils/libedit/map.c index d99c36ff665..6be9279b5e5 100644 --- a/cmd-line-utils/libedit/map.c +++ b/cmd-line-utils/libedit/map.c @@ -961,7 +961,7 @@ map_init_nls(EditLine *el) el_action_t *map = el->el_map.key; for (i = 0200; i <= 0377; i++) - if (isprint(i)) + if (el_isprint(i)) map[i] = ED_INSERT; } diff --git a/cmd-line-utils/libedit/read.c b/cmd-line-utils/libedit/read.c index 051f3e8e42e..51848c2038e 100644 --- a/cmd-line-utils/libedit/read.c +++ b/cmd-line-utils/libedit/read.c @@ -508,7 +508,7 @@ el_gets(EditLine *el, int *nread) el->el_chared.c_redo.pos < el->el_chared.c_redo.lim) { if (cmdnum == VI_DELETE_PREV_CHAR && el->el_chared.c_redo.pos != el->el_chared.c_redo.buf - && isprint((unsigned char)el->el_chared.c_redo.pos[-1])) + && el_isprint((unsigned char)el->el_chared.c_redo.pos[-1])) el->el_chared.c_redo.pos--; else *el->el_chared.c_redo.pos++ = ch; diff --git a/cmd-line-utils/libedit/refresh.c b/cmd-line-utils/libedit/refresh.c index b2833d215c5..46aca15ef08 100644 --- a/cmd-line-utils/libedit/refresh.c +++ b/cmd-line-utils/libedit/refresh.c @@ -88,7 +88,7 @@ private void re_addc(EditLine *el, int c) { - if (isprint(c)) { + if (el_isprint(c)) { re_putc(el, c, 1); return; } @@ -964,7 +964,7 @@ re_refresh_cursor(EditLine *el) h = 1; v++; } - } else if (!isprint((unsigned char) c)) { + } else if (!el_isprint((unsigned char) c)) { h += 3; if (h > th) { /* if overflow, compensate */ h = h - th; @@ -1057,7 +1057,7 @@ re_fastaddc(EditLine *el) char mc = (c == '\177') ? '?' : (c | 0100); re_fastputc(el, '^'); re_fastputc(el, mc); - } else if (isprint((unsigned char) c)) { /* normal char */ + } else if (el_isprint((unsigned char) c)) { /* normal char */ re_fastputc(el, c); } else { re_fastputc(el, '\\'); From f2c9483dd5071747bfb46f4dd97d3607fe7d74ec Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 6 Mar 2008 09:58:49 +0400 Subject: [PATCH 11/19] additional test fixes for bug 27580 --- mysql-test/r/ctype_cp1250_ch.result | 34 +++++++++++++++++++++++++++++ mysql-test/r/ctype_cp932.result | 19 ++++++++++++++++ mysql-test/r/ctype_ucs.result | 17 +++++++++++++++ 3 files changed, 70 insertions(+) diff --git a/mysql-test/r/ctype_cp1250_ch.result b/mysql-test/r/ctype_cp1250_ch.result index 3f22933e260..2ed22802805 100644 --- a/mysql-test/r/ctype_cp1250_ch.result +++ b/mysql-test/r/ctype_cp1250_ch.result @@ -57,6 +57,23 @@ SELECT c1 as want1result from t1 where c1 like 'location%'; want1result location DROP TABLE t1; +create table t1 (a set('a') not null); +insert into t1 values (),(); +Warnings: +Warning 1364 Field 'a' doesn't have a default value +select cast(a as char(1)) from t1; +cast(a as char(1)) + + +select a sounds like a from t1; +a sounds like a +1 +1 +select 1 from t1 order by cast(a as char(1)); +1 +1 +1 +drop table t1; set names utf8; create table t1 ( name varchar(10), @@ -133,6 +150,23 @@ SELECT c1 as want1result from t1 where c1 like 'location%'; want1result location DROP TABLE t1; +create table t1 (a set('a') not null); +insert into t1 values (),(); +Warnings: +Warning 1364 Field 'a' doesn't have a default value +select cast(a as char(1)) from t1; +cast(a as char(1)) + + +select a sounds like a from t1; +a sounds like a +1 +1 +select 1 from t1 order by cast(a as char(1)); +1 +1 +1 +drop table t1; set names utf8; create table t1 ( name varchar(10), diff --git a/mysql-test/r/ctype_cp932.result b/mysql-test/r/ctype_cp932.result index 8974a6a8594..63cd720746f 100755 --- a/mysql-test/r/ctype_cp932.result +++ b/mysql-test/r/ctype_cp932.result @@ -6,6 +6,8 @@ SET @test_character_set= 'cp932'; SET @test_collation= 'cp932_japanese_ci'; SET @safe_character_set_server= @@character_set_server; SET @safe_collation_server= @@collation_server; +SET @safe_character_set_client= @@character_set_client; +SET @safe_character_set_results= @@character_set_results; SET character_set_server= @test_character_set; SET collation_server= @test_collation; CREATE DATABASE d1; @@ -72,10 +74,27 @@ select 1 from t1 order by cast(a as char(1)); 1 1 drop table t1; +set names utf8; +create table t1 ( +name varchar(10), +level smallint unsigned); +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `name` varchar(10) default NULL, + `level` smallint(5) unsigned default NULL +) ENGINE=MyISAM DEFAULT CHARSET=cp932 +insert into t1 values ('string',1); +select concat(name,space(level)), concat(name, repeat(' ',level)) from t1; +concat(name,space(level)) concat(name, repeat(' ',level)) +string string +drop table t1; DROP DATABASE d1; USE test; SET character_set_server= @safe_character_set_server; SET collation_server= @safe_collation_server; +SET character_set_client= @safe_character_set_client; +SET character_set_results= @safe_character_set_results; set names cp932; set character_set_database = cp932; CREATE TABLE t1(c1 CHAR(1)) DEFAULT CHARACTER SET = cp932; diff --git a/mysql-test/r/ctype_ucs.result b/mysql-test/r/ctype_ucs.result index a69c98590c6..f1a2da6c6bb 100644 --- a/mysql-test/r/ctype_ucs.result +++ b/mysql-test/r/ctype_ucs.result @@ -54,6 +54,23 @@ SELECT c1 as want1result from t1 where c1 like 'location%'; want1result location DROP TABLE t1; +create table t1 (a set('a') not null); +insert into t1 values (),(); +Warnings: +Warning 1364 Field 'a' doesn't have a default value +select cast(a as char(1)) from t1; +cast(a as char(1)) + + +select a sounds like a from t1; +a sounds like a +1 +1 +select 1 from t1 order by cast(a as char(1)); +1 +1 +1 +drop table t1; set names utf8; create table t1 ( name varchar(10), From b80afe52011ff8edc22298cc96f86eabe8f69a8a Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 12 Mar 2008 15:38:57 +0100 Subject: [PATCH 12/19] Bug#35247 - rpl_transaction.test produces warnings files The test file tried to use a mysqltest command '--warning' but there is no such command. Changed '--warning' to '#--warning'. mysql-test/t/rpl_transaction.test: Bug#35247 - rpl_transaction.test produces warnings files Changed '--warning' to '#--warning'. --- mysql-test/t/rpl_transaction.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/t/rpl_transaction.test b/mysql-test/t/rpl_transaction.test index 07ba2ea8281..ee9de3b5a5e 100644 --- a/mysql-test/t/rpl_transaction.test +++ b/mysql-test/t/rpl_transaction.test @@ -39,7 +39,7 @@ COMMIT; BEGIN; INSERT INTO tmyisam VALUES (5); INSERT INTO tmyisam VALUES (6); ---warning 1196 +#--warning 1196 ROLLBACK; SELECT * FROM tmyisam ORDER BY a; From e85d6a915d242e8d39f021f4933c450323cec953 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 13 Mar 2008 16:39:27 +0100 Subject: [PATCH 13/19] Bug#33756 - query cache with concurrent_insert=0 appears broken When concurrent inserts were disabled, statements after an INSERT were not put into the query cache. This happened because we do not save the current data file length at statement start when concurrent inserts are disabled. But we checked the always zero local length against the real file length anyway. Fixed by doing the check only if concurrent inserts are not diabled. mysql-test/r/query_cache.result: Bug#33756 - query cache with concurrent_insert=0 appears broken Added test result. mysql-test/t/query_cache.test: Bug#33756 - query cache with concurrent_insert=0 appears broken Added test. sql/ha_myisam.cc: Bug#33756 - query cache with concurrent_insert=0 appears broken Changed code so that file length check is done only when concurrent inserts are possible. --- mysql-test/r/query_cache.result | 27 +++++++++++++ mysql-test/t/query_cache.test | 20 ++++++++++ sql/ha_myisam.cc | 70 +++++++++++++++++++-------------- 3 files changed, 87 insertions(+), 30 deletions(-) diff --git a/mysql-test/r/query_cache.result b/mysql-test/r/query_cache.result index 68f18d1b0b2..fa80a44c177 100644 --- a/mysql-test/r/query_cache.result +++ b/mysql-test/r/query_cache.result @@ -1654,3 +1654,30 @@ set GLOBAL query_cache_type=default; set GLOBAL query_cache_limit=default; set GLOBAL query_cache_min_res_unit=default; set GLOBAL query_cache_size=default; +use test; +FLUSH STATUS; +SET GLOBAL query_cache_size=10*1024*1024; +SET @save_concurrent_insert= @@concurrent_insert; +SET GLOBAL concurrent_insert= 0; +CREATE TABLE t1 (c1 INT NOT NULL) ENGINE=MyISAM; +INSERT INTO t1 (c1) VALUES (1), (2); +SHOW GLOBAL VARIABLES LIKE 'concurrent_insert'; +Variable_name Value +concurrent_insert 0 +SHOW STATUS LIKE 'Qcache_hits'; +Variable_name Value +Qcache_hits 0 +SELECT * FROM t1; +c1 +1 +2 +SELECT * FROM t1; +c1 +1 +2 +SHOW STATUS LIKE 'Qcache_hits'; +Variable_name Value +Qcache_hits 1 +DROP TABLE t1; +SET GLOBAL concurrent_insert= @save_concurrent_insert; +SET GLOBAL query_cache_size= default; diff --git a/mysql-test/t/query_cache.test b/mysql-test/t/query_cache.test index 44b63df9739..ebd24bf2b89 100644 --- a/mysql-test/t/query_cache.test +++ b/mysql-test/t/query_cache.test @@ -1255,5 +1255,25 @@ set GLOBAL query_cache_type=default; set GLOBAL query_cache_limit=default; set GLOBAL query_cache_min_res_unit=default; set GLOBAL query_cache_size=default; +use test; + +# +# Bug#33756 - query cache with concurrent_insert=0 appears broken +# +FLUSH STATUS; +SET GLOBAL query_cache_size=10*1024*1024; +SET @save_concurrent_insert= @@concurrent_insert; +SET GLOBAL concurrent_insert= 0; +CREATE TABLE t1 (c1 INT NOT NULL) ENGINE=MyISAM; +INSERT INTO t1 (c1) VALUES (1), (2); +# +SHOW GLOBAL VARIABLES LIKE 'concurrent_insert'; +SHOW STATUS LIKE 'Qcache_hits'; +SELECT * FROM t1; +SELECT * FROM t1; +SHOW STATUS LIKE 'Qcache_hits'; +DROP TABLE t1; +SET GLOBAL concurrent_insert= @save_concurrent_insert; +SET GLOBAL query_cache_size= default; # End of 5.0 tests diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index 46afa7a8f45..8c8f027bb6b 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -1954,6 +1954,7 @@ my_bool ha_myisam::register_query_cache_table(THD *thd, char *table_name, *engine_callback, ulonglong *engine_data) { + DBUG_ENTER("ha_myisam::register_query_cache_table"); /* No call back function is needed to determine if a cached statement is valid or not. @@ -1965,39 +1966,48 @@ my_bool ha_myisam::register_query_cache_table(THD *thd, char *table_name, */ *engine_data= 0; - /* - If a concurrent INSERT has happened just before the currently processed - SELECT statement, the total size of the table is unknown. - - To determine if the table size is known, the current thread's snap shot of - the table size with the actual table size are compared. - - If the table size is unknown the SELECT statement can't be cached. - */ - ulonglong actual_data_file_length; - ulonglong current_data_file_length; - - /* - POSIX visibility rules specify that "2. Whatever memory values a - thread can see when it unlocks a mutex <...> can also be seen by any - thread that later locks the same mutex". In this particular case, - concurrent insert thread had modified the data_file_length in - MYISAM_SHARE before it has unlocked (or even locked) - structure_guard_mutex. So, here we're guaranteed to see at least that - value after we've locked the same mutex. We can see a later value - (modified by some other thread) though, but it's ok, as we only want - to know if the variable was changed, the actual new value doesn't matter - */ - actual_data_file_length= file->s->state.state.data_file_length; - current_data_file_length= file->save_state.data_file_length; - - if (current_data_file_length != actual_data_file_length) + if (file->s->concurrent_insert) { - /* Don't cache current statement. */ - return FALSE; + /* + If a concurrent INSERT has happened just before the currently + processed SELECT statement, the total size of the table is + unknown. + + To determine if the table size is known, the current thread's snap + shot of the table size with the actual table size are compared. + + If the table size is unknown the SELECT statement can't be cached. + + When concurrent inserts are disabled at table open, mi_open() + does not assign a get_status() function. In this case the local + ("current") status is never updated. We would wrongly think that + we cannot cache the statement. + */ + ulonglong actual_data_file_length; + ulonglong current_data_file_length; + + /* + POSIX visibility rules specify that "2. Whatever memory values a + thread can see when it unlocks a mutex <...> can also be seen by any + thread that later locks the same mutex". In this particular case, + concurrent insert thread had modified the data_file_length in + MYISAM_SHARE before it has unlocked (or even locked) + structure_guard_mutex. So, here we're guaranteed to see at least that + value after we've locked the same mutex. We can see a later value + (modified by some other thread) though, but it's ok, as we only want + to know if the variable was changed, the actual new value doesn't matter + */ + actual_data_file_length= file->s->state.state.data_file_length; + current_data_file_length= file->save_state.data_file_length; + + if (current_data_file_length != actual_data_file_length) + { + /* Don't cache current statement. */ + DBUG_RETURN(FALSE); + } } /* It is ok to try to cache current statement. */ - return TRUE; + DBUG_RETURN(TRUE); } #endif From 3ec867679a12e6a14bed745bc76752d5e2691774 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 14 Mar 2008 17:17:03 +0400 Subject: [PATCH 14/19] BUG#13861 - START SLAVE UNTIL may stop 1 evnt too late if log-slave-updates and circul repl This is a test case fix for BUG#13861. mysql-test/r/rpl_dual_pos_advance.result: Fix for a test case for BUG#13861. mysql-test/t/rpl_dual_pos_advance.test: Fix for a test case for BUG#13861. master_pos_wait() requires slave sql thread running. But it is not guaranteed for this test case. As we use start slave until it may execute all events and shutdown before master_pos_wait() is started. On the other hand it is safe just to wait for slave to stop here, as start slave returns _after_ sql thread is started. --- mysql-test/r/rpl_dual_pos_advance.result | 3 --- mysql-test/t/rpl_dual_pos_advance.test | 6 ------ 2 files changed, 9 deletions(-) diff --git a/mysql-test/r/rpl_dual_pos_advance.result b/mysql-test/r/rpl_dual_pos_advance.result index 97ca8101c33..4c6323a61db 100644 --- a/mysql-test/r/rpl_dual_pos_advance.result +++ b/mysql-test/r/rpl_dual_pos_advance.result @@ -25,9 +25,6 @@ insert into t3 values(4); start slave until master_log_file="slave-bin.000001",master_log_pos=195; Warnings: Note 1278 It is recommended to use --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL; otherwise, you will get problems if you get an unexpected slave's mysqld restart -select master_pos_wait("slave-bin.000001",137); -master_pos_wait("slave-bin.000001",137) -0 show tables; Tables_in_test t1 diff --git a/mysql-test/t/rpl_dual_pos_advance.test b/mysql-test/t/rpl_dual_pos_advance.test index 5e08284a2f2..6a3cf9e4f97 100644 --- a/mysql-test/t/rpl_dual_pos_advance.test +++ b/mysql-test/t/rpl_dual_pos_advance.test @@ -65,12 +65,6 @@ connection master; # asking it to stop before creation of t3. start slave until master_log_file="slave-bin.000001",master_log_pos=195; - -# wait until it's started (the position below is the start of "CREATE -# TABLE t2") (otherwise wait_for_slave_to_stop may return at once) - -select master_pos_wait("slave-bin.000001",137); - wait_for_slave_to_stop; # then BUG#13861 causes t3 to show up below (because stopped too From 196b616accbc73d212f21e2ec84353931a2f286b Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 14 Mar 2008 19:38:22 +0400 Subject: [PATCH 15/19] BUG#28248 - mysqldump results with MERGE ... UNION=() cannot be executed When there are no underlying tables specified for a merge table, SHOW CREATE TABLE outputs a statement that cannot be executed. The same is true for mysqldump (it generates dumps that cannot be executed). This happens because SQL parser does not accept empty UNION() clause. This patch changes the following: - it is now possible to execute CREATE/ALTER statement with empty UNION() clause. - the same as above, but still worth noting: it is now possible to remove underlying tables mapping using ALTER TABLE ... UNION=(). - SHOW CREATE TABLE does not output UNION() clause if there are no underlying tables specified for a merge table. This makes mysqldump slightly smaller. mysql-test/r/merge.result: A test case for BUG#28248. mysql-test/t/merge.test: A test case for BUG#28248. sql/ha_myisammrg.cc: Do not output UNION clause in SHOW CREATE TABLE, when there are no underlying tables defined. sql/sql_yacc.yy: Make underlying table list for MERGE engine optional. As for MERGE engine empty underlying tables list is valid, it should be valid for the parser as well. This change is mostly needed to restore dumps made by earlier MySQL versions. Also with this fix it is possible to remove underlying tables mapping by using ALTER TABLE ... UNION=(). --- mysql-test/r/merge.result | 22 ++++++++++++++++++++++ mysql-test/t/merge.test | 13 +++++++++++++ sql/ha_myisammrg.cc | 6 ++++++ sql/sql_yacc.yy | 2 +- 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index 2ceae95dfca..f8ef4f23180 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -918,4 +918,26 @@ id ref 3 2 4 5 DROP TABLE t1, t2, t3; +CREATE TABLE t1(a INT); +CREATE TABLE m1(a INT) ENGINE=MERGE; +SHOW CREATE TABLE m1; +Table Create Table +m1 CREATE TABLE `m1` ( + `a` int(11) default NULL +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 +DROP TABLE m1; +CREATE TABLE m1(a INT) ENGINE=MERGE UNION=(); +SHOW CREATE TABLE m1; +Table Create Table +m1 CREATE TABLE `m1` ( + `a` int(11) default NULL +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 +ALTER TABLE m1 UNION=(t1); +ALTER TABLE m1 UNION=(); +SHOW CREATE TABLE m1; +Table Create Table +m1 CREATE TABLE `m1` ( + `a` int(11) default NULL +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1, m1; End of 5.0 tests diff --git a/mysql-test/t/merge.test b/mysql-test/t/merge.test index 35443987858..e5cc4d703f2 100644 --- a/mysql-test/t/merge.test +++ b/mysql-test/t/merge.test @@ -542,5 +542,18 @@ SELECT * FROM t3; DROP TABLE t1, t2, t3; +# +# BUG#28248 - mysqldump results with MERGE ... UNION=() cannot be executed +# +CREATE TABLE t1(a INT); +CREATE TABLE m1(a INT) ENGINE=MERGE; +SHOW CREATE TABLE m1; +DROP TABLE m1; +CREATE TABLE m1(a INT) ENGINE=MERGE UNION=(); +SHOW CREATE TABLE m1; +ALTER TABLE m1 UNION=(t1); +ALTER TABLE m1 UNION=(); +SHOW CREATE TABLE m1; +DROP TABLE t1, m1; --echo End of 5.0 tests diff --git a/sql/ha_myisammrg.cc b/sql/ha_myisammrg.cc index 78492d2843d..b796978f352 100644 --- a/sql/ha_myisammrg.cc +++ b/sql/ha_myisammrg.cc @@ -602,6 +602,12 @@ void ha_myisammrg::append_create_info(String *packet) packet->append(STRING_WITH_LEN(" INSERT_METHOD=")); packet->append(get_type(&merge_insert_method,file->merge_insert_method-1)); } + /* + There is no sence adding UNION clause in case there is no underlying + tables specified. + */ + if (file->open_tables == file->end_table) + return; packet->append(STRING_WITH_LEN(" UNION=(")); MYRG_TABLE *open_table,*first; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 04285fea227..b877a789732 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -2932,7 +2932,7 @@ create_table_option: my_error(ER_WARN_DEPRECATED_SYNTAX, MYF(0), "RAID_CHUNKSIZE", "PARTITION"); MYSQL_YYABORT; } - | UNION_SYM opt_equal '(' table_list ')' + | UNION_SYM opt_equal '(' opt_table_list ')' { /* Move the union list to the merge_list */ LEX *lex=Lex; From 451de554fcf32a7a7d42f0496b701fe4237403c6 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 14 Mar 2008 20:51:32 +0100 Subject: [PATCH 16/19] Post-merge fix --- mysql-test/r/type_set.result | 1 + mysql-test/t/type_set.test | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/type_set.result b/mysql-test/r/type_set.result index 9829d4951c9..4722db1432f 100644 --- a/mysql-test/r/type_set.result +++ b/mysql-test/r/type_set.result @@ -93,3 +93,4 @@ c 1,2,3 64 DROP TABLE t1; +End of 5.0 tests diff --git a/mysql-test/t/type_set.test b/mysql-test/t/type_set.test index c7f8c59de28..e98555e137b 100644 --- a/mysql-test/t/type_set.test +++ b/mysql-test/t/type_set.test @@ -75,4 +75,4 @@ INSERT INTO t1 VALUES(9223372036854775808); SELECT * FROM t1; DROP TABLE t1; ---# echo End of 5.0 tests +--echo End of 5.0 tests From 7343db297a1f1ab08812c9b4471306535551ddec Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 18 Mar 2008 16:38:12 +0400 Subject: [PATCH 17/19] Make gcov happy. --- sql/log_event.cc | 2 +- sql/repl_failsafe.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/log_event.cc b/sql/log_event.cc index 8e14a873fc6..05dccd782ad 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -406,7 +406,7 @@ Log_event::Log_event(const char* buf, binlog, so which will cause problems if the user uses this value in CHANGE MASTER). */ - log_pos+= data_written; + log_pos+= data_written; /* purecov: inspected */ } DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos)); diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index f1826678c9f..7f98dd2d64a 100644 --- a/sql/repl_failsafe.cc +++ b/sql/repl_failsafe.cc @@ -191,7 +191,7 @@ int register_slave(THD* thd, uchar* packet, uint packet_length) err: my_free((gptr) si, MYF(MY_WME)); - my_message(ER_UNKNOWN_ERROR, errmsg, MYF(0)); + my_message(ER_UNKNOWN_ERROR, errmsg, MYF(0)); /* purecov: inspected */ err2: return 1; } From 8030bdfc16a647c15e0de5e07b5f53d263ef5ca2 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 20 Mar 2008 19:07:17 +0400 Subject: [PATCH 18/19] BUG#34788 - malformed federated connection url is not handled correctly - crashes server ! Creating federated table with connect string containing empty (zero-length) host name and port is evaluated as 0 (port is incorrect, omitted or 0) crashes server. This happens because federated calls strcmp() with NULL pointer. Fixed by avoiding strcmp() call if hostname is set to NULL. mysql-test/r/federated.result: A test case for BUG#34788. mysql-test/t/federated.test: A test case for BUG#34788. sql/ha_federated.cc: Fixed that parse_url() may call strcmp() with NULL pointer. --- mysql-test/r/federated.result | 2 ++ mysql-test/t/federated.test | 7 +++++++ sql/ha_federated.cc | 9 ++++++++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/federated.result b/mysql-test/r/federated.result index 3a934e7fe3c..685e4d0c335 100644 --- a/mysql-test/r/federated.result +++ b/mysql-test/r/federated.result @@ -2069,6 +2069,8 @@ a b 1 1 DROP TABLE t1; DROP TABLE t1; +CREATE TABLE t1 (a INT) ENGINE=federated CONNECTION='mysql://@:://'; +DROP TABLE t1; DROP TABLE IF EXISTS federated.t1; DROP DATABASE IF EXISTS federated; DROP TABLE IF EXISTS federated.t1; diff --git a/mysql-test/t/federated.test b/mysql-test/t/federated.test index 934db5cd68b..f33dfa3a1b8 100644 --- a/mysql-test/t/federated.test +++ b/mysql-test/t/federated.test @@ -1738,4 +1738,11 @@ DROP TABLE t1; connection slave; DROP TABLE t1; +# +# BUG#34788 - malformed federated connection url is not handled correctly - +# crashes server ! +# +CREATE TABLE t1 (a INT) ENGINE=federated CONNECTION='mysql://@:://'; +DROP TABLE t1; + source include/federated_cleanup.inc; diff --git a/sql/ha_federated.cc b/sql/ha_federated.cc index c0743bd6c9a..a5e4714c53a 100644 --- a/sql/ha_federated.cc +++ b/sql/ha_federated.cc @@ -643,12 +643,19 @@ static int parse_url(FEDERATED_SHARE *share, TABLE *table, if ((strchr(share->table_name, '/'))) goto error; + /* + If hostname is omitted, we set it to NULL. According to + mysql_real_connect() manual: + The value of host may be either a hostname or an IP address. + If host is NULL or the string "localhost", a connection to the + local host is assumed. + */ if (share->hostname[0] == '\0') share->hostname= NULL; if (!share->port) { - if (strcmp(share->hostname, my_localhost) == 0) + if (!share->hostname || strcmp(share->hostname, my_localhost) == 0) share->socket= my_strdup(MYSQL_UNIX_ADDR, MYF(0)); else share->port= MYSQL_PORT; From 5ad505dda2d8ae019f6abfbf6eb9091cea1a5c55 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 25 Mar 2008 12:47:57 +0400 Subject: [PATCH 19/19] BUG#35509 - Federated leaks memory when connecting to localhost/default port When creating federated table that points to unspecified host or localhost on unspecified port or port is 0, small memory leak occurs. This happens because we make a copy of unix socket path, which is never freed. With this fix we do not make a copy of unix socket path, instead share->socket points to MYSQL_UNIX_ADDR constant directly. This fix is covered by a test case for BUG34788. Affects 5.0 only. mysql-test/t/federated.test: A test case for BUG#35509. sql/ha_federated.cc: When creating federated table we call parse_url() to check if connect string is correct. parse_url() may make a copy of unix socket path if port is not specified or 0 and host is not specified or 'localhost'. This copy is never freed. As there is no need to make a copy of unix socket path, let share->socket point to MYSQL_UNIX_ADDR directly. --- mysql-test/t/federated.test | 5 +++++ sql/ha_federated.cc | 3 +-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/mysql-test/t/federated.test b/mysql-test/t/federated.test index f33dfa3a1b8..9e7548a21ed 100644 --- a/mysql-test/t/federated.test +++ b/mysql-test/t/federated.test @@ -1742,6 +1742,11 @@ DROP TABLE t1; # BUG#34788 - malformed federated connection url is not handled correctly - # crashes server ! # +# also tests +# +# BUG#35509 - Federated leaks memory when connecting to localhost/default +# port +# CREATE TABLE t1 (a INT) ENGINE=federated CONNECTION='mysql://@:://'; DROP TABLE t1; diff --git a/sql/ha_federated.cc b/sql/ha_federated.cc index a5e4714c53a..d414dc34f02 100644 --- a/sql/ha_federated.cc +++ b/sql/ha_federated.cc @@ -656,7 +656,7 @@ static int parse_url(FEDERATED_SHARE *share, TABLE *table, if (!share->port) { if (!share->hostname || strcmp(share->hostname, my_localhost) == 0) - share->socket= my_strdup(MYSQL_UNIX_ADDR, MYF(0)); + share->socket= (char*) MYSQL_UNIX_ADDR; else share->port= MYSQL_PORT; } @@ -1342,7 +1342,6 @@ static int free_share(FEDERATED_SHARE *share) { hash_delete(&federated_open_tables, (byte*) share); my_free((gptr) share->scheme, MYF(MY_ALLOW_ZERO_PTR)); - my_free((gptr) share->socket, MYF(MY_ALLOW_ZERO_PTR)); thr_lock_delete(&share->lock); VOID(pthread_mutex_destroy(&share->mutex)); my_free((gptr) share, MYF(0));