Commit 86e8ecc9 authored by guilhem@mysql.com's avatar guilhem@mysql.com

Implementation of WL#1824 "Add replication of character set variables in 4.1",

by binlogging some SET ONE_SHOT CHARACTER_SETetc,
which will be enough until we have it more compact and more complete in 5.0. With the present patch,
replication will work ok between 4.1.3 master and slaves, as long as:
- master and slave have the same GLOBAL.COLLATION_SERVER
- COLLATION_DATABASE and CHARACTER_SET_DATABASE are not used
- application does not use the fact that table is created with charset of the USEd db (BUG#2326).
all of which are not too hard to fulfill. 
ONE_SHOT is reserved for internal use of mysqlbinlog|mysql and works only for charsets,
so we give error if used for non-charset vars.
Fix for BUG#3875 "mysqlbinlog produces wrong ouput  if query uses
 variables containing quotes" and BUG#3943 "Queries with non-ASCII literals are not replicated
 properly after SET NAMES".
Detecting that master and slave have different global charsets or server ids.
parent a5d8f243
stop slave;
drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
reset master;
reset slave;
drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
start slave;
drop database if exists test2;
drop database if exists test3;
create database test2 character set latin2;
set @@character_set_server=latin5;
create database test3;
--- --master--
show create database test2;
Database Create Database
test2 CREATE DATABASE `test2` /*!40100 DEFAULT CHARACTER SET latin2 */
show create database test3;
Database Create Database
test3 CREATE DATABASE `test3` /*!40100 DEFAULT CHARACTER SET latin5 */
--- --slave--
show create database test2;
Database Create Database
test2 CREATE DATABASE `test2` /*!40100 DEFAULT CHARACTER SET latin2 */
show create database test3;
Database Create Database
test3 CREATE DATABASE `test3` /*!40100 DEFAULT CHARACTER SET latin5 */
set @@collation_server=armscii_bin;
drop database test3;
create database test3;
--- --master--
show create database test3;
Database Create Database
test3 CREATE DATABASE `test3` /*!40100 DEFAULT CHARACTER SET armscii8 COLLATE armscii_bin */
--- --slave--
show create database test3;
Database Create Database
test3 CREATE DATABASE `test3` /*!40100 DEFAULT CHARACTER SET armscii8 COLLATE armscii_bin */
use test2;
create table t1 (a int auto_increment primary key, b varchar(100));
set character_set_client=cp850, collation_connection=latin2_croatian_ci;
insert into t1 (b) values(@@character_set_server);
insert into t1 (b) values(@@collation_server);
insert into t1 (b) values(@@character_set_client);
insert into t1 (b) values(@@character_set_connection);
insert into t1 (b) values(@@collation_connection);
--- --master--
select * from t1 order by a;
a b
1 armscii8
2 armscii_bin
3 cp850
4 latin2
5 latin2_croatian_ci
--- --slave--
select * from test2.t1 order by a;
a b
1 armscii8
2 armscii_bin
3 cp850
4 latin2
5 latin2_croatian_ci
set character_set_client=latin1, collation_connection=latin1_german1_ci;
truncate table t1;
insert into t1 (b) values(@@collation_connection);
insert into t1 (b) values(LEAST("Müller","Muffler"));
set collation_connection=latin1_german2_ci;
insert into t1 (b) values(@@collation_connection);
insert into t1 (b) values(LEAST("Müller","Muffler"));
--- --master--
select * from t1 order by a;
a b
1 latin1_german1_ci
2 Muffler
3 latin1_german2_ci
4 Müller
--- --slave--
select * from test2.t1 order by a;
a b
1 latin1_german1_ci
2 Muffler
3 latin1_german2_ci
4 Müller
load data infile '../../std_data/words.dat' into table t1 (b);
set @a= _cp850 'Müller' collate cp850_general_ci;
truncate table t1;
insert into t1 (b) values(collation(@a));
--- --master--
select * from t1 order by a;
a b
1 cp850_general_ci
--- --slave--
select * from test2.t1 order by a;
a b
1 cp850_general_ci
drop database test2;
drop database test3;
show binlog events from 79;
Log_name Pos Event_type Server_id Orig_log_pos Info
master-bin.000001 79 Query 1 79 use `test`; create database test2 character set latin2
master-bin.000001 156 Query 1 156 use `test`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=8,COLLATION_DATABASE=8,COLLATION_SERVER=30
master-bin.000001 290 Query 1 290 use `test`; create database test3
master-bin.000001 346 Query 1 346 use `test`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=8,COLLATION_DATABASE=8,COLLATION_SERVER=64
master-bin.000001 480 Query 1 480 use `test`; drop database test3
master-bin.000001 534 Query 1 534 use `test`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=8,COLLATION_DATABASE=8,COLLATION_SERVER=64
master-bin.000001 668 Query 1 668 use `test`; create database test3
master-bin.000001 724 Query 1 724 use `test2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=8,COLLATION_DATABASE=9,COLLATION_SERVER=64
master-bin.000001 859 Query 1 859 use `test2`; create table t1 (a int auto_increment primary key, b varchar(100))
master-bin.000001 961 Query 1 961 use `test2`; SET ONE_SHOT CHARACTER_SET_CLIENT=4,COLLATION_CONNECTION=27,COLLATION_DATABASE=9,COLLATION_SERVER=64
master-bin.000001 1097 Intvar 1 1097 INSERT_ID=1
master-bin.000001 1125 Query 1 1125 use `test2`; insert into t1 (b) values(@@character_set_server)
master-bin.000001 1210 Query 1 1210 use `test2`; SET ONE_SHOT CHARACTER_SET_CLIENT=4,COLLATION_CONNECTION=27,COLLATION_DATABASE=9,COLLATION_SERVER=64
master-bin.000001 1346 Intvar 1 1346 INSERT_ID=2
master-bin.000001 1374 Query 1 1374 use `test2`; insert into t1 (b) values(@@collation_server)
master-bin.000001 1455 Query 1 1455 use `test2`; SET ONE_SHOT CHARACTER_SET_CLIENT=4,COLLATION_CONNECTION=27,COLLATION_DATABASE=9,COLLATION_SERVER=64
master-bin.000001 1591 Intvar 1 1591 INSERT_ID=3
master-bin.000001 1619 Query 1 1619 use `test2`; insert into t1 (b) values(@@character_set_client)
master-bin.000001 1704 Query 1 1704 use `test2`; SET ONE_SHOT CHARACTER_SET_CLIENT=4,COLLATION_CONNECTION=27,COLLATION_DATABASE=9,COLLATION_SERVER=64
master-bin.000001 1840 Intvar 1 1840 INSERT_ID=4
master-bin.000001 1868 Query 1 1868 use `test2`; insert into t1 (b) values(@@character_set_connection)
master-bin.000001 1957 Query 1 1957 use `test2`; SET ONE_SHOT CHARACTER_SET_CLIENT=4,COLLATION_CONNECTION=27,COLLATION_DATABASE=9,COLLATION_SERVER=64
master-bin.000001 2093 Intvar 1 2093 INSERT_ID=5
master-bin.000001 2121 Query 1 2121 use `test2`; insert into t1 (b) values(@@collation_connection)
master-bin.000001 2206 Query 1 2206 use `test2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=5,COLLATION_DATABASE=9,COLLATION_SERVER=64
master-bin.000001 2341 Query 1 2341 use `test2`; truncate table t1
master-bin.000001 2394 Query 1 2394 use `test2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=5,COLLATION_DATABASE=9,COLLATION_SERVER=64
master-bin.000001 2529 Intvar 1 2529 INSERT_ID=1
master-bin.000001 2557 Query 1 2557 use `test2`; insert into t1 (b) values(@@collation_connection)
master-bin.000001 2642 Query 1 2642 use `test2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=5,COLLATION_DATABASE=9,COLLATION_SERVER=64
master-bin.000001 2777 Intvar 1 2777 INSERT_ID=2
master-bin.000001 2805 Query 1 2805 use `test2`; insert into t1 (b) values(LEAST("Müller","Muffler"))
master-bin.000001 2893 Query 1 2893 use `test2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64
master-bin.000001 3029 Intvar 1 3029 INSERT_ID=3
master-bin.000001 3057 Query 1 3057 use `test2`; insert into t1 (b) values(@@collation_connection)
master-bin.000001 3142 Query 1 3142 use `test2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64
master-bin.000001 3278 Intvar 1 3278 INSERT_ID=4
master-bin.000001 3306 Query 1 3306 use `test2`; insert into t1 (b) values(LEAST("Müller","Muffler"))
master-bin.000001 3394 Query 1 3394 use `test2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64
master-bin.000001 3530 Intvar 1 3530 INSERT_ID=74
master-bin.000001 3558 Create_file 1 3558 db=test2;table=t1;file_id=1;block_len=581
master-bin.000001 4226 Query 1 4226 use `test2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64
master-bin.000001 4362 Intvar 1 4362 INSERT_ID=5
master-bin.000001 4390 Exec_load 1 4390 ;file_id=1
master-bin.000001 4413 Query 1 4413 use `test2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64
master-bin.000001 4549 Query 1 4549 use `test2`; truncate table t1
master-bin.000001 4602 Query 1 4602 use `test2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64
master-bin.000001 4738 Intvar 1 4738 INSERT_ID=1
master-bin.000001 4766 User var 1 4766 @`a`=_cp850'Müller' COLLATE cp850_general_ci
master-bin.000001 4806 Query 1 4806 use `test2`; insert into t1 (b) values(collation(@a))
master-bin.000001 4882 Query 1 4882 use `test2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64
master-bin.000001 5018 Query 1 5018 use `test2`; drop database test2
master-bin.000001 5073 Query 1 5073 SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64
master-bin.000001 5204 Query 1 5204 drop database test3
set global character_set_server=latin2;
ERROR HY000: Binary logging and replication forbid changing the global server character set or collation
set global character_set_server=latin2;
ERROR HY000: Binary logging and replication forbid changing the global server character set or collation
set one_shot @@character_set_server=latin5;
set @@max_join_size=1000;
select @@character_set_server;
@@character_set_server
latin5
select @@character_set_server;
@@character_set_server
latin1
set @@character_set_server=latin5;
select @@character_set_server;
@@character_set_server
latin5
select @@character_set_server;
@@character_set_server
latin5
set one_shot max_join_size=10;
ERROR HY000: The SET ONE_SHOT syntax is reserved for purposes internal to the MySQL server
set character_set_client=9999999;
ERROR 42000: Unknown character set: '9999999'
set collation_server=9999998;
ERROR HY000: Unknown collation: '9999998'
use test;
CREATE TABLE t1 (c1 VARBINARY(255), c2 VARBINARY(255));
SET CHARACTER_SET_CLIENT=koi8r,
CHARACTER_SET_CONNECTION=cp1251,
CHARACTER_SET_RESULTS=koi8r;
INSERT INTO t1 (c1, c2) VALUES ('îÕ, ÚÁ ÒÙÂÁÌËÕ','îÕ, ÚÁ ÒÙÂÁÌËÕ');
select hex(c1), hex(c2) from t1;
hex(c1) hex(c2)
CDF32C20E7E020F0FBE1E0EBEAF3 CDF32C20E7E020F0FBE1E0EBEAF3
select hex(c1), hex(c2) from t1;
hex(c1) hex(c2)
CDF32C20E7E020F0FBE1E0EBEAF3 CDF32C20E7E020F0FBE1E0EBEAF3
drop table t1;
......@@ -15,8 +15,5 @@ start slave;
insert into t1 values (1);
show status like "slave_running";
Variable_name Value
Slave_running ON
select * from t1;
n
1
Slave_running OFF
drop table t1;
......@@ -86,11 +86,11 @@ slave-bin.000001 313 Query 1 313 use `test`; insert into t1 values (@i1), (@i2),
slave-bin.000001 396 User var 2 396 @r1=12.5
slave-bin.000001 439 User var 2 439 @r2=-12.5
slave-bin.000001 482 Query 1 482 use `test`; insert into t1 values (@r1), (@r2)
slave-bin.000001 551 User var 2 551 @s1='This is a test'
slave-bin.000001 600 User var 2 600 @s2=''
slave-bin.000001 635 User var 2 635 @s3='abc'def'
slave-bin.000001 677 User var 2 677 @s4='abc\def'
slave-bin.000001 719 User var 2 719 @s5='abc'def'
slave-bin.000001 551 User var 2 551 @s1=_latin1'This is a test' COLLATE latin1_swedish_ci
slave-bin.000001 600 User var 2 600 @s2=_latin1'' COLLATE latin1_swedish_ci
slave-bin.000001 635 User var 2 635 @s3=_latin1'abc'def' COLLATE latin1_swedish_ci
slave-bin.000001 677 User var 2 677 @s4=_latin1'abc\def' COLLATE latin1_swedish_ci
slave-bin.000001 719 User var 2 719 @s5=_latin1'abc'def' COLLATE latin1_swedish_ci
slave-bin.000001 761 Query 1 761 use `test`; insert into t1 values (@s1), (@s2), (@s3), (@s4), (@s5)
slave-bin.000001 851 User var 2 851 @n1=NULL
slave-bin.000001 877 Query 1 877 use `test`; insert into t1 values (@n1)
......@@ -99,7 +99,7 @@ slave-bin.000001 965 Query 1 965 use `test`; insert into t1 values (@n2)
slave-bin.000001 1027 Query 1 1027 use `test`; insert into t1 values (@a:=0), (@a:=@a+1), (@a:=@a+1)
slave-bin.000001 1115 User var 2 1115 @a=2
slave-bin.000001 1157 Query 1 1157 use `test`; insert into t1 values (@a+(@b:=@a+1))
slave-bin.000001 1229 User var 2 1229 @q='abc'
slave-bin.000001 1229 User var 2 1229 @q=_latin1'abc' COLLATE latin1_swedish_ci
slave-bin.000001 1266 Query 1 1266 use `test`; insert t1 values (@q), (@q:=concat(@q, 'n1')), (@q:=concat(@q, 'n2'))
slave-bin.000001 1370 User var 2 1370 @a=5
slave-bin.000001 1412 Query 1 1412 use `test`; insert into t1 values (@a),(@a)
......
......@@ -162,3 +162,36 @@ charset(@a) collation(@a) coercibility(@a)
latin2 latin2_bin 0
select (@a:=_latin2'test' collate latin2_bin) = _latin2'TEST' collate latin2_general_ci;
ERROR HY000: Illegal mix of collations (latin2_bin,EXPLICIT) and (latin2_general_ci,EXPLICIT) for operation '='
create table t1 (a varchar(50));
reset master;
SET TIMESTAMP=10000;
SET @`a b`='hello';
INSERT INTO t1 VALUES(@`a b`);
set @var1= "';aaa";
insert into t1 values (@var1);
create table t2 (c char(30)) charset=ucs2;
set @v=convert('abc' using ucs2);
insert into t2 values (@v);
show binlog events from 79;
Log_name Pos Event_type Server_id Orig_log_pos Info
master-bin.000001 79 User var 1 79 @`a b`=_latin1'hello' COLLATE latin1_swedish_ci
master-bin.000001 120 Query 1 120 use `test`; INSERT INTO t1 VALUES(@`a b`)
master-bin.000001 184 User var 1 184 @`var1`=_latin1'\';aaa' COLLATE latin1_swedish_ci
master-bin.000001 226 Query 1 226 use `test`; insert into t1 values (@var1)
master-bin.000001 290 Query 1 290 use `test`; create table t2 (c char(30)) charset=ucs2
master-bin.000001 366 User var 1 366 @`v`=_ucs2'\0a\0b\0c' COLLATE ucs2_general_ci
master-bin.000001 406 Query 1 406 use `test`; insert into t2 values (@v)
/*!40019 SET @@session.max_insert_delayed_threads=0*/;
SET @`a b`:=_latin1'hello' COLLATE latin1_swedish_ci;
use test;
SET TIMESTAMP=10000;
INSERT INTO t1 VALUES(@`a b`);
SET @`var1`:=_latin1'\';aaa' COLLATE latin1_swedish_ci;
SET TIMESTAMP=10000;
insert into t1 values (@var1);
SET TIMESTAMP=10000;
create table t2 (c char(30)) charset=ucs2;
SET @`v`:=_ucs2'\0a\0b\0c' COLLATE ucs2_general_ci;
SET TIMESTAMP=10000;
insert into t2 values (@v);
drop table t1, t2;
# Replication of character sets.
# This test will fail if the server/client does not support enough charsets.
# Remember that there currently exists
# Bug #2326: Charset of table is determined by charset of db only if "USE db;"
source include/master-slave.inc;
--disable_warnings
drop database if exists test2;
drop database if exists test3;
--enable_warnings
create database test2 character set latin2;
set @@character_set_server=latin5;
create database test3;
--disable_query_log
select "--- --master--" as "";
--enable_query_log
show create database test2;
show create database test3;
sync_slave_with_master;
--disable_query_log
select "--- --slave--" as "";
--enable_query_log
show create database test2;
show create database test3;
connection master;
set @@collation_server=armscii_bin;
drop database test3;
create database test3;
--disable_query_log
select "--- --master--" as "";
--enable_query_log
show create database test3;
sync_slave_with_master;
--disable_query_log
select "--- --slave--" as "";
--enable_query_log
show create database test3;
connection master;
use test2;
create table t1 (a int auto_increment primary key, b varchar(100));
set character_set_client=cp850, collation_connection=latin2_croatian_ci;
insert into t1 (b) values(@@character_set_server);
insert into t1 (b) values(@@collation_server);
# character_set_database and collation_database are not tested as they
# are not replicated (Bar said that this variable may be removed shortly).
insert into t1 (b) values(@@character_set_client);
# collation_client does not exist
insert into t1 (b) values(@@character_set_connection);
insert into t1 (b) values(@@collation_connection);
--disable_query_log
select "--- --master--" as "";
--enable_query_log
select * from t1 order by a;
sync_slave_with_master;
--disable_query_log
select "--- --slave--" as "";
--enable_query_log
select * from test2.t1 order by a;
connection master;
set character_set_client=latin1, collation_connection=latin1_german1_ci;
truncate table t1;
insert into t1 (b) values(@@collation_connection);
insert into t1 (b) values(LEAST("Müller","Muffler"));
set collation_connection=latin1_german2_ci;
insert into t1 (b) values(@@collation_connection);
insert into t1 (b) values(LEAST("Müller","Muffler"));
--disable_query_log
select "--- --master--" as "";
--enable_query_log
select * from t1 order by a;
sync_slave_with_master;
--disable_query_log
select "--- --slave--" as "";
--enable_query_log
select * from test2.t1 order by a;
# See if SET ONE_SHOT gets into binlog when LOAD DATA
connection master;
load data infile '../../std_data/words.dat' into table t1 (b);
# See if user var is prefixed with collation in binlog and replicated well.
# Note: replication of user variables is broken as far as derivation is
# concerned. That's because when we store a user variable in the binlog,
# we lose its derivation. So later on the slave, it's impossible to
# know if the collation was explicit or not, so we use DERIVATION_NONE,
# which provokes error messages (like 'Illegal mix of collation') when
# we replay the master's INSERT/etc statements.
set @a= _cp850 'Müller' collate cp850_general_ci;
truncate table t1;
insert into t1 (b) values(collation(@a));
--disable_query_log
select "--- --master--" as "";
--enable_query_log
select * from t1 order by a;
sync_slave_with_master;
--disable_query_log
select "--- --slave--" as "";
--enable_query_log
select * from test2.t1 order by a;
connection master;
drop database test2;
drop database test3;
show binlog events from 79;
sync_slave_with_master;
# Check that we can't change global.collation_server
error 1105;
set global character_set_server=latin2;
connection master;
error 1105;
set global character_set_server=latin2;
# Check that SET ONE_SHOT is really one shot
set one_shot @@character_set_server=latin5;
set @@max_join_size=1000;
select @@character_set_server;
select @@character_set_server;
set @@character_set_server=latin5;
select @@character_set_server;
select @@character_set_server;
# ONE_SHOT on not charset/collation stuff is not allowed
error 1105;
set one_shot max_join_size=10;
# Test of wrong character set numbers;
error 1115;
set character_set_client=9999999;
error 1273;
set collation_server=9999998;
# This one was contributed by Sergey Petrunia (BUG#3943)
use test;
CREATE TABLE t1 (c1 VARBINARY(255), c2 VARBINARY(255));
SET CHARACTER_SET_CLIENT=koi8r,
CHARACTER_SET_CONNECTION=cp1251,
CHARACTER_SET_RESULTS=koi8r;
INSERT INTO t1 (c1, c2) VALUES ('îÕ, ÚÁ ÒÙÂÁÌËÕ','îÕ, ÚÁ ÒÙÂÁÌËÕ');
select hex(c1), hex(c2) from t1;
sync_slave_with_master;
select hex(c1), hex(c2) from t1;
connection master;
drop table t1;
sync_slave_with_master;
# This test checks that a slave does not execute queries originating
# from itself, by default.
# This test checks that the slave I/O thread refuses to start if slave
# and master have the same server id (because this is a useless setup,
# and otherwise SHOW SLAVE STATUS shows progress but all queries are
# ignored, which has caught our customers), unless
# --replicate-same-server-id.
source include/master-slave.inc;
connection slave;
......@@ -18,5 +21,4 @@ insert into t1 values (1);
# (when slave is its own master without --replicate-same-server-id)
sleep 2; # enough time for the event to be replicated (it should not)
show status like "slave_running";
select * from t1;
drop table t1;
......@@ -99,3 +99,24 @@ select (@a:=_latin2'test' collate latin2_bin) = _latin2'TEST';
select charset(@a),collation(@a),coercibility(@a);
--error 1267
select (@a:=_latin2'test' collate latin2_bin) = _latin2'TEST' collate latin2_general_ci;
# Check that user variables are binlogged correctly (BUG#3875)
create table t1 (a varchar(50));
reset master;
SET TIMESTAMP=10000;
SET @`a b`='hello';
INSERT INTO t1 VALUES(@`a b`);
set @var1= "';aaa";
insert into t1 values (@var1);
create table t2 (c char(30)) charset=ucs2;
set @v=convert('abc' using ucs2);
insert into t2 values (@v);
show binlog events from 79;
# more important than SHOW BINLOG EVENTS, mysqlbinlog (where we
# absolutely need variables names to be quoted and strings to be
# escaped).
--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR
--exec $MYSQL_BINLOG --short-form $MYSQL_TEST_DIR/var/log/master-bin.000001
drop table t1, t2;
......@@ -310,6 +310,7 @@ static SYMBOL symbols[] = {
{ "OFFSET", SYM(OFFSET_SYM)},
{ "OLD_PASSWORD", SYM(OLD_PASSWORD)},
{ "ON", SYM(ON)},
{ "ONE_SHOT", SYM(ONE_SHOT_SYM)},
{ "OPEN", SYM(OPEN_SYM)},
{ "OPTIMIZE", SYM(OPTIMIZE)},
{ "OPTION", SYM(OPTION)},
......
......@@ -1231,6 +1231,40 @@ bool MYSQL_LOG::write(Log_event* event_info)
if (thd)
{
#if MYSQL_VERSION_ID < 50000
/*
To make replication of charsets working in 4.1 we are writing values
of charset related variables before every statement in the binlog,
if values of those variables differ from global server-wide defaults.
We are using SET ONE_SHOT command so that the charset vars get reset
to default after the first non-SET statement.
In the next 5.0 this won't be needed as we will use the new binlog
format to store charset info.
*/
if ((thd->variables.character_set_client->number !=
global_system_variables.collation_server->number) ||
(thd->variables.character_set_client->number !=
thd->variables.collation_connection->number) ||
(thd->variables.collation_server->number !=
thd->variables.collation_connection->number))
{
char buf[200];
int written= my_snprintf(buf, sizeof(buf)-1,
"SET ONE_SHOT CHARACTER_SET_CLIENT=%lu,\
COLLATION_CONNECTION=%lu,COLLATION_DATABASE=%lu,COLLATION_SERVER=%lu",
thd->variables.character_set_client->number,
thd->variables.collation_connection->number,
thd->variables.collation_database->number,
thd->variables.collation_server->number);
Query_log_event e(thd, buf, written, 0);
e.set_log_pos(this);
if (e.write(file))
goto err;
}
#endif
/* Add logging of timezones here */
if (thd->last_insert_id_used)
{
Intvar_log_event e(thd,(uchar) LAST_INSERT_ID_EVENT,
......
......@@ -2176,7 +2176,7 @@ int Rand_log_event::exec_event(struct st_relay_log_info* rli)
void User_var_log_event::pack_info(Protocol* protocol)
{
char *buf= 0;
uint val_offset= 2 + name_len;
uint val_offset= 4 + name_len;
uint event_len= val_offset;
if (is_null)
......@@ -2200,16 +2200,21 @@ void User_var_log_event::pack_info(Protocol* protocol)
event_len= longlong10_to_str(uint8korr(val), buf + val_offset,-10)-buf;
break;
case STRING_RESULT:
/*
This is correct as pack_info is used for SHOW BINLOG command
only. But be carefull this is may be incorrect in other cases as
string may contain \ and '.
*/
event_len= val_offset + 2 + val_len;
buf= my_malloc(event_len, MYF(MY_WME));
buf[val_offset]= '\'';
memcpy(buf + val_offset + 1, val, val_len);
buf[val_offset + val_len + 1]= '\'';
/* 15 is for 'COLLATE' and other chars */
buf= my_malloc(event_len+val_len*2+1+2*MY_CS_NAME_SIZE+15, MYF(MY_WME));
CHARSET_INFO *cs;
if (!(cs= get_charset(charset_number, MYF(0))))
{
strmov(buf+val_offset, "???");
event_len+= 3;
}
else
{
char *p= strxmov(buf + val_offset, "_", cs->csname, "'", NullS);
p+= escape_string_for_mysql(&my_charset_bin, p, val, val_len);
p= strxmov(p, "' COLLATE ", cs->name, NullS);
event_len= p-buf;
}
break;
case ROW_RESULT:
default:
......@@ -2218,8 +2223,10 @@ void User_var_log_event::pack_info(Protocol* protocol)
}
}
buf[0]= '@';
buf[1+name_len]= '=';
memcpy(buf+1, name, name_len);
buf[1]= '`';
buf[2+name_len]= '`';
buf[3+name_len]= '=';
memcpy(buf+2, name, name_len);
protocol->store(buf, event_len, &my_charset_bin);
my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
}
......@@ -2311,8 +2318,9 @@ void User_var_log_event::print(FILE* file, bool short_form, char* last_db)
fprintf(file, "\tUser_var\n");
}
fprintf(file, "SET @");
fprintf(file, "SET @`");
my_fwrite(file, (byte*) name, (uint) (name_len), MYF(MY_NABP | MY_WME));
fprintf(file, "`");
if (is_null)
{
......@@ -2332,7 +2340,36 @@ void User_var_log_event::print(FILE* file, bool short_form, char* last_db)
fprintf(file, ":=%s;\n", int_buf);
break;
case STRING_RESULT:
fprintf(file, ":='%s';\n", val);
{
char *p;
if (!(p= (char *)my_alloca(2*val_len+1)))
break; // no error, as we are 'void'
escape_string_for_mysql(&my_charset_bin, p, val, val_len);
#if MYSQL_VERSION_ID < 50000
/*
For proper behaviour when mysqlbinlog|mysql, we need to explicitely
specify the variable's collation. It will however cause problems when
people want to mysqlbinlog|mysql into another server not supporting the
character set. But there's not much to do about this and it's unlikely.
*/
CHARSET_INFO *cs;
if (!(cs= get_charset(charset_number, MYF(0))))
/*
Generate an unusable command (=> syntax error) is probably the best
thing we can do here.
*/
fprintf(file, ":=???;\n");
else
fprintf(file, ":=_%s'%s' COLLATE %s;\n", cs->csname, p, cs->name);
#else
/*
In 5.0 we will have some SET CHARACTER_SET_ect automatically printed
for all events where it's needed.
*/
fprintf(file, ":='%s';\n", p);
#endif
my_afree(p);
}
break;
case ROW_RESULT:
default:
......@@ -2353,7 +2390,9 @@ void User_var_log_event::print(FILE* file, bool short_form, char* last_db)
int User_var_log_event::exec_event(struct st_relay_log_info* rli)
{
Item *it= 0;
CHARSET_INFO *charset= get_charset(charset_number, MYF(0));
CHARSET_INFO *charset;
if (!(charset= get_charset(charset_number, MYF(MY_WME))))
return 1;
LEX_STRING user_var_name;
user_var_name.str= name;
user_var_name.length= name_len;
......
......@@ -1708,19 +1708,31 @@ CHARSET_INFO *get_old_charset_by_name(const char *name)
bool sys_var_collation::check(THD *thd, set_var *var)
{
CHARSET_INFO *tmp;
char buff[80];
String str(buff,sizeof(buff), system_charset_info), *res;
if (!(res=var->value->val_str(&str)))
if (var->value->result_type() == STRING_RESULT)
{
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, "NULL");
return 1;
char buff[80];
String str(buff,sizeof(buff), system_charset_info), *res;
if (!(res=var->value->val_str(&str)))
{
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, "NULL");
return 1;
}
if (!(tmp=get_charset_by_name(res->c_ptr(),MYF(0))))
{
my_error(ER_UNKNOWN_COLLATION, MYF(0), res->c_ptr());
return 1;
}
}
if (!(tmp=get_charset_by_name(res->c_ptr(),MYF(0))))
else // INT_RESULT
{
my_error(ER_UNKNOWN_COLLATION, MYF(0), res->c_ptr());
return 1;
if (!(tmp=get_charset(var->value->val_int(),MYF(0))))
{
char buf[20];
int10_to_str(var->value->val_int(), buf, -10);
my_error(ER_UNKNOWN_COLLATION, MYF(0), buf);
return 1;
}
}
var->save_result.charset= tmp; // Save for update
return 0;
......@@ -1730,23 +1742,36 @@ bool sys_var_collation::check(THD *thd, set_var *var)
bool sys_var_character_set::check(THD *thd, set_var *var)
{
CHARSET_INFO *tmp;
char buff[80];
String str(buff,sizeof(buff), system_charset_info), *res;
if (!(res=var->value->val_str(&str)))
{
if (!nullable)
if (var->value->result_type() == STRING_RESULT)
{
char buff[80];
String str(buff,sizeof(buff), system_charset_info), *res;
if (!(res=var->value->val_str(&str)))
{
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, "NULL");
if (!nullable)
{
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, "NULL");
return 1;
}
tmp= NULL;
}
else if (!(tmp=get_charset_by_csname(res->c_ptr(),MY_CS_PRIMARY,MYF(0))) &&
!(tmp=get_old_charset_by_name(res->c_ptr())))
{
my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), res->c_ptr());
return 1;
}
tmp= NULL;
}
else if (!(tmp=get_charset_by_csname(res->c_ptr(),MY_CS_PRIMARY,MYF(0))) &&
!(tmp=get_old_charset_by_name(res->c_ptr())))
else // INT_RESULT
{
my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), res->c_ptr());
return 1;
if (!(tmp=get_charset(var->value->val_int(),MYF(0))))
{
char buf[20];
int10_to_str(var->value->val_int(), buf, -10);
my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), buf);
return 1;
}
}
var->save_result.charset= tmp; // Save for update
return 0;
......@@ -1859,6 +1884,20 @@ void sys_var_character_set_server::set_default(THD *thd, enum_var_type type)
}
}
#if defined(HAVE_REPLICATION) && (MYSQL_VERSION_ID < 50000)
bool sys_var_character_set_server::check(THD *thd, set_var *var)
{
if ((var->type == OPT_GLOBAL) &&
(mysql_bin_log.is_open() ||
active_mi->slave_running || active_mi->rli.slave_running))
{
my_printf_error(0, "Binary logging and replication forbid changing \
the global server character set or collation", MYF(0));
return 1;
}
return sys_var_character_set::check(thd,var);
}
#endif
CHARSET_INFO ** sys_var_character_set_database::ci_ptr(THD *thd,
enum_var_type type)
......@@ -1952,6 +1991,20 @@ void sys_var_collation_database::set_default(THD *thd, enum_var_type type)
}
}
#if defined(HAVE_REPLICATION) && (MYSQL_VERSION_ID < 50000)
bool sys_var_collation_server::check(THD *thd, set_var *var)
{
if ((var->type == OPT_GLOBAL) &&
(mysql_bin_log.is_open() ||
active_mi->slave_running || active_mi->rli.slave_running))
{
my_printf_error(0, "Binary logging and replication forbid changing \
the global server character set or collation", MYF(0));
return 1;
}
return sys_var_collation::check(thd,var);
}
#endif
bool sys_var_collation_server::update(THD *thd, set_var *var)
{
......@@ -2524,6 +2577,36 @@ int sql_set_variables(THD *thd, List<set_var_base> *var_list)
}
/*
Say if all variables set by a SET support the ONE_SHOT keyword (currently,
only character set and collation do; later timezones will).
SYNOPSIS
not_all_support_one_shot
set_var List of variables to update
NOTES
It has a "not_" because it makes faster tests (no need to "!")
RETURN VALUE
0 all variables of the list support ONE_SHOT
1 at least one does not support ONE_SHOT
*/
bool not_all_support_one_shot(List<set_var_base> *var_list)
{
List_iterator_fast<set_var_base> it(*var_list);
set_var_base *var;
while ((var= it++))
{
if (var->no_support_one_shot())
return 1;
}
return 0;
}
/*****************************************************************************
Functions to handle SET mysql_internal_variable=const_expr
*****************************************************************************/
......
......@@ -49,10 +49,20 @@ class sys_var
const char *name;
sys_after_update_func after_update;
sys_var(const char *name_arg) :name(name_arg),after_update(0)
{}
#if MYSQL_VERSION_ID < 50000
bool no_support_one_shot;
#endif
sys_var(const char *name_arg)
:name(name_arg), after_update(0)
#if MYSQL_VERSION_ID < 50000
, no_support_one_shot(1)
#endif
{}
sys_var(const char *name_arg,sys_after_update_func func)
:name(name_arg),after_update(func)
:name(name_arg), after_update(func)
#if MYSQL_VERSION_ID < 50000
, no_support_one_shot(1)
#endif
{}
virtual ~sys_var() {}
virtual bool check(THD *thd, set_var *var);
......@@ -487,12 +497,17 @@ class sys_var_rand_seed2 :public sys_var
class sys_var_collation :public sys_var_thd
{
public:
sys_var_collation(const char *name_arg) :sys_var_thd(name_arg) {}
sys_var_collation(const char *name_arg) :sys_var_thd(name_arg)
{
#if MYSQL_VERSION_ID < 50000
no_support_one_shot= 0;
#endif
}
bool check(THD *thd, set_var *var);
SHOW_TYPE type() { return SHOW_CHAR; }
bool check_update_type(Item_result type)
{
return type != STRING_RESULT; /* Only accept strings */
return ((type != STRING_RESULT) && (type != INT_RESULT));
}
bool check_default(enum_var_type type) { return 0; }
virtual void set_default(THD *thd, enum_var_type type)= 0;
......@@ -502,13 +517,23 @@ class sys_var_character_set :public sys_var_thd
{
public:
bool nullable;
sys_var_character_set(const char *name_arg) :sys_var_thd(name_arg)
{ nullable= 0; }
sys_var_character_set(const char *name_arg) :
sys_var_thd(name_arg)
{
nullable= 0;
#if MYSQL_VERSION_ID < 50000
/*
In fact only almost all variables derived from sys_var_character_set
support ONE_SHOT; character_set_results doesn't. But that's good enough.
*/
no_support_one_shot= 0;
#endif
}
bool check(THD *thd, set_var *var);
SHOW_TYPE type() { return SHOW_CHAR; }
bool check_update_type(Item_result type)
{
return type != STRING_RESULT; /* Only accept strings */
return ((type != STRING_RESULT) && (type != INT_RESULT));
}
bool check_default(enum_var_type type) { return 0; }
bool update(THD *thd, set_var *var);
......@@ -541,6 +566,9 @@ class sys_var_character_set_server :public sys_var_character_set
public:
sys_var_character_set_server(const char *name_arg) :
sys_var_character_set(name_arg) {}
#if defined(HAVE_REPLICATION) && (MYSQL_VERSION_ID < 50000)
bool check(THD *thd, set_var *var);
#endif
void set_default(THD *thd, enum_var_type type);
CHARSET_INFO **ci_ptr(THD *thd, enum_var_type type);
};
......@@ -576,6 +604,9 @@ class sys_var_collation_server :public sys_var_collation
{
public:
sys_var_collation_server(const char *name_arg) :sys_var_collation(name_arg) {}
#if defined(HAVE_REPLICATION) && (MYSQL_VERSION_ID < 50000)
bool check(THD *thd, set_var *var);
#endif
bool update(THD *thd, set_var *var);
void set_default(THD *thd, enum_var_type type);
byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
......@@ -689,7 +720,10 @@ class set_var_base :public Sql_alloc
virtual int check(THD *thd)=0; /* To check privileges etc. */
virtual int update(THD *thd)=0; /* To set the value */
/* light check for PS */
virtual int light_check(THD *thd) { return check(thd); }
virtual int light_check(THD *thd) { return check(thd); }
#if MYSQL_VERSION_ID < 50000
virtual bool no_support_one_shot() { return 1; }
#endif
};
......@@ -731,6 +765,9 @@ class set_var :public set_var_base
int check(THD *thd);
int update(THD *thd);
int light_check(THD *thd);
#if MYSQL_VERSION_ID < 50000
bool no_support_one_shot() { return var->no_support_one_shot; }
#endif
};
......@@ -833,6 +870,7 @@ void set_var_init();
void set_var_free();
sys_var *find_sys_var(const char *str, uint length=0);
int sql_set_variables(THD *thd, List<set_var_base> *var_list);
bool not_all_support_one_shot(List<set_var_base> *var_list);
void fix_delay_key_write(THD *thd, enum_var_type type);
ulong fix_sql_mode(ulong sql_mode);
extern sys_var_str sys_charset_system;
......
......@@ -72,7 +72,7 @@ static int safe_sleep(THD* thd, int sec, CHECK_KILLED_FUNC thread_killed,
static int request_table_dump(MYSQL* mysql, const char* db, const char* table);
static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db,
const char* table_name, bool overwrite);
static int check_master_version_and_clock(MYSQL* mysql, MASTER_INFO* mi);
static int get_master_version_and_clock(MYSQL* mysql, MASTER_INFO* mi);
/*
......@@ -1187,38 +1187,75 @@ slaves can't replicate a 5.0 or newer master.";
break;
}
MYSQL_RES *master_clock_res;
MYSQL_ROW master_clock_row;
time_t slave_clock;
if (mysql_real_query(mysql, "SELECT UNIX_TIMESTAMP()", 23))
errmsg= "\"SELECT UNIX_TIMESTAMP()\" failed on master";
else if (!(master_clock_res= mysql_store_result(mysql)))
/*
Compare the master and slave's clock. Do not die if master's clock is
unavailable (very old master not supporting UNIX_TIMESTAMP()?).
*/
MYSQL_RES *master_res= 0;
MYSQL_ROW master_row;
if (!mysql_real_query(mysql, "SELECT UNIX_TIMESTAMP()", 23) &&
(master_res= mysql_store_result(mysql)) &&
(master_row= mysql_fetch_row(master_res)))
{
errmsg= "Could not read the result of \"SELECT UNIX_TIMESTAMP()\" on \
master";
mi->clock_diff_with_master=
(long) (time((time_t*) 0) - strtoul(master_row[0], 0, 10));
}
else
else
{
if (!(master_clock_row= mysql_fetch_row(master_clock_res)))
errmsg= "Could not read a row from the result of \"SELECT \
UNIX_TIMESTAMP()\" on master";
else
{
slave_clock= time((time_t*) 0);
mi->clock_diff_with_master= (long) (slave_clock -
strtoul(master_clock_row[0], 0, 10));
DBUG_PRINT("info",("slave_clock=%lu, master_clock=%s",
slave_clock, master_clock_row[0]));
}
mysql_free_result(master_clock_res);
mi->clock_diff_with_master= 0; /* The "most sensible" value */
sql_print_error("Warning: \"SELECT UNIX_TIMESTAMP()\" failed on master, \
do not trust column Seconds_Behind_Master of SHOW SLAVE STATUS");
}
if (master_res)
mysql_free_result(master_res);
/*
Check that the master's server id and ours are different. Because if they
are equal (which can result from a simple copy of master's datadir to slave,
thus copying some my.cnf), replication will work but all events will be
skipped.
Do not die if SHOW VARIABLES LIKE 'SERVER_ID' fails on master (very old
master?).
Note: we could have put a @@SERVER_ID in the previous SELECT
UNIX_TIMESTAMP() instead, but this would not have worked on 3.23 masters.
*/
if (!mysql_real_query(mysql, "SHOW VARIABLES LIKE 'SERVER_ID'", 31) &&
(master_res= mysql_store_result(mysql)))
{
if ((master_row= mysql_fetch_row(master_res)) &&
(::server_id == strtoul(master_row[1], 0, 10)) &&
!replicate_same_server_id)
errmsg= "The slave I/O thread stops because master and slave have equal \
MySQL server ids; these ids must be different for replication to work (or \
the --replicate-same-server-id option must be used on slave but this does \
not always make sense; please check the manual before using it).";
mysql_free_result(master_res);
}
/*
Check that the master's global character_set_server and ours are the same.
Not fatal if query fails (old master?).
*/
if (!mysql_real_query(mysql, "SELECT @@GLOBAL.COLLATION_SERVER", 32) &&
(master_res= mysql_store_result(mysql)))
{
if ((master_row= mysql_fetch_row(master_res)) &&
strcmp(master_row[0], global_system_variables.collation_server->name))
errmsg= "The slave I/O thread stops because master and slave have \
different values for the COLLATION_SERVER global variable. The values must \
be equal for replication to work";
mysql_free_result(master_res);
}
/* Add a timezones check here */
if (errmsg)
{
sql_print_error(errmsg);
return 1;
}
return 0;
}
......
......@@ -181,6 +181,7 @@ THD::THD():user_time(0), current_statement(0), is_fatal_error(0),
current_linfo = 0;
slave_thread = 0;
variables.pseudo_thread_id= 0;
one_shot_set= 0;
file_id = 0;
warn_id= 0;
db_charset= global_system_variables.collation_database;
......
......@@ -801,7 +801,7 @@ class THD :public ilink,
/* scramble - random string sent to client on handshake */
char scramble[SCRAMBLE_LENGTH+1];
bool slave_thread;
bool slave_thread, one_shot_set;
bool locked, some_tables_deleted;
bool last_cuted_field;
bool no_errors, password, is_fatal_error;
......
......@@ -611,7 +611,7 @@ typedef struct st_lex
uint fk_delete_opt, fk_update_opt, fk_match_option;
uint slave_thd_opt;
uint8 describe;
bool drop_if_exists, drop_temporary, local_file;
bool drop_if_exists, drop_temporary, local_file, one_shot_set;
bool in_comment, ignore_space, verbose, no_write_to_binlog;
bool derived_tables;
bool safe_to_cache_query;
......
......@@ -2918,14 +2918,31 @@ mysql_execute_command(THD *thd)
}
case SQLCOM_SET_OPTION:
{
List<set_var_base> *lex_var_list= &lex->var_list;
if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) ||
(res= open_and_lock_tables(thd,tables))))
break;
if (!(res= sql_set_variables(thd, &lex->var_list)))
if (lex->one_shot_set && not_all_support_one_shot(lex_var_list))
{
my_printf_error(0, "The SET ONE_SHOT syntax is reserved for \
purposes internal to the MySQL server", MYF(0));
res= -1;
break;
}
if (!(res= sql_set_variables(thd, lex_var_list)))
{
/*
If the previous command was a SET ONE_SHOT, we don't want to forget
about the ONE_SHOT property of that SET. So we use a |= instead of = .
*/
thd->one_shot_set|= lex->one_shot_set;
send_ok(thd);
}
if (thd->net.report_error)
res= -1;
break;
}
case SQLCOM_UNLOCK_TABLES:
unlock_locked_tables(thd);
......@@ -3377,6 +3394,29 @@ mysql_execute_command(THD *thd)
break;
}
thd->proc_info="query end"; // QQ
if (thd->one_shot_set)
{
/*
If this is a SET, do nothing. This is to allow mysqlbinlog to print
many SET commands (in this case we want the charset temp setting to
live until the real query). This is also needed so that SET
CHARACTER_SET_CLIENT... does not cancel itself immediately.
*/
if (lex->sql_command != SQLCOM_SET_OPTION)
{
thd->variables.character_set_client=
global_system_variables.character_set_client;
thd->variables.collation_connection=
global_system_variables.collation_connection;
thd->variables.collation_database=
global_system_variables.collation_database;
thd->variables.collation_server=
global_system_variables.collation_server;
thd->update_charset();
/* Add timezone stuff here */
thd->one_shot_set= 0;
}
}
if (res < 0)
send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0);
......
......@@ -316,6 +316,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token NUM
%token OFFSET_SYM
%token ON
%token ONE_SHOT_SYM
%token OPEN_SYM
%token OPTION
%token OPTIONALLY
......@@ -5002,6 +5003,7 @@ keyword:
| NVARCHAR_SYM {}
| OFFSET_SYM {}
| OLD_PASSWORD {}
| ONE_SHOT_SYM {}
| OPEN_SYM {}
| PACK_KEYS_SYM {}
| PARTIAL {}
......@@ -5088,6 +5090,7 @@ set:
lex->sql_command= SQLCOM_SET_OPTION;
lex->option_type=OPT_SESSION;
lex->var_list.empty();
lex->one_shot_set= 0;
}
option_value_list
{}
......@@ -5106,6 +5109,7 @@ option_type:
| GLOBAL_SYM { Lex->option_type= OPT_GLOBAL; }
| LOCAL_SYM { Lex->option_type= OPT_SESSION; }
| SESSION_SYM { Lex->option_type= OPT_SESSION; }
| ONE_SHOT_SYM { Lex->option_type= OPT_SESSION; Lex->one_shot_set= 1; }
;
opt_var_type:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment