Commit 0055056c authored by malff/marcsql@weblab.(none)'s avatar malff/marcsql@weblab.(none)

Merge weblab.(none):/home/marcsql/TREE/mysql-5.1-base

into  weblab.(none):/home/marcsql/TREE/mysql-5.1-merge
parents 60a3f102 c5586269
...@@ -5223,3 +5223,11 @@ check table bug22080_3; ...@@ -5223,3 +5223,11 @@ check table bug22080_3;
Table Op Msg_type Msg_text Table Op Msg_type Msg_text
test.bug22080_3 check error Corrupt test.bug22080_3 check error Corrupt
drop tables bug22080_1,bug22080_2,bug22080_3; drop tables bug22080_1,bug22080_2,bug22080_3;
CREATE TABLE `bug21328` (
`col1` int(11) DEFAULT NULL,
`col2` int(11) DEFAULT NULL,
`col3` int(11) DEFAULT NULL
) ENGINE=CSV;
insert into bug21328 values (1,NULL,NULL);
alter table bug21328 engine=myisam;
drop table bug21328;
...@@ -111,9 +111,6 @@ slow_log CREATE TABLE `slow_log` ( ...@@ -111,9 +111,6 @@ slow_log CREATE TABLE `slow_log` (
) ENGINE=CSV DEFAULT CHARSET=utf8 COMMENT='Slow log' ) ENGINE=CSV DEFAULT CHARSET=utf8 COMMENT='Slow log'
alter table mysql.general_log engine=myisam; alter table mysql.general_log engine=myisam;
alter table mysql.slow_log engine=myisam; alter table mysql.slow_log engine=myisam;
Warnings:
Warning 1366 Incorrect integer value: '' for column 'last_insert_id' at row 0
Warning 1366 Incorrect integer value: '' for column 'insert_id' at row 0
show create table mysql.general_log; show create table mysql.general_log;
Table Create Table Table Create Table
general_log CREATE TABLE `general_log` ( general_log CREATE TABLE `general_log` (
......
...@@ -39,11 +39,61 @@ delete t1 from t1,t3 where t1.a=t3.a; ...@@ -39,11 +39,61 @@ delete t1 from t1,t3 where t1.a=t3.a;
drop table t1; drop table t1;
insert into t1 values(1); insert into t1 values(1);
ERROR HY000: The MySQL server is running with the --read-only option so it cannot execute this statement ERROR HY000: The MySQL server is running with the --read-only option so it cannot execute this statement
set global read_only=0;
lock table t1 write;
lock table t2 write;
set global read_only=1;
ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
unlock tables ;
set global read_only=1;
select @@global.read_only;
@@global.read_only
0
unlock tables ;
select @@global.read_only;
@@global.read_only
1
set global read_only=0;
lock table t1 read;
lock table t2 read;
set global read_only=1;
ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
unlock tables ;
set global read_only=1;
select @@global.read_only;
@@global.read_only
0
unlock tables ;
select @@global.read_only;
@@global.read_only
1
set global read_only=0;
BEGIN;
BEGIN;
set global read_only=1;
ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
ROLLBACK;
set global read_only=1;
select @@global.read_only;
@@global.read_only
1
ROLLBACK;
set global read_only=0;
flush tables with read lock;
set global read_only=1;
unlock tables;
set global read_only=0;
flush tables with read lock;
set global read_only=1;
select @@global.read_only;
@@global.read_only
1
unlock tables;
drop temporary table ttt; drop temporary table ttt;
ERROR 42S02: Unknown table 'ttt' ERROR 42S02: Unknown table 'ttt'
drop temporary table if exists ttt; drop temporary table if exists ttt;
Warnings: Warnings:
Note 1051 Unknown table 'ttt' Note 1051 Unknown table 'ttt'
set global read_only=0;
drop table t1,t2; drop table t1,t2;
drop user test@localhost; drop user test@localhost;
set global read_only=0;
DROP TABLE IF EXISTS table_11733 ;
grant CREATE, SELECT, DROP on *.* to test@localhost;
set global read_only=0;
create table table_11733 (a int) engine=InnoDb;
BEGIN;
insert into table_11733 values(11733);
set global read_only=1;
select @@global.read_only;
@@global.read_only
1
select * from table_11733 ;
a
11733
COMMIT;
ERROR HY000: The MySQL server is running with the --read-only option so it cannot execute this statement
set global read_only=0;
drop table table_11733 ;
drop user test@localhost;
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;
create table t1(a int) engine=InnoDB;
create table t2(a int) engine=MyISAM;
insert into t1 values(1001);
insert into t2 values(2001);
set global read_only=1;
select @@read_only;
@@read_only
1
select * from t1;
a
1001
select * from t2;
a
2001
select @@read_only;
@@read_only
0
select * from t1;
a
1001
select * from t2;
a
2001
set global read_only=0;
BEGIN;
insert into t1 values(1002);
insert into t2 values(2002);
BEGIN;
insert into t1 values(1003);
insert into t2 values(2003);
set global read_only=1;
COMMIT;
COMMIT;
ERROR HY000: The MySQL server is running with the --read-only option so it cannot execute this statement
set global read_only=0;
insert into t1 values(1004);
insert into t2 values(2004);
select * from t1;
a
1001
1002
1004
select * from t2;
a
2001
2002
2003
2004
select * from t1;
a
1001
1002
1004
select * from t2;
a
2001
2002
2003
2004
set global read_only=1;
select @@read_only;
@@read_only
1
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
show create table t2;
Table Create Table
t2 CREATE TABLE `t2` (
`a` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
insert into t1 values(1005);
insert into t2 values(2005);
select * from t1;
a
1001
1002
1004
1005
select * from t2;
a
2001
2002
2003
2004
2005
select * from t1;
a
1001
1002
1004
1005
select * from t2;
a
2001
2002
2003
2004
2005
insert into t1 values(1006);
ERROR HY000: The MySQL server is running with the --read-only option so it cannot execute this statement
insert into t2 values(2006);
ERROR HY000: The MySQL server is running with the --read-only option so it cannot execute this statement
drop table t1;
drop table t2;
...@@ -199,6 +199,421 @@ Pos Instruction ...@@ -199,6 +199,421 @@ Pos Instruction
44 jump 14 44 jump 14
45 stmt 9 "drop temporary table sudoku_work, sud..." 45 stmt 9 "drop temporary table sudoku_work, sud..."
drop procedure sudoku_solve; drop procedure sudoku_solve;
DROP PROCEDURE IF EXISTS proc_19194_simple;
DROP PROCEDURE IF EXISTS proc_19194_searched;
DROP PROCEDURE IF EXISTS proc_19194_nested_1;
DROP PROCEDURE IF EXISTS proc_19194_nested_2;
DROP PROCEDURE IF EXISTS proc_19194_nested_3;
DROP PROCEDURE IF EXISTS proc_19194_nested_4;
CREATE PROCEDURE proc_19194_simple(i int)
BEGIN
DECLARE str CHAR(10);
CASE i
WHEN 1 THEN SET str="1";
WHEN 2 THEN SET str="2";
WHEN 3 THEN SET str="3";
ELSE SET str="unknown";
END CASE;
SELECT str;
END|
CREATE PROCEDURE proc_19194_searched(i int)
BEGIN
DECLARE str CHAR(10);
CASE
WHEN i=1 THEN SET str="1";
WHEN i=2 THEN SET str="2";
WHEN i=3 THEN SET str="3";
ELSE SET str="unknown";
END CASE;
SELECT str;
END|
CREATE PROCEDURE proc_19194_nested_1(i int, j int)
BEGIN
DECLARE str_i CHAR(10);
DECLARE str_j CHAR(10);
CASE i
WHEN 10 THEN SET str_i="10";
WHEN 20 THEN
BEGIN
set str_i="20";
CASE
WHEN j=1 THEN SET str_j="1";
WHEN j=2 THEN SET str_j="2";
WHEN j=3 THEN SET str_j="3";
ELSE SET str_j="unknown";
END CASE;
select "i was 20";
END;
WHEN 30 THEN SET str_i="30";
WHEN 40 THEN SET str_i="40";
ELSE SET str_i="unknown";
END CASE;
SELECT str_i, str_j;
END|
CREATE PROCEDURE proc_19194_nested_2(i int, j int)
BEGIN
DECLARE str_i CHAR(10);
DECLARE str_j CHAR(10);
CASE
WHEN i=10 THEN SET str_i="10";
WHEN i=20 THEN
BEGIN
set str_i="20";
CASE j
WHEN 1 THEN SET str_j="1";
WHEN 2 THEN SET str_j="2";
WHEN 3 THEN SET str_j="3";
ELSE SET str_j="unknown";
END CASE;
select "i was 20";
END;
WHEN i=30 THEN SET str_i="30";
WHEN i=40 THEN SET str_i="40";
ELSE SET str_i="unknown";
END CASE;
SELECT str_i, str_j;
END|
CREATE PROCEDURE proc_19194_nested_3(i int, j int)
BEGIN
DECLARE str_i CHAR(10);
DECLARE str_j CHAR(10);
CASE i
WHEN 10 THEN SET str_i="10";
WHEN 20 THEN
BEGIN
set str_i="20";
CASE j
WHEN 1 THEN SET str_j="1";
WHEN 2 THEN SET str_j="2";
WHEN 3 THEN SET str_j="3";
ELSE SET str_j="unknown";
END CASE;
select "i was 20";
END;
WHEN 30 THEN SET str_i="30";
WHEN 40 THEN SET str_i="40";
ELSE SET str_i="unknown";
END CASE;
SELECT str_i, str_j;
END|
CREATE PROCEDURE proc_19194_nested_4(i int, j int)
BEGIN
DECLARE str_i CHAR(10);
DECLARE str_j CHAR(10);
CASE
WHEN i=10 THEN SET str_i="10";
WHEN i=20 THEN
BEGIN
set str_i="20";
CASE
WHEN j=1 THEN SET str_j="1";
WHEN j=2 THEN SET str_j="2";
WHEN j=3 THEN SET str_j="3";
ELSE SET str_j="unknown";
END CASE;
select "i was 20";
END;
WHEN i=30 THEN SET str_i="30";
WHEN i=40 THEN SET str_i="40";
ELSE SET str_i="unknown";
END CASE;
SELECT str_i, str_j;
END|
SHOW PROCEDURE CODE proc_19194_simple;
Pos Instruction
0 set str@1 NULL
1 set_case_expr (12) 0 i@0
2 jump_if_not 5(12) (case_expr@0 = 1)
3 set str@1 _latin1'1'
4 jump 12
5 jump_if_not 8(12) (case_expr@0 = 2)
6 set str@1 _latin1'2'
7 jump 12
8 jump_if_not 11(12) (case_expr@0 = 3)
9 set str@1 _latin1'3'
10 jump 12
11 set str@1 _latin1'unknown'
12 stmt 0 "SELECT str"
SHOW PROCEDURE CODE proc_19194_searched;
Pos Instruction
0 set str@1 NULL
1 jump_if_not 4(11) (i@0 = 1)
2 set str@1 _latin1'1'
3 jump 11
4 jump_if_not 7(11) (i@0 = 2)
5 set str@1 _latin1'2'
6 jump 11
7 jump_if_not 10(11) (i@0 = 3)
8 set str@1 _latin1'3'
9 jump 11
10 set str@1 _latin1'unknown'
11 stmt 0 "SELECT str"
SHOW PROCEDURE CODE proc_19194_nested_1;
Pos Instruction
0 set str_i@2 NULL
1 set str_j@3 NULL
2 set_case_expr (27) 0 i@0
3 jump_if_not 6(27) (case_expr@0 = 10)
4 set str_i@2 _latin1'10'
5 jump 27
6 jump_if_not 20(27) (case_expr@0 = 20)
7 set str_i@2 _latin1'20'
8 jump_if_not 11(18) (j@1 = 1)
9 set str_j@3 _latin1'1'
10 jump 18
11 jump_if_not 14(18) (j@1 = 2)
12 set str_j@3 _latin1'2'
13 jump 18
14 jump_if_not 17(18) (j@1 = 3)
15 set str_j@3 _latin1'3'
16 jump 18
17 set str_j@3 _latin1'unknown'
18 stmt 0 "select "i was 20""
19 jump 27
20 jump_if_not 23(27) (case_expr@0 = 30)
21 set str_i@2 _latin1'30'
22 jump 27
23 jump_if_not 26(27) (case_expr@0 = 40)
24 set str_i@2 _latin1'40'
25 jump 27
26 set str_i@2 _latin1'unknown'
27 stmt 0 "SELECT str_i, str_j"
SHOW PROCEDURE CODE proc_19194_nested_2;
Pos Instruction
0 set str_i@2 NULL
1 set str_j@3 NULL
2 jump_if_not 5(27) (i@0 = 10)
3 set str_i@2 _latin1'10'
4 jump 27
5 jump_if_not 20(27) (i@0 = 20)
6 set str_i@2 _latin1'20'
7 set_case_expr (18) 0 j@1
8 jump_if_not 11(18) (case_expr@0 = 1)
9 set str_j@3 _latin1'1'
10 jump 18
11 jump_if_not 14(18) (case_expr@0 = 2)
12 set str_j@3 _latin1'2'
13 jump 18
14 jump_if_not 17(18) (case_expr@0 = 3)
15 set str_j@3 _latin1'3'
16 jump 18
17 set str_j@3 _latin1'unknown'
18 stmt 0 "select "i was 20""
19 jump 27
20 jump_if_not 23(27) (i@0 = 30)
21 set str_i@2 _latin1'30'
22 jump 27
23 jump_if_not 26(27) (i@0 = 40)
24 set str_i@2 _latin1'40'
25 jump 27
26 set str_i@2 _latin1'unknown'
27 stmt 0 "SELECT str_i, str_j"
SHOW PROCEDURE CODE proc_19194_nested_3;
Pos Instruction
0 set str_i@2 NULL
1 set str_j@3 NULL
2 set_case_expr (28) 0 i@0
3 jump_if_not 6(28) (case_expr@0 = 10)
4 set str_i@2 _latin1'10'
5 jump 28
6 jump_if_not 21(28) (case_expr@0 = 20)
7 set str_i@2 _latin1'20'
8 set_case_expr (19) 1 j@1
9 jump_if_not 12(19) (case_expr@1 = 1)
10 set str_j@3 _latin1'1'
11 jump 19
12 jump_if_not 15(19) (case_expr@1 = 2)
13 set str_j@3 _latin1'2'
14 jump 19
15 jump_if_not 18(19) (case_expr@1 = 3)
16 set str_j@3 _latin1'3'
17 jump 19
18 set str_j@3 _latin1'unknown'
19 stmt 0 "select "i was 20""
20 jump 28
21 jump_if_not 24(28) (case_expr@0 = 30)
22 set str_i@2 _latin1'30'
23 jump 28
24 jump_if_not 27(28) (case_expr@0 = 40)
25 set str_i@2 _latin1'40'
26 jump 28
27 set str_i@2 _latin1'unknown'
28 stmt 0 "SELECT str_i, str_j"
SHOW PROCEDURE CODE proc_19194_nested_4;
Pos Instruction
0 set str_i@2 NULL
1 set str_j@3 NULL
2 jump_if_not 5(26) (i@0 = 10)
3 set str_i@2 _latin1'10'
4 jump 26
5 jump_if_not 19(26) (i@0 = 20)
6 set str_i@2 _latin1'20'
7 jump_if_not 10(17) (j@1 = 1)
8 set str_j@3 _latin1'1'
9 jump 17
10 jump_if_not 13(17) (j@1 = 2)
11 set str_j@3 _latin1'2'
12 jump 17
13 jump_if_not 16(17) (j@1 = 3)
14 set str_j@3 _latin1'3'
15 jump 17
16 set str_j@3 _latin1'unknown'
17 stmt 0 "select "i was 20""
18 jump 26
19 jump_if_not 22(26) (i@0 = 30)
20 set str_i@2 _latin1'30'
21 jump 26
22 jump_if_not 25(26) (i@0 = 40)
23 set str_i@2 _latin1'40'
24 jump 26
25 set str_i@2 _latin1'unknown'
26 stmt 0 "SELECT str_i, str_j"
CALL proc_19194_nested_1(10, 1);
str_i str_j
10 NULL
CALL proc_19194_nested_1(25, 1);
str_i str_j
unknown NULL
CALL proc_19194_nested_1(20, 1);
i was 20
i was 20
str_i str_j
20 1
CALL proc_19194_nested_1(20, 2);
i was 20
i was 20
str_i str_j
20 2
CALL proc_19194_nested_1(20, 3);
i was 20
i was 20
str_i str_j
20 3
CALL proc_19194_nested_1(20, 4);
i was 20
i was 20
str_i str_j
20 unknown
CALL proc_19194_nested_1(30, 1);
str_i str_j
30 NULL
CALL proc_19194_nested_1(40, 1);
str_i str_j
40 NULL
CALL proc_19194_nested_1(0, 0);
str_i str_j
unknown NULL
CALL proc_19194_nested_2(10, 1);
str_i str_j
10 NULL
CALL proc_19194_nested_2(25, 1);
str_i str_j
unknown NULL
CALL proc_19194_nested_2(20, 1);
i was 20
i was 20
str_i str_j
20 1
CALL proc_19194_nested_2(20, 2);
i was 20
i was 20
str_i str_j
20 2
CALL proc_19194_nested_2(20, 3);
i was 20
i was 20
str_i str_j
20 3
CALL proc_19194_nested_2(20, 4);
i was 20
i was 20
str_i str_j
20 unknown
CALL proc_19194_nested_2(30, 1);
str_i str_j
30 NULL
CALL proc_19194_nested_2(40, 1);
str_i str_j
40 NULL
CALL proc_19194_nested_2(0, 0);
str_i str_j
unknown NULL
CALL proc_19194_nested_3(10, 1);
str_i str_j
10 NULL
CALL proc_19194_nested_3(25, 1);
str_i str_j
unknown NULL
CALL proc_19194_nested_3(20, 1);
i was 20
i was 20
str_i str_j
20 1
CALL proc_19194_nested_3(20, 2);
i was 20
i was 20
str_i str_j
20 2
CALL proc_19194_nested_3(20, 3);
i was 20
i was 20
str_i str_j
20 3
CALL proc_19194_nested_3(20, 4);
i was 20
i was 20
str_i str_j
20 unknown
CALL proc_19194_nested_3(30, 1);
str_i str_j
30 NULL
CALL proc_19194_nested_3(40, 1);
str_i str_j
40 NULL
CALL proc_19194_nested_3(0, 0);
str_i str_j
unknown NULL
CALL proc_19194_nested_4(10, 1);
str_i str_j
10 NULL
CALL proc_19194_nested_4(25, 1);
str_i str_j
unknown NULL
CALL proc_19194_nested_4(20, 1);
i was 20
i was 20
str_i str_j
20 1
CALL proc_19194_nested_4(20, 2);
i was 20
i was 20
str_i str_j
20 2
CALL proc_19194_nested_4(20, 3);
i was 20
i was 20
str_i str_j
20 3
CALL proc_19194_nested_4(20, 4);
i was 20
i was 20
str_i str_j
20 unknown
CALL proc_19194_nested_4(30, 1);
str_i str_j
30 NULL
CALL proc_19194_nested_4(40, 1);
str_i str_j
40 NULL
CALL proc_19194_nested_4(0, 0);
str_i str_j
unknown NULL
DROP PROCEDURE proc_19194_simple;
DROP PROCEDURE proc_19194_searched;
DROP PROCEDURE proc_19194_nested_1;
DROP PROCEDURE proc_19194_nested_2;
DROP PROCEDURE proc_19194_nested_3;
DROP PROCEDURE proc_19194_nested_4;
DROP PROCEDURE IF EXISTS p1; DROP PROCEDURE IF EXISTS p1;
CREATE PROCEDURE p1() CREATE INDEX idx ON t1 (c1); CREATE PROCEDURE p1() CREATE INDEX idx ON t1 (c1);
SHOW PROCEDURE CODE p1; SHOW PROCEDURE CODE p1;
......
DROP PROCEDURE IF EXISTS proc_19194_codegen;
DROP PROCEDURE IF EXISTS bug_19194_simple;
DROP PROCEDURE IF EXISTS bug_19194_searched;
CREATE PROCEDURE proc_19194_codegen(
IN proc_name VARCHAR(50),
IN count INTEGER,
IN simple INTEGER,
OUT body MEDIUMTEXT)
BEGIN
DECLARE code MEDIUMTEXT;
DECLARE i INT DEFAULT 1;
SET code = concat("CREATE PROCEDURE ", proc_name, "(i INT)\n");
SET code = concat(code, "BEGIN\n");
SET code = concat(code, " DECLARE str CHAR(10);\n");
IF (simple)
THEN
SET code = concat(code, " CASE i\n");
ELSE
SET code = concat(code, " CASE\n");
END IF;
WHILE (i <= count)
DO
IF (simple)
THEN
SET code = concat(code, " WHEN ", i, " THEN SET str=\"", i, "\";\n");
ELSE
SET code = concat(code, " WHEN i=", i, " THEN SET str=\"", i, "\";\n");
END IF;
SET i = i + 1;
END WHILE;
SET code = concat(code, " ELSE SET str=\"unknown\";\n");
SET code = concat(code, " END CASE;\n");
SET code = concat(code, " SELECT str;\n");
SET code = concat(code, "END\n");
SET body = code;
END|
set @body="";
call proc_19194_codegen("test_simple", 10, 1, @body);
select @body;
@body
CREATE PROCEDURE test_simple(i INT)
BEGIN
DECLARE str CHAR(10);
CASE i
WHEN 1 THEN SET str="1";
WHEN 2 THEN SET str="2";
WHEN 3 THEN SET str="3";
WHEN 4 THEN SET str="4";
WHEN 5 THEN SET str="5";
WHEN 6 THEN SET str="6";
WHEN 7 THEN SET str="7";
WHEN 8 THEN SET str="8";
WHEN 9 THEN SET str="9";
WHEN 10 THEN SET str="10";
ELSE SET str="unknown";
END CASE;
SELECT str;
END
call proc_19194_codegen("test_searched", 10, 0, @body);
select @body;
@body
CREATE PROCEDURE test_searched(i INT)
BEGIN
DECLARE str CHAR(10);
CASE
WHEN i=1 THEN SET str="1";
WHEN i=2 THEN SET str="2";
WHEN i=3 THEN SET str="3";
WHEN i=4 THEN SET str="4";
WHEN i=5 THEN SET str="5";
WHEN i=6 THEN SET str="6";
WHEN i=7 THEN SET str="7";
WHEN i=8 THEN SET str="8";
WHEN i=9 THEN SET str="9";
WHEN i=10 THEN SET str="10";
ELSE SET str="unknown";
END CASE;
SELECT str;
END
CALL bug_19194_simple(1);
str
1
CALL bug_19194_simple(2);
str
2
CALL bug_19194_simple(1000);
str
1000
CALL bug_19194_simple(4998);
str
4998
CALL bug_19194_simple(4999);
str
4999
CALL bug_19194_simple(9999);
str
unknown
CALL bug_19194_searched(1);
str
1
CALL bug_19194_searched(2);
str
2
CALL bug_19194_searched(1000);
str
1000
CALL bug_19194_searched(4998);
str
4998
CALL bug_19194_searched(4999);
str
4999
CALL bug_19194_searched(9999);
str
unknown
DROP PROCEDURE proc_19194_codegen;
DROP PROCEDURE bug_19194_simple;
DROP PROCEDURE bug_19194_searched;
...@@ -1605,3 +1605,17 @@ check table bug22080_2; ...@@ -1605,3 +1605,17 @@ check table bug22080_2;
check table bug22080_3; check table bug22080_3;
drop tables bug22080_1,bug22080_2,bug22080_3; drop tables bug22080_1,bug22080_2,bug22080_3;
#
# Bug #21328 mysqld issues warnings on ALTER CSV table to MyISAM
#
CREATE TABLE `bug21328` (
`col1` int(11) DEFAULT NULL,
`col2` int(11) DEFAULT NULL,
`col3` int(11) DEFAULT NULL
) ENGINE=CSV;
insert into bug21328 values (1,NULL,NULL);
alter table bug21328 engine=myisam;
drop table bug21328;
#!/bin/bash #!/bin/sh
########################################################################### ###########################################################################
......
...@@ -102,6 +102,111 @@ drop table t1; ...@@ -102,6 +102,111 @@ drop table t1;
insert into t1 values(1); insert into t1 values(1);
# #
# BUG#11733: COMMITs should not happen if read-only is set
#
# LOCK TABLE ... WRITE / READ_ONLY
# - is an error in the same connection
# - is ok in a different connection
connection default;
set global read_only=0;
lock table t1 write;
connection con1;
lock table t2 write;
connection default;
--error ER_LOCK_OR_ACTIVE_TRANSACTION
set global read_only=1;
unlock tables ;
# The following call blocks until con1 releases the write lock.
# Blocking is expected.
send set global read_only=1;
connection con1;
--sleep 1
select @@global.read_only;
unlock tables ;
--sleep 1
select @@global.read_only;
connection default;
reap;
# LOCK TABLE ... READ / READ_ONLY
# - is an error in the same connection
# - is ok in a different connection
connection default;
set global read_only=0;
lock table t1 read;
connection con1;
lock table t2 read;
connection default;
--error ER_LOCK_OR_ACTIVE_TRANSACTION
set global read_only=1;
unlock tables ;
# The following call blocks until con1 releases the read lock.
# Blocking is a limitation, and could be improved.
send set global read_only=1;
connection con1;
--sleep 1
select @@global.read_only;
unlock tables ;
--sleep 1
select @@global.read_only;
connection default;
reap;
# pending transaction / READ_ONLY
# - is an error in the same connection
# - is ok in a different connection
connection default;
set global read_only=0;
BEGIN;
connection con1;
BEGIN;
connection default;
--error ER_LOCK_OR_ACTIVE_TRANSACTION
set global read_only=1;
ROLLBACK;
set global read_only=1;
connection con1;
select @@global.read_only;
ROLLBACK;
# Verify that FLUSH TABLES WITH READ LOCK do not block READ_ONLY
# - in the same SUPER connection
# - in another SUPER connection
connection default;
set global read_only=0;
flush tables with read lock;
set global read_only=1;
unlock tables;
connect (root2,localhost,root,,test);
connection default;
set global read_only=0;
flush tables with read lock;
connection root2;
set global read_only=1;
connection default;
select @@global.read_only;
unlock tables;
# BUG #22077 "DROP TEMPORARY TABLE fails with wrong error if read_only is set" # BUG #22077 "DROP TEMPORARY TABLE fails with wrong error if read_only is set"
# #
# check if DROP TEMPORARY on a non-existing temporary table returns the right # check if DROP TEMPORARY on a non-existing temporary table returns the right
...@@ -113,8 +218,10 @@ drop temporary table ttt; ...@@ -113,8 +218,10 @@ drop temporary table ttt;
# check if DROP TEMPORARY TABLE IF EXISTS produces a warning with read_only set # check if DROP TEMPORARY TABLE IF EXISTS produces a warning with read_only set
drop temporary table if exists ttt; drop temporary table if exists ttt;
#
# Cleanup
#
connection default; connection default;
set global read_only=0;
drop table t1,t2; drop table t1,t2;
drop user test@localhost; drop user test@localhost;
set global read_only=0;
# should work with embedded server after mysqltest is fixed
-- source include/not_embedded.inc
-- source include/have_innodb.inc
#
# BUG#11733: COMMITs should not happen if read-only is set
#
--disable_warnings
DROP TABLE IF EXISTS table_11733 ;
--enable_warnings
# READ_ONLY does nothing to SUPER users
# so we use a non-SUPER one:
grant CREATE, SELECT, DROP on *.* to test@localhost;
connect (con1,localhost,test,,test);
connection default;
set global read_only=0;
# Any transactional engine will do
create table table_11733 (a int) engine=InnoDb;
connection con1;
BEGIN;
insert into table_11733 values(11733);
connection default;
set global read_only=1;
connection con1;
select @@global.read_only;
select * from table_11733 ;
-- error ER_OPTION_PREVENTS_STATEMENT
COMMIT;
connection default;
set global read_only=0;
drop table table_11733 ;
drop user test@localhost;
# Test case for BUG #11733
-- source include/master-slave.inc
-- source include/have_innodb.inc
# Setting the master readonly :
# - the variable @@readonly is not replicated on the slave
connect (master2,127.0.0.1,test,,test,$MASTER_MYPORT,);
connect (slave2,127.0.0.1,test,,test,$SLAVE_MYPORT,);
connection master1;
create table t1(a int) engine=InnoDB;
create table t2(a int) engine=MyISAM;
insert into t1 values(1001);
insert into t2 values(2001);
connection master;
set global read_only=1;
connection master1;
select @@read_only;
select * from t1;
select * from t2;
sync_slave_with_master;
select @@read_only;
select * from t1;
select * from t2;
# - replication of transactions
connection master;
set global read_only=0;
connection master1;
BEGIN;
insert into t1 values(1002);
insert into t2 values(2002);
connection master2;
BEGIN;
insert into t1 values(1003);
insert into t2 values(2003);
connection master;
set global read_only=1;
connection master1;
## works even with read_only=1, because master1 is root
COMMIT;
connection master2;
--error ER_OPTION_PREVENTS_STATEMENT
COMMIT;
connection master;
set global read_only=0;
connection master1;
insert into t1 values(1004);
insert into t2 values(2004);
select * from t1;
select * from t2;
sync_slave_with_master;
select * from t1;
select * from t2;
# Setting the slave readonly : replication will pass
#
connection slave1;
set global read_only=1;
connection slave;
select @@read_only;
# Make sure the replicated table is also transactional
show create table t1;
# Make sure the replicated table is not transactional
show create table t2;
connection master;
insert into t1 values(1005);
insert into t2 values(2005);
select * from t1;
select * from t2;
sync_slave_with_master;
connection slave;
select * from t1;
select * from t2;
# Non root user can not write on the slave
connection slave2;
--error ER_OPTION_PREVENTS_STATEMENT
insert into t1 values(1006);
--error ER_OPTION_PREVENTS_STATEMENT
insert into t2 values(2006);
## Cleanup
connection master;
drop table t1;
drop table t2;
sync_slave_with_master;
...@@ -191,6 +191,241 @@ show procedure code sudoku_solve; ...@@ -191,6 +191,241 @@ show procedure code sudoku_solve;
drop procedure sudoku_solve; drop procedure sudoku_solve;
#
# Bug#19194 (Right recursion in parser for CASE causes excessive stack
# usage, limitation)
# This bug also exposed a flaw in the generated code with nested case
# statements
#
--disable_warnings
DROP PROCEDURE IF EXISTS proc_19194_simple;
DROP PROCEDURE IF EXISTS proc_19194_searched;
DROP PROCEDURE IF EXISTS proc_19194_nested_1;
DROP PROCEDURE IF EXISTS proc_19194_nested_2;
DROP PROCEDURE IF EXISTS proc_19194_nested_3;
DROP PROCEDURE IF EXISTS proc_19194_nested_4;
--enable_warnings
delimiter |;
CREATE PROCEDURE proc_19194_simple(i int)
BEGIN
DECLARE str CHAR(10);
CASE i
WHEN 1 THEN SET str="1";
WHEN 2 THEN SET str="2";
WHEN 3 THEN SET str="3";
ELSE SET str="unknown";
END CASE;
SELECT str;
END|
CREATE PROCEDURE proc_19194_searched(i int)
BEGIN
DECLARE str CHAR(10);
CASE
WHEN i=1 THEN SET str="1";
WHEN i=2 THEN SET str="2";
WHEN i=3 THEN SET str="3";
ELSE SET str="unknown";
END CASE;
SELECT str;
END|
# Outer SIMPLE case, inner SEARCHED case
CREATE PROCEDURE proc_19194_nested_1(i int, j int)
BEGIN
DECLARE str_i CHAR(10);
DECLARE str_j CHAR(10);
CASE i
WHEN 10 THEN SET str_i="10";
WHEN 20 THEN
BEGIN
set str_i="20";
CASE
WHEN j=1 THEN SET str_j="1";
WHEN j=2 THEN SET str_j="2";
WHEN j=3 THEN SET str_j="3";
ELSE SET str_j="unknown";
END CASE;
select "i was 20";
END;
WHEN 30 THEN SET str_i="30";
WHEN 40 THEN SET str_i="40";
ELSE SET str_i="unknown";
END CASE;
SELECT str_i, str_j;
END|
# Outer SEARCHED case, inner SIMPLE case
CREATE PROCEDURE proc_19194_nested_2(i int, j int)
BEGIN
DECLARE str_i CHAR(10);
DECLARE str_j CHAR(10);
CASE
WHEN i=10 THEN SET str_i="10";
WHEN i=20 THEN
BEGIN
set str_i="20";
CASE j
WHEN 1 THEN SET str_j="1";
WHEN 2 THEN SET str_j="2";
WHEN 3 THEN SET str_j="3";
ELSE SET str_j="unknown";
END CASE;
select "i was 20";
END;
WHEN i=30 THEN SET str_i="30";
WHEN i=40 THEN SET str_i="40";
ELSE SET str_i="unknown";
END CASE;
SELECT str_i, str_j;
END|
# Outer SIMPLE case, inner SIMPLE case
CREATE PROCEDURE proc_19194_nested_3(i int, j int)
BEGIN
DECLARE str_i CHAR(10);
DECLARE str_j CHAR(10);
CASE i
WHEN 10 THEN SET str_i="10";
WHEN 20 THEN
BEGIN
set str_i="20";
CASE j
WHEN 1 THEN SET str_j="1";
WHEN 2 THEN SET str_j="2";
WHEN 3 THEN SET str_j="3";
ELSE SET str_j="unknown";
END CASE;
select "i was 20";
END;
WHEN 30 THEN SET str_i="30";
WHEN 40 THEN SET str_i="40";
ELSE SET str_i="unknown";
END CASE;
SELECT str_i, str_j;
END|
# Outer SEARCHED case, inner SEARCHED case
CREATE PROCEDURE proc_19194_nested_4(i int, j int)
BEGIN
DECLARE str_i CHAR(10);
DECLARE str_j CHAR(10);
CASE
WHEN i=10 THEN SET str_i="10";
WHEN i=20 THEN
BEGIN
set str_i="20";
CASE
WHEN j=1 THEN SET str_j="1";
WHEN j=2 THEN SET str_j="2";
WHEN j=3 THEN SET str_j="3";
ELSE SET str_j="unknown";
END CASE;
select "i was 20";
END;
WHEN i=30 THEN SET str_i="30";
WHEN i=40 THEN SET str_i="40";
ELSE SET str_i="unknown";
END CASE;
SELECT str_i, str_j;
END|
delimiter ;|
SHOW PROCEDURE CODE proc_19194_simple;
SHOW PROCEDURE CODE proc_19194_searched;
SHOW PROCEDURE CODE proc_19194_nested_1;
SHOW PROCEDURE CODE proc_19194_nested_2;
SHOW PROCEDURE CODE proc_19194_nested_3;
SHOW PROCEDURE CODE proc_19194_nested_4;
CALL proc_19194_nested_1(10, 1);
#
# Before 19194, the generated code was:
# 20 jump_if_not 23(27) 30
# 21 set str_i@2 _latin1'30'
# As opposed to the expected:
# 20 jump_if_not 23(27) (case_expr@0 = 30)
# 21 set str_i@2 _latin1'30'
#
# and as a result, this call returned "30",
# because the expression 30 is always true,
# masking the case 40, case 0 and the else.
#
CALL proc_19194_nested_1(25, 1);
CALL proc_19194_nested_1(20, 1);
CALL proc_19194_nested_1(20, 2);
CALL proc_19194_nested_1(20, 3);
CALL proc_19194_nested_1(20, 4);
CALL proc_19194_nested_1(30, 1);
CALL proc_19194_nested_1(40, 1);
CALL proc_19194_nested_1(0, 0);
CALL proc_19194_nested_2(10, 1);
#
# Before 19194, the generated code was:
# 20 jump_if_not 23(27) (case_expr@0 = (i@0 = 30))
# 21 set str_i@2 _latin1'30'
# As opposed to the expected:
# 20 jump_if_not 23(27) (i@0 = 30)
# 21 set str_i@2 _latin1'30'
# and as a result, this call crashed the server, because there is no
# such variable as "case_expr@0".
#
CALL proc_19194_nested_2(25, 1);
CALL proc_19194_nested_2(20, 1);
CALL proc_19194_nested_2(20, 2);
CALL proc_19194_nested_2(20, 3);
CALL proc_19194_nested_2(20, 4);
CALL proc_19194_nested_2(30, 1);
CALL proc_19194_nested_2(40, 1);
CALL proc_19194_nested_2(0, 0);
CALL proc_19194_nested_3(10, 1);
CALL proc_19194_nested_3(25, 1);
CALL proc_19194_nested_3(20, 1);
CALL proc_19194_nested_3(20, 2);
CALL proc_19194_nested_3(20, 3);
CALL proc_19194_nested_3(20, 4);
CALL proc_19194_nested_3(30, 1);
CALL proc_19194_nested_3(40, 1);
CALL proc_19194_nested_3(0, 0);
CALL proc_19194_nested_4(10, 1);
CALL proc_19194_nested_4(25, 1);
CALL proc_19194_nested_4(20, 1);
CALL proc_19194_nested_4(20, 2);
CALL proc_19194_nested_4(20, 3);
CALL proc_19194_nested_4(20, 4);
CALL proc_19194_nested_4(30, 1);
CALL proc_19194_nested_4(40, 1);
CALL proc_19194_nested_4(0, 0);
DROP PROCEDURE proc_19194_simple;
DROP PROCEDURE proc_19194_searched;
DROP PROCEDURE proc_19194_nested_1;
DROP PROCEDURE proc_19194_nested_2;
DROP PROCEDURE proc_19194_nested_3;
DROP PROCEDURE proc_19194_nested_4;
# #
# Bug#19207: Final parenthesis omitted for CREATE INDEX in Stored # Bug#19207: Final parenthesis omitted for CREATE INDEX in Stored
......
#
# Bug#19194 (Right recursion in parser for CASE causes excessive stack
# usage, limitation)
#
--disable_warnings
DROP PROCEDURE IF EXISTS proc_19194_codegen;
DROP PROCEDURE IF EXISTS bug_19194_simple;
DROP PROCEDURE IF EXISTS bug_19194_searched;
--enable_warnings
delimiter |;
CREATE PROCEDURE proc_19194_codegen(
IN proc_name VARCHAR(50),
IN count INTEGER,
IN simple INTEGER,
OUT body MEDIUMTEXT)
BEGIN
DECLARE code MEDIUMTEXT;
DECLARE i INT DEFAULT 1;
SET code = concat("CREATE PROCEDURE ", proc_name, "(i INT)\n");
SET code = concat(code, "BEGIN\n");
SET code = concat(code, " DECLARE str CHAR(10);\n");
IF (simple)
THEN
SET code = concat(code, " CASE i\n");
ELSE
SET code = concat(code, " CASE\n");
END IF;
WHILE (i <= count)
DO
IF (simple)
THEN
SET code = concat(code, " WHEN ", i, " THEN SET str=\"", i, "\";\n");
ELSE
SET code = concat(code, " WHEN i=", i, " THEN SET str=\"", i, "\";\n");
END IF;
SET i = i + 1;
END WHILE;
SET code = concat(code, " ELSE SET str=\"unknown\";\n");
SET code = concat(code, " END CASE;\n");
SET code = concat(code, " SELECT str;\n");
SET code = concat(code, "END\n");
SET body = code;
END|
delimiter ;|
set @body="";
call proc_19194_codegen("test_simple", 10, 1, @body);
select @body;
call proc_19194_codegen("test_searched", 10, 0, @body);
select @body;
--disable_query_log
call proc_19194_codegen("bug_19194_simple", 5000, 1, @body);
let $proc_body = `select @body`;
eval $proc_body;
call proc_19194_codegen("bug_19194_searched", 5000, 1, @body);
let $proc_body = `select @body`;
eval $proc_body;
--enable_query_log
CALL bug_19194_simple(1);
CALL bug_19194_simple(2);
CALL bug_19194_simple(1000);
CALL bug_19194_simple(4998);
CALL bug_19194_simple(4999);
CALL bug_19194_simple(9999);
CALL bug_19194_searched(1);
CALL bug_19194_searched(2);
CALL bug_19194_searched(1000);
CALL bug_19194_searched(4998);
CALL bug_19194_searched(4999);
CALL bug_19194_searched(9999);
DROP PROCEDURE proc_19194_codegen;
DROP PROCEDURE bug_19194_simple;
DROP PROCEDURE bug_19194_searched;
This diff is collapsed.
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
#endif #endif
/* /**
Print all instances of this instance manager. Print all instances of this instance manager.
Grammar: SHOW INSTANCES Grammar: SHOW INSTANCES
*/ */
...@@ -49,7 +49,7 @@ private: ...@@ -49,7 +49,7 @@ private:
}; };
/* /**
Reread configuration file and refresh internal cache. Reread configuration file and refresh internal cache.
Grammar: FLUSH INSTANCES Grammar: FLUSH INSTANCES
*/ */
...@@ -65,11 +65,50 @@ public: ...@@ -65,11 +65,50 @@ public:
}; };
/* /**
Base class for Instance-specific commands
(commands that operate on one instance).
Instance_cmd extends Command class by:
- an attribute for storing instance name;
- code to initialize instance name in constructor;
- an accessor to get instance name.
*/
class Instance_cmd : public Command
{
public:
Instance_cmd(const LEX_STRING *instance_name_arg);
protected:
inline const LEX_STRING *get_instance_name() const
{
return instance_name.get_str();
}
private:
Instance_name instance_name;
};
/**
Abstract class for Instance-specific commands. Abstract class for Instance-specific commands.
Abstract_instance_cmd extends Instance_cmd by providing a common
framework for writing command-implementations. Basically, the class
implements Command::execute() pure virtual function in the following
way:
- Lock Instance_map;
- Get an instance by name. Return an error, if there is no such
instance;
- Lock the instance;
- Unlock Instance_map;
- Call execute_impl(), which should be implemented in derived class;
- Unlock the instance;
- Send response to the client and return error status.
*/ */
class Abstract_instance_cmd: public Command class Abstract_instance_cmd: public Instance_cmd
{ {
public: public:
Abstract_instance_cmd(const LEX_STRING *instance_name_arg); Abstract_instance_cmd(const LEX_STRING *instance_name_arg);
...@@ -78,29 +117,24 @@ public: ...@@ -78,29 +117,24 @@ public:
virtual int execute(st_net *net, ulong connection_id); virtual int execute(st_net *net, ulong connection_id);
protected: protected:
/* MT-NOTE: this operation is called under acquired Instance_map's lock. */ /**
This operation is intended to contain command-specific implementation.
MT-NOTE: this operation is called under acquired Instance's lock.
*/
virtual int execute_impl(st_net *net, Instance *instance) = 0; virtual int execute_impl(st_net *net, Instance *instance) = 0;
/* /**
This operation is invoked on successful return of execute_impl() and is This operation is invoked on successful return of execute_impl() and is
intended to send closing data. intended to send closing data.
MT-NOTE: this operation is called under released Instance_map's lock. MT-NOTE: this operation is called under released Instance's lock.
*/ */
virtual int send_ok_response(st_net *net, ulong connection_id) = 0; virtual int send_ok_response(st_net *net, ulong connection_id) = 0;
protected:
inline const LEX_STRING *get_instance_name() const
{
return instance_name.get_str();
}
private:
Instance_name instance_name;
}; };
/* /**
Print status of an instance. Print status of an instance.
Grammar: SHOW INSTANCE STATUS <instance_name> Grammar: SHOW INSTANCE STATUS <instance_name>
*/ */
...@@ -120,7 +154,7 @@ private: ...@@ -120,7 +154,7 @@ private:
}; };
/* /**
Print options of chosen instance. Print options of chosen instance.
Grammar: SHOW INSTANCE OPTIONS <instance_name> Grammar: SHOW INSTANCE OPTIONS <instance_name>
*/ */
...@@ -140,7 +174,7 @@ private: ...@@ -140,7 +174,7 @@ private:
}; };
/* /**
Start an instance. Start an instance.
Grammar: START INSTANCE <instance_name> Grammar: START INSTANCE <instance_name>
*/ */
...@@ -156,7 +190,7 @@ protected: ...@@ -156,7 +190,7 @@ protected:
}; };
/* /**
Stop an instance. Stop an instance.
Grammar: STOP INSTANCE <instance_name> Grammar: STOP INSTANCE <instance_name>
*/ */
...@@ -172,12 +206,12 @@ protected: ...@@ -172,12 +206,12 @@ protected:
}; };
/* /**
Create an instance. Create an instance.
Grammar: CREATE INSTANCE <instance_name> [<options>] Grammar: CREATE INSTANCE <instance_name> [<options>]
*/ */
class Create_instance: public Command class Create_instance: public Instance_cmd
{ {
public: public:
Create_instance(const LEX_STRING *instance_name_arg); Create_instance(const LEX_STRING *instance_name_arg);
...@@ -188,22 +222,15 @@ public: ...@@ -188,22 +222,15 @@ public:
protected: protected:
virtual int execute(st_net *net, ulong connection_id); virtual int execute(st_net *net, ulong connection_id);
inline const LEX_STRING *get_instance_name() const
{
return instance_name.get_str();
}
private: private:
bool parse_args(const char **text); bool parse_args(const char **text);
private: private:
Instance_name instance_name;
Named_value_arr options; Named_value_arr options;
}; };
/* /**
Drop an instance. Drop an instance.
Grammar: DROP INSTANCE <instance_name> Grammar: DROP INSTANCE <instance_name>
...@@ -212,18 +239,17 @@ private: ...@@ -212,18 +239,17 @@ private:
is removed from the instance map. is removed from the instance map.
*/ */
class Drop_instance: public Abstract_instance_cmd class Drop_instance: public Instance_cmd
{ {
public: public:
Drop_instance(const LEX_STRING *instance_name_arg); Drop_instance(const LEX_STRING *instance_name_arg);
protected: protected:
virtual int execute_impl(st_net *net, Instance *instance); virtual int execute(st_net *net, ulong connection_id);
virtual int send_ok_response(st_net *net, ulong connection_id);
}; };
/* /**
Print requested part of the log. Print requested part of the log.
Grammar: Grammar:
SHOW <instance_name> LOG {ERROR | SLOW | GENERAL} size[, offset_from_end] SHOW <instance_name> LOG {ERROR | SLOW | GENERAL} size[, offset_from_end]
...@@ -251,7 +277,7 @@ private: ...@@ -251,7 +277,7 @@ private:
}; };
/* /**
Shows the list of the log files, used by an instance. Shows the list of the log files, used by an instance.
Grammar: SHOW <instance_name> LOG FILES Grammar: SHOW <instance_name> LOG FILES
*/ */
...@@ -271,7 +297,7 @@ private: ...@@ -271,7 +297,7 @@ private:
}; };
/* /**
Abstract class for option-management commands. Abstract class for option-management commands.
*/ */
...@@ -311,7 +337,7 @@ private: ...@@ -311,7 +337,7 @@ private:
}; };
/* /**
Set an option for the instance. Set an option for the instance.
Grammar: SET instance_name.option[=option_value][, ...] Grammar: SET instance_name.option[=option_value][, ...]
*/ */
...@@ -328,7 +354,7 @@ protected: ...@@ -328,7 +354,7 @@ protected:
}; };
/* /**
Remove option of the instance. Remove option of the instance.
Grammar: UNSET instance_name.option[, ...] Grammar: UNSET instance_name.option[, ...]
*/ */
...@@ -345,7 +371,7 @@ protected: ...@@ -345,7 +371,7 @@ protected:
}; };
/* /**
Syntax error command. Syntax error command.
This command is issued if parser reported a syntax error. We need it to This command is issued if parser reported a syntax error. We need it to
......
This diff is collapsed.
...@@ -16,10 +16,12 @@ ...@@ -16,10 +16,12 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "thread_registry.h" #include <my_global.h>
#include <my_sys.h> #include <my_sys.h>
#include <my_list.h> #include <my_list.h>
#include "thread_registry.h"
#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE) #if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
#pragma interface #pragma interface
#endif #endif
...@@ -27,7 +29,6 @@ ...@@ -27,7 +29,6 @@
class Instance; class Instance;
class Instance_map; class Instance_map;
class Thread_registry; class Thread_registry;
struct GUARD_NODE;
/** /**
The guardian thread is responsible for monitoring and restarting of guarded The guardian thread is responsible for monitoring and restarting of guarded
...@@ -37,97 +38,73 @@ struct GUARD_NODE; ...@@ -37,97 +38,73 @@ struct GUARD_NODE;
class Guardian: public Thread class Guardian: public Thread
{ {
public: public:
/* states of an instance */ Guardian(Thread_registry *thread_registry_arg,
enum enum_instance_state { NOT_STARTED= 1, STARTING, STARTED, JUST_CRASHED, Instance_map *instance_map_arg);
CRASHED, CRASHED_AND_ABANDONED, STOPPING }; ~Guardian();
/*
The Guardian list node structure. Guardian utilizes it to store
guarded instances plus some additional info.
*/
struct GUARD_NODE void init();
{
Instance *instance;
/* state of an instance (i.e. STARTED, CRASHED, etc.) */
enum_instance_state state;
/* the amount of attemts to restart instance (cleaned up at success) */
int restart_counter;
/* triggered at a crash */
time_t crash_moment;
/* General time field. Used to provide timeouts (at shutdown and restart) */
time_t last_checked;
};
/* Return client state name. */
static const char *get_instance_state_name(enum_instance_state state);
Guardian(Thread_registry *thread_registry_arg, public:
Instance_map *instance_map_arg,
uint monitoring_interval_arg);
virtual ~Guardian();
/* Initialize or refresh the list of guarded instances */
int init();
/* Request guardian shutdown. Stop instances if needed */
void request_shutdown(); void request_shutdown();
/* Start instance protection */
int guard(Instance *instance, bool nolock= FALSE); bool is_stopped();
/* Stop instance protection */
int stop_guard(Instance *instance);
/* Returns TRUE if guardian thread is stopped */
int is_stopped();
void lock(); void lock();
void unlock(); void unlock();
/* void ping();
Return an internal list node for the given instance if the instance is
managed by Guardian. Otherwise, return NULL.
MT-NOTE: must be called under acquired lock. protected:
*/ virtual void run();
LIST *find_instance_node(Instance *instance);
private:
void stop_instances();
/* The operation is used to check if the instance is active or not. */ void process_instance(Instance *instance);
bool is_active(Instance *instance);
private:
/* /*
Return state of the given instance list node. The pointer must specify LOCK_guardian protectes the members in this section:
a valid list node. - shutdown_requested;
- stopped;
Also, it is used for COND_guardian.
*/ */
inline enum_instance_state get_instance_state(LIST *instance_node); pthread_mutex_t LOCK_guardian;
protected:
/* Main funtion of the thread */
virtual void run();
public: /*
Guardian's main loop waits on this condition. So, it should be signalled
each time, when instance state has been changed and we want Guardian to
wake up.
TODO: Change this to having data-scoped conditions, i.e. conditions,
which indicate that some data has been changed.
*/
pthread_cond_t COND_guardian; pthread_cond_t COND_guardian;
private: /*
/* Prepares Guardian shutdown. Stops instances is needed */ This variable is set to TRUE, when Manager thread is shutting down.
int stop_instances(); The flag is used by Guardian thread to understand that it's time to
/* check instance state and act accordingly */ finish.
void process_instance(Instance *instance, GUARD_NODE *current_node, */
LIST **guarded_instances, LIST *elem); bool shutdown_requested;
/*
This flag is set to TRUE on shutdown by Guardian thread, when all guarded
mysqlds are stopped.
int stopped; The flag is used in the Manager thread to wait for Guardian to stop all
mysqlds.
*/
bool stopped;
private:
pthread_mutex_t LOCK_guardian;
Thread_info thread_info; Thread_info thread_info;
int monitoring_interval;
Thread_registry *thread_registry; Thread_registry *thread_registry;
Instance_map *instance_map; Instance_map *instance_map;
LIST *guarded_instances;
MEM_ROOT alloc;
/* this variable is set to TRUE when we want to stop Guardian thread */
bool shutdown_requested;
};
inline Guardian::enum_instance_state private:
Guardian::get_instance_state(LIST *instance_node) Guardian(const Guardian &);
{ Guardian&operator =(const Guardian &);
return ((GUARD_NODE *) instance_node->data)->state; };
}
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_GUARDIAN_H */ #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_GUARDIAN_H */
This diff is collapsed.
...@@ -29,7 +29,7 @@ class Instance_map; ...@@ -29,7 +29,7 @@ class Instance_map;
class Thread_registry; class Thread_registry;
/* /**
Instance_name -- the class represents instance name -- a string of length Instance_name -- the class represents instance name -- a string of length
less than MAX_INSTANCE_NAME_SIZE. less than MAX_INSTANCE_NAME_SIZE.
...@@ -67,72 +67,127 @@ private: ...@@ -67,72 +67,127 @@ private:
class Instance class Instance
{ {
public: public:
/* /* States of an instance. */
The following two constants defines name of the default mysqld-instance enum enum_instance_state
("mysqld"). {
STOPPED,
NOT_STARTED,
STARTING,
STARTED,
JUST_CRASHED,
CRASHED,
CRASHED_AND_ABANDONED,
STOPPING
};
public:
/**
The constant defines name of the default mysqld-instance ("mysqld").
*/ */
static const LEX_STRING DFLT_INSTANCE_NAME; static const LEX_STRING DFLT_INSTANCE_NAME;
public: public:
/*
The operation is intended to check whether string is a well-formed
instance name or not.
*/
static bool is_name_valid(const LEX_STRING *name); static bool is_name_valid(const LEX_STRING *name);
/*
The operation is intended to check if the given instance name is
mysqld-compatible or not.
*/
static bool is_mysqld_compatible_name(const LEX_STRING *name); static bool is_mysqld_compatible_name(const LEX_STRING *name);
public: public:
Instance(); Instance();
~Instance(); ~Instance();
bool init(const LEX_STRING *name_arg); bool init(const LEX_STRING *name_arg);
bool complete_initialization(); bool complete_initialization();
public:
bool is_active();
bool is_mysqld_running(); bool is_mysqld_running();
int start();
int stop(); bool start_mysqld();
/* send a signal to the instance */ bool stop_mysqld();
void kill_mysqld(int signo); void kill_mysqld(int signo);
bool is_crashed();
void set_crash_flag_n_wake_all();
/* void lock();
void unlock();
const char *get_state_name();
void reset_stat();
public:
/**
The operation is intended to check if the instance is mysqld-compatible The operation is intended to check if the instance is mysqld-compatible
or not. or not.
*/ */
inline bool is_mysqld_compatible() const; inline bool is_mysqld_compatible() const;
/* /**
The operation is intended to check if the instance is configured properly The operation is intended to check if the instance is configured properly
or not. Misconfigured instances are not managed. or not. Misconfigured instances are not managed.
*/ */
inline bool is_configured() const; inline bool is_configured() const;
/**
The operation returns TRUE if the instance is guarded and FALSE otherwise.
*/
inline bool is_guarded() const;
/**
The operation returns name of the instance.
*/
inline const LEX_STRING *get_name() const; inline const LEX_STRING *get_name() const;
/**
The operation returns the current state of the instance.
NOTE: At the moment should be used only for guarded instances.
*/
inline enum_instance_state get_state() const;
/**
The operation changes the state of the instance.
NOTE: At the moment should be used only for guarded instances.
TODO: Make private.
*/
inline void set_state(enum_instance_state new_state);
/**
The operation returns crashed flag.
*/
inline bool is_crashed();
public: public:
enum { DEFAULT_SHUTDOWN_DELAY= 35 }; /**
This attributes contains instance options.
TODO: Make private.
*/
Instance_options options; Instance_options options;
private: private:
/* This attributes is a flag, specifies if the instance has been crashed. */ /**
monitoring_thread_active is TRUE if there is a thread that monitors the
corresponding mysqld-process.
*/
bool monitoring_thread_active;
/**
crashed is TRUE when corresponding mysqld-process has been died after
start.
*/
bool crashed; bool crashed;
/* /**
This attribute specifies if the instance is configured properly or not. configured is TRUE when the instance is configured and FALSE otherwise.
Misconfigured instances are not managed. Misconfigured instances are not managed.
*/ */
bool configured; bool configured;
/* /*
This attribute specifies whether the instance is mysqld-compatible or not. mysqld_compatible specifies whether the instance is mysqld-compatible
Mysqld-compatible instances can contain only mysqld-specific options. or not. Mysqld-compatible instances can contain only mysqld-specific
At the moment an instance is mysqld-compatible if its name is "mysqld". options. At the moment an instance is mysqld-compatible if its name is
"mysqld".
The idea is that [mysqld] section should contain only mysqld-specific The idea is that [mysqld] section should contain only mysqld-specific
options (no Instance Manager-specific options) to be readable by mysqld options (no Instance Manager-specific options) to be readable by mysqld
...@@ -141,18 +196,36 @@ private: ...@@ -141,18 +196,36 @@ private:
bool mysqld_compatible; bool mysqld_compatible;
/* /*
Mutex protecting the instance. Currently we use it to avoid the Mutex protecting the instance.
double start of the instance. This happens when the instance is starting
and we issue the start command once more.
*/ */
pthread_mutex_t LOCK_instance; pthread_mutex_t LOCK_instance;
/*
This condition variable is used to wake threads waiting for instance to
stop in Instance::stop()
*/
pthread_cond_t COND_instance_stopped;
private:
/* Guarded-instance attributes. */
/* state of an instance (i.e. STARTED, CRASHED, etc.) */
enum_instance_state state;
public:
/* the amount of attemts to restart instance (cleaned up at success) */
int restart_counter;
/* triggered at a crash */
time_t crash_moment;
/* General time field. Used to provide timeouts (at shutdown and restart) */
time_t last_checked;
private:
static const char *get_instance_state_name(enum_instance_state state);
private:
void remove_pid(); void remove_pid();
bool wait_for_stop();
private:
friend class Instance_monitor;
}; };
...@@ -168,9 +241,33 @@ inline bool Instance::is_configured() const ...@@ -168,9 +241,33 @@ inline bool Instance::is_configured() const
} }
inline bool Instance::is_guarded() const
{
return !options.nonguarded;
}
inline const LEX_STRING *Instance::get_name() const inline const LEX_STRING *Instance::get_name() const
{ {
return &options.instance_name; return &options.instance_name;
} }
inline Instance::enum_instance_state Instance::get_state() const
{
return state;
}
inline void Instance::set_state(enum_instance_state new_state)
{
state= new_state;
}
inline bool Instance::is_crashed()
{
return crashed;
}
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_H */ #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_H */
...@@ -24,26 +24,18 @@ ...@@ -24,26 +24,18 @@
#include <mysql_com.h> #include <mysql_com.h>
#include "buffer.h" #include "buffer.h"
#include "guardian.h"
#include "instance.h" #include "instance.h"
#include "log.h" #include "log.h"
#include "manager.h"
#include "mysqld_error.h" #include "mysqld_error.h"
#include "mysql_manager_error.h" #include "mysql_manager_error.h"
#include "options.h" #include "options.h"
#include "priv.h" #include "priv.h"
/*
Note: As we are going to suppost different types of connections,
we shouldn't have connection-specific functions. To avoid it we could
put such functions to the Command-derived class instead.
The command could be easily constructed for a specific connection if
we would provide a special factory for each connection.
*/
C_MODE_START C_MODE_START
/* Procedure needed for HASH initialization */ /**
HASH-routines: get key of instance for storing in hash.
*/
static byte* get_instance_key(const byte* u, uint* len, static byte* get_instance_key(const byte* u, uint* len,
my_bool __attribute__((unused)) t) my_bool __attribute__((unused)) t)
...@@ -53,14 +45,18 @@ static byte* get_instance_key(const byte* u, uint* len, ...@@ -53,14 +45,18 @@ static byte* get_instance_key(const byte* u, uint* len,
return (byte *) instance->options.instance_name.str; return (byte *) instance->options.instance_name.str;
} }
/**
HASH-routines: cleanup handler.
*/
static void delete_instance(void *u) static void delete_instance(void *u)
{ {
Instance *instance= (Instance *) u; Instance *instance= (Instance *) u;
delete instance; delete instance;
} }
/* /**
The option handler to pass to the process_default_option_files finction. The option handler to pass to the process_default_option_files function.
SYNOPSIS SYNOPSIS
process_option() process_option()
...@@ -95,7 +91,7 @@ static int process_option(void *ctx, const char *group, const char *option) ...@@ -95,7 +91,7 @@ static int process_option(void *ctx, const char *group, const char *option)
C_MODE_END C_MODE_END
/* /**
Parse option string. Parse option string.
SYNOPSIS SYNOPSIS
...@@ -136,7 +132,7 @@ static void parse_option(const char *option_str, ...@@ -136,7 +132,7 @@ static void parse_option(const char *option_str,
} }
/* /**
Process one option from the configuration file. Process one option from the configuration file.
SYNOPSIS SYNOPSIS
...@@ -150,6 +146,10 @@ static void parse_option(const char *option_str, ...@@ -150,6 +146,10 @@ static void parse_option(const char *option_str,
process_option(). The caller ensures proper locking process_option(). The caller ensures proper locking
of the instance map object. of the instance map object.
*/ */
/*
Process a given option and assign it to appropricate instance. This is
required for the option handler, passed to my_search_option_files().
*/
int Instance_map::process_one_option(const LEX_STRING *group, int Instance_map::process_one_option(const LEX_STRING *group,
const char *option) const char *option)
...@@ -212,92 +212,97 @@ int Instance_map::process_one_option(const LEX_STRING *group, ...@@ -212,92 +212,97 @@ int Instance_map::process_one_option(const LEX_STRING *group,
} }
/**
Instance_map constructor.
*/
Instance_map::Instance_map() Instance_map::Instance_map()
{ {
pthread_mutex_init(&LOCK_instance_map, 0); pthread_mutex_init(&LOCK_instance_map, 0);
} }
/**
Initialize Instance_map internals.
*/
bool Instance_map::init() bool Instance_map::init()
{ {
return hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0, return hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0,
get_instance_key, delete_instance, 0); get_instance_key, delete_instance, 0);
} }
/**
Reset Instance_map data.
*/
bool Instance_map::reset()
{
hash_free(&hash);
return init();
}
/**
Instance_map destructor.
*/
Instance_map::~Instance_map() Instance_map::~Instance_map()
{ {
pthread_mutex_lock(&LOCK_instance_map); lock();
/*
NOTE: it's necessary to synchronize on each instance before removal,
because Instance-monitoring thread can be still alive an hold the mutex
(because it is detached and we have no control over it).
*/
while (true)
{
Iterator it(this);
Instance *instance= it.next();
if (!instance)
break;
instance->lock();
instance->unlock();
remove_instance(instance);
}
hash_free(&hash); hash_free(&hash);
pthread_mutex_unlock(&LOCK_instance_map); unlock();
pthread_mutex_destroy(&LOCK_instance_map); pthread_mutex_destroy(&LOCK_instance_map);
} }
/**
Lock Instance_map.
*/
void Instance_map::lock() void Instance_map::lock()
{ {
pthread_mutex_lock(&LOCK_instance_map); pthread_mutex_lock(&LOCK_instance_map);
} }
/**
Unlock Instance_map.
*/
void Instance_map::unlock() void Instance_map::unlock()
{ {
pthread_mutex_unlock(&LOCK_instance_map); pthread_mutex_unlock(&LOCK_instance_map);
} }
/*
Re-read instance configuration file.
SYNOPSIS
Instance_map::flush_instances()
DESCRIPTION /**
This function will: Check if there is an active instance or not.
- clear the current list of instances. This removes both
running and stopped instances.
- load a new instance configuration from the file.
- pass on the new map to the guardian thread: it will start
all instances that are marked `guarded' and not yet started.
Note, as the check whether an instance is started is currently
very simple (returns TRUE if there is a MySQL server running
at the given port), this function has some peculiar
side-effects:
* if the port number of a running instance was changed, the
old instance is forgotten, even if it was running. The new
instance will be started at the new port.
* if the configuration was changed in a way that two
instances swapped their port numbers, the guardian thread
will not notice that and simply report that both instances
are configured successfully and running.
In order to avoid such side effects one should never call
FLUSH INSTANCES without prior stop of all running instances.
NOTE: The operation should be invoked with the following locks acquired:
- Guardian;
- Instance_map;
*/ */
int Instance_map::flush_instances()
{
int rc;
/*
Guardian thread relies on the instance map repository for guarding
instances. This is why refreshing instance map, we need (1) to stop
guardian (2) reload the instance map (3) reinitialize the guardian
with new instances.
*/
hash_free(&hash);
hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0,
get_instance_key, delete_instance, 0);
rc= load();
/* don't init guardian if we failed to load instances */
if (!rc)
guardian->init(); // TODO: check error status.
return rc;
}
bool Instance_map::is_there_active_instance() bool Instance_map::is_there_active_instance()
{ {
Instance *instance; Instance *instance;
...@@ -305,29 +310,50 @@ bool Instance_map::is_there_active_instance() ...@@ -305,29 +310,50 @@ bool Instance_map::is_there_active_instance()
while ((instance= iterator.next())) while ((instance= iterator.next()))
{ {
if (guardian->find_instance_node(instance) != NULL || bool active_instance_found;
instance->is_mysqld_running())
{ instance->lock();
active_instance_found= instance->is_active();
instance->unlock();
if (active_instance_found)
return TRUE; return TRUE;
} }
}
return FALSE; return FALSE;
} }
/**
Add an instance into the internal hash.
MT-NOTE: Instance Map must be locked before calling the operation.
*/
int Instance_map::add_instance(Instance *instance) int Instance_map::add_instance(Instance *instance)
{ {
return my_hash_insert(&hash, (byte *) instance); return my_hash_insert(&hash, (byte *) instance);
} }
/**
Remove instance from the internal hash.
MT-NOTE: Instance Map must be locked before calling the operation.
*/
int Instance_map::remove_instance(Instance *instance) int Instance_map::remove_instance(Instance *instance)
{ {
return hash_delete(&hash, (byte *) instance); return hash_delete(&hash, (byte *) instance);
} }
/**
Create a new instance and register it in the internal hash.
MT-NOTE: Instance Map must be locked before calling the operation.
*/
int Instance_map::create_instance(const LEX_STRING *instance_name, int Instance_map::create_instance(const LEX_STRING *instance_name,
const Named_value_arr *options) const Named_value_arr *options)
{ {
...@@ -391,12 +417,22 @@ int Instance_map::create_instance(const LEX_STRING *instance_name, ...@@ -391,12 +417,22 @@ int Instance_map::create_instance(const LEX_STRING *instance_name,
} }
/**
Return a pointer to the instance or NULL, if there is no such instance.
MT-NOTE: Instance Map must be locked before calling the operation.
*/
Instance * Instance_map::find(const LEX_STRING *name) Instance * Instance_map::find(const LEX_STRING *name)
{ {
return (Instance *) hash_search(&hash, (byte *) name->str, name->length); return (Instance *) hash_search(&hash, (byte *) name->str, name->length);
} }
/**
Init instances command line arguments after all options have been loaded.
*/
bool Instance_map::complete_initialization() bool Instance_map::complete_initialization()
{ {
bool mysqld_found; bool mysqld_found;
...@@ -454,7 +490,10 @@ bool Instance_map::complete_initialization() ...@@ -454,7 +490,10 @@ bool Instance_map::complete_initialization()
} }
/* load options from config files and create appropriate instance structures */ /**
Load options from config files and create appropriate instance
structures.
*/
int Instance_map::load() int Instance_map::load()
{ {
...@@ -504,8 +543,9 @@ int Instance_map::load() ...@@ -504,8 +543,9 @@ int Instance_map::load()
} }
/*--- Implementaton of the Instance map iterator class ---*/ /*************************************************************************
{{{ Instance_map::Iterator implementation.
*************************************************************************/
void Instance_map::Iterator::go_to_first() void Instance_map::Iterator::go_to_first()
{ {
...@@ -521,29 +561,12 @@ Instance *Instance_map::Iterator::next() ...@@ -521,29 +561,12 @@ Instance *Instance_map::Iterator::next()
return NULL; return NULL;
} }
/*************************************************************************
const char *Instance_map::get_instance_state_name(Instance *instance) }}}
{ *************************************************************************/
LIST *instance_node;
if (!instance->is_configured())
return "misconfigured";
if ((instance_node= guardian->find_instance_node(instance)) != NULL)
{
/* The instance is managed by Guardian: we can report precise state. */
return Guardian::get_instance_state_name(
guardian->get_instance_state(instance_node));
}
/* The instance is not managed by Guardian: we can report status only. */
return instance->is_mysqld_running() ? "online" : "offline";
}
/* /**
Create a new configuration section for mysqld-instance in the config file. Create a new configuration section for mysqld-instance in the config file.
SYNOPSIS SYNOPSIS
......
...@@ -36,14 +36,17 @@ extern int create_instance_in_file(const LEX_STRING *instance_name, ...@@ -36,14 +36,17 @@ extern int create_instance_in_file(const LEX_STRING *instance_name,
const Named_value_arr *options); const Named_value_arr *options);
/* /**
Instance_map - stores all existing instances Instance_map - stores all existing instances
*/ */
class Instance_map class Instance_map
{ {
public: public:
/* Instance_map iterator */ /**
Instance_map iterator
*/
class Iterator class Iterator
{ {
private: private:
...@@ -57,79 +60,43 @@ public: ...@@ -57,79 +60,43 @@ public:
void go_to_first(); void go_to_first();
Instance *next(); Instance *next();
}; };
friend class Iterator;
public: public:
/*
Return a pointer to the instance or NULL, if there is no such instance.
MT-NOTE: must be called under acquired lock.
*/
Instance *find(const LEX_STRING *name); Instance *find(const LEX_STRING *name);
/* Clear the configuration cache and reload the configuration file. */
int flush_instances();
/* The operation is used to check if there is an active instance or not. */
bool is_there_active_instance(); bool is_there_active_instance();
void lock(); void lock();
void unlock(); void unlock();
bool init(); bool init();
bool reset();
/* int load();
Process a given option and assign it to appropricate instance. This is
required for the option handler, passed to my_search_option_files().
*/
int process_one_option(const LEX_STRING *group, const char *option);
/* int process_one_option(const LEX_STRING *group, const char *option);
Add an instance into the internal hash.
MT-NOTE: the operation must be called under acquired lock.
*/
int add_instance(Instance *instance); int add_instance(Instance *instance);
/*
Remove instance from the internal hash.
MT-NOTE: the operation must be called under acquired lock.
*/
int remove_instance(Instance *instance); int remove_instance(Instance *instance);
/*
Create a new instance and register it in the internal hash.
MT-NOTE: the operation must be called under acquired lock.
*/
int create_instance(const LEX_STRING *instance_name, int create_instance(const LEX_STRING *instance_name,
const Named_value_arr *options); const Named_value_arr *options);
public:
Instance_map(); Instance_map();
~Instance_map(); ~Instance_map();
/*
Retrieve client state name of the given instance.
MT-NOTE: the options must be called under acquired locks of the following
objects:
- Instance_map;
- Guardian;
*/
const char *get_instance_state_name(Instance *instance);
public:
const char *mysqld_path;
Guardian *guardian;
private: private:
/* loads options from config files */
int load();
/* inits instances argv's after all options have been loaded */
bool complete_initialization(); bool complete_initialization();
private: private:
enum { START_HASH_SIZE = 16 }; enum { START_HASH_SIZE = 16 };
pthread_mutex_t LOCK_instance_map; pthread_mutex_t LOCK_instance_map;
HASH hash; HASH hash;
private:
friend class Iterator;
}; };
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_MAP_H */ #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_MAP_H */
...@@ -45,7 +45,6 @@ public: ...@@ -45,7 +45,6 @@ public:
Instance_options(); Instance_options();
~Instance_options(); ~Instance_options();
/* fills in argv */
bool complete_initialization(); bool complete_initialization();
bool set_option(Named_value *option); bool set_option(Named_value *option);
......
...@@ -153,7 +153,7 @@ void Listener::run() ...@@ -153,7 +153,7 @@ void Listener::run()
else else
{ {
shutdown(client_fd, SHUT_RDWR); shutdown(client_fd, SHUT_RDWR);
close(client_fd); closesocket(client_fd);
} }
} }
} }
...@@ -165,7 +165,7 @@ void Listener::run() ...@@ -165,7 +165,7 @@ void Listener::run()
log_info("Listener: shutdown requested, exiting..."); log_info("Listener: shutdown requested, exiting...");
for (i= 0; i < num_sockets; i++) for (i= 0; i < num_sockets; i++)
close(sockets[i]); closesocket(sockets[i]);
#ifndef __WIN__ #ifndef __WIN__
unlink(unix_socket_address.sun_path); unlink(unix_socket_address.sun_path);
...@@ -179,7 +179,7 @@ void Listener::run() ...@@ -179,7 +179,7 @@ void Listener::run()
err: err:
// we have to close the ip sockets in case of error // we have to close the ip sockets in case of error
for (i= 0; i < num_sockets; i++) for (i= 0; i < num_sockets; i++)
close(sockets[i]); closesocket(sockets[i]);
thread_registry->unregister_thread(&thread_info); thread_registry->unregister_thread(&thread_info);
thread_registry->request_shutdown(); thread_registry->request_shutdown();
...@@ -227,7 +227,7 @@ int Listener::create_tcp_socket() ...@@ -227,7 +227,7 @@ int Listener::create_tcp_socket()
{ {
log_error("Listener: bind(ip socket) failed: %s.", log_error("Listener: bind(ip socket) failed: %s.",
(const char *) strerror(errno)); (const char *) strerror(errno));
close(ip_socket); closesocket(ip_socket);
return -1; return -1;
} }
...@@ -235,7 +235,7 @@ int Listener::create_tcp_socket() ...@@ -235,7 +235,7 @@ int Listener::create_tcp_socket()
{ {
log_error("Listener: listen(ip socket) failed: %s.", log_error("Listener: listen(ip socket) failed: %s.",
(const char *) strerror(errno)); (const char *) strerror(errno));
close(ip_socket); closesocket(ip_socket);
return -1; return -1;
} }
......
...@@ -36,6 +36,9 @@ ...@@ -36,6 +36,9 @@
#include "user_map.h" #include "user_map.h"
/**********************************************************************
{{{ Platform-specific implementation.
**********************************************************************/
#ifndef __WIN__ #ifndef __WIN__
void set_signals(sigset_t *mask) void set_signals(sigset_t *mask)
...@@ -91,9 +94,13 @@ int my_sigwait(const sigset_t *set, int *sig) ...@@ -91,9 +94,13 @@ int my_sigwait(const sigset_t *set, int *sig)
#endif #endif
/**********************************************************************
}}}
**********************************************************************/
/********************************************************************** /**********************************************************************
Implementation of checking the actual thread model. {{{ Implementation of checking the actual thread model.
***********************************************************************/ ***********************************************************************/
namespace { /* no-indent */ namespace { /* no-indent */
...@@ -136,6 +143,10 @@ bool check_if_linux_threads(bool *linux_threads) ...@@ -136,6 +143,10 @@ bool check_if_linux_threads(bool *linux_threads)
} }
/**********************************************************************
}}}
***********************************************************************/
/********************************************************************** /**********************************************************************
Manager implementation Manager implementation
...@@ -151,23 +162,35 @@ bool Manager::linux_threads; ...@@ -151,23 +162,35 @@ bool Manager::linux_threads;
#endif // __WIN__ #endif // __WIN__
/**
Request shutdown of guardian and threads registered in Thread_registry.
SYNOPSIS
stop_all_threads()
*/
void Manager::stop_all_threads() void Manager::stop_all_threads()
{ {
/* /*
Let guardian thread know that it should break it's processing cycle, Let Guardian thread know that it should break it's processing cycle,
once it wakes up. once it wakes up.
*/ */
p_guardian->request_shutdown(); p_guardian->request_shutdown();
/* wake guardian */
pthread_cond_signal(&p_guardian->COND_guardian); /* Stop all threads. */
/* stop all threads */
p_thread_registry->deliver_shutdown(); p_thread_registry->deliver_shutdown();
} }
/* /**
manager - entry point to the main instance manager process: start Main manager function.
listener thread, write pid file and enter into signal handling.
SYNOPSIS
main()
DESCRIPTION
This is an entry point to the main instance manager process:
start listener thread, write pid file and enter into signal handling.
See also comments in mysqlmanager.cc to picture general Instance Manager See also comments in mysqlmanager.cc to picture general Instance Manager
architecture. architecture.
...@@ -193,22 +216,33 @@ int Manager::main() ...@@ -193,22 +216,33 @@ int Manager::main()
(const char *) (linux_threads ? "LINUX threads" : "POSIX threads")); (const char *) (linux_threads ? "LINUX threads" : "POSIX threads"));
#endif // __WIN__ #endif // __WIN__
Thread_registry thread_registry;
/* /*
All objects created in the manager() function live as long as All objects created in the Manager object live as long as thread_registry
thread_registry lives, and thread_registry is alive until there are lives, and thread_registry is alive until there are working threads.
working threads.
There are two main purposes of the Thread Registry:
1. Interrupt blocking I/O and signal condition variables in case of
shutdown;
2. Wait for detached threads before shutting down the main thread.
NOTE:
1. Handling shutdown can be done in more elegant manner by introducing
Event (or Condition) object with support of logical operations.
2. Using Thread Registry to wait for detached threads is definitely not
the best way, because when Thread Registry unregisters an thread, the
thread is still alive. Accurate way to wait for threads to stop is
not using detached threads and join all threads before shutdown.
*/ */
Thread_registry thread_registry;
User_map user_map; User_map user_map;
Instance_map instance_map; Instance_map instance_map;
Guardian guardian(&thread_registry, &instance_map, Guardian guardian(&thread_registry, &instance_map);
Options::Main::monitoring_interval);
Listener listener(&thread_registry, &user_map); Listener listener(&thread_registry, &user_map);
p_instance_map= &instance_map; p_instance_map= &instance_map;
p_guardian= instance_map.guardian= &guardian; p_guardian= &guardian;
p_thread_registry= &thread_registry; p_thread_registry= &thread_registry;
p_user_map= &user_map; p_user_map= &user_map;
...@@ -248,7 +282,7 @@ int Manager::main() ...@@ -248,7 +282,7 @@ int Manager::main()
} }
} }
/* write Instance Manager pid file */ /* Write Instance Manager pid file. */
log_info("IM pid file: '%s'; PID: %d.", log_info("IM pid file: '%s'; PID: %d.",
(const char *) Options::Main::pid_file_name, (const char *) Options::Main::pid_file_name,
...@@ -289,6 +323,7 @@ int Manager::main() ...@@ -289,6 +323,7 @@ int Manager::main()
permitted to process instances. And before flush_instances() has permitted to process instances. And before flush_instances() has
completed, there are no instances to guard. completed, there are no instances to guard.
*/ */
if (guardian.start(Thread::DETACHED)) if (guardian.start(Thread::DETACHED))
{ {
log_error("Can not start Guardian thread."); log_error("Can not start Guardian thread.");
...@@ -297,22 +332,12 @@ int Manager::main() ...@@ -297,22 +332,12 @@ int Manager::main()
/* Load instances. */ /* Load instances. */
{ if (Manager::flush_instances())
instance_map.guardian->lock();
instance_map.lock();
int flush_instances_status= instance_map.flush_instances();
instance_map.unlock();
instance_map.guardian->unlock();
if (flush_instances_status)
{ {
log_error("Can not init instances repository."); log_error("Can not init instances repository.");
stop_all_threads(); stop_all_threads();
goto err; goto err;
} }
}
/* Initialize the Listener. */ /* Initialize the Listener. */
...@@ -327,7 +352,8 @@ int Manager::main() ...@@ -327,7 +352,8 @@ int Manager::main()
After the list of guarded instances have been initialized, After the list of guarded instances have been initialized,
Guardian should start them. Guardian should start them.
*/ */
pthread_cond_signal(&guardian.COND_guardian);
guardian.ping();
/* Main loop. */ /* Main loop. */
...@@ -380,7 +406,6 @@ int Manager::main() ...@@ -380,7 +406,6 @@ int Manager::main()
if (!guardian.is_stopped()) if (!guardian.is_stopped())
{ {
guardian.request_shutdown(); guardian.request_shutdown();
pthread_cond_signal(&guardian.COND_guardian);
} }
else else
{ {
...@@ -405,3 +430,64 @@ err: ...@@ -405,3 +430,64 @@ err:
#endif #endif
return rc; return rc;
} }
/**
Re-read instance configuration file.
SYNOPSIS
flush_instances()
DESCRIPTION
This function will:
- clear the current list of instances. This removes both
running and stopped instances.
- load a new instance configuration from the file.
- pass on the new map to the guardian thread: it will start
all instances that are marked `guarded' and not yet started.
Note, as the check whether an instance is started is currently
very simple (returns TRUE if there is a MySQL server running
at the given port), this function has some peculiar
side-effects:
* if the port number of a running instance was changed, the
old instance is forgotten, even if it was running. The new
instance will be started at the new port.
* if the configuration was changed in a way that two
instances swapped their port numbers, the guardian thread
will not notice that and simply report that both instances
are configured successfully and running.
In order to avoid such side effects one should never call
FLUSH INSTANCES without prior stop of all running instances.
*/
bool Manager::flush_instances()
{
p_instance_map->lock();
if (p_instance_map->is_there_active_instance())
{
p_instance_map->unlock();
return TRUE;
}
if (p_instance_map->reset())
{
p_instance_map->unlock();
return TRUE;
}
if (p_instance_map->load())
{
p_instance_map->unlock();
return TRUE; /* Don't init guardian if we failed to load instances. */
}
get_guardian()->init(); /* TODO: check error status. */
get_guardian()->ping();
p_instance_map->unlock();
return FALSE;
}
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE) #if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
#pragma interface #pragma interface
#endif #endif
#include <my_global.h> #include <my_global.h>
class Guardian; class Guardian;
...@@ -30,8 +31,12 @@ class Manager ...@@ -30,8 +31,12 @@ class Manager
{ {
public: public:
static int main(); static int main();
static bool flush_instances();
public:
/** /**
These methods return a non-zero value only for the duration These methods return a non-NULL value only for the duration
of main(). of main().
*/ */
static Instance_map *get_instance_map() { return p_instance_map; } static Instance_map *get_instance_map() { return p_instance_map; }
...@@ -39,6 +44,7 @@ public: ...@@ -39,6 +44,7 @@ public:
static Thread_registry *get_thread_registry() { return p_thread_registry; } static Thread_registry *get_thread_registry() { return p_thread_registry; }
static User_map *get_user_map() { return p_user_map; } static User_map *get_user_map() { return p_user_map; }
public:
#ifndef __WIN__ #ifndef __WIN__
static bool is_linux_threads() { return linux_threads; } static bool is_linux_threads() { return linux_threads; }
#endif // __WIN__ #endif // __WIN__
......
...@@ -77,7 +77,7 @@ int User::init(const char *line) ...@@ -77,7 +77,7 @@ int User::init(const char *line)
"User line: '%s'.", "User line: '%s'.",
(int) password_length, (int) password_length,
(int) SCRAMBLED_PASSWORD_CHAR_LENGTH, (int) SCRAMBLED_PASSWORD_CHAR_LENGTH,
line); (const char *) line);
return 1; return 1;
} }
......
...@@ -394,7 +394,14 @@ Event_parse_data::init_starts(THD *thd) ...@@ -394,7 +394,14 @@ Event_parse_data::init_starts(THD *thd)
if ((not_used= item_starts->get_date(&ltime, TIME_NO_ZERO_DATE))) if ((not_used= item_starts->get_date(&ltime, TIME_NO_ZERO_DATE)))
goto wrong_value; goto wrong_value;
/* Let's check whether time is in the past */ /*
Let's check whether time is in the past.
Note: This call is not post year 2038 safe. That's because
thd->query_start() is of time_t, while gmt_sec_to_TIME()
wants my_time_t. In the case time_t is larger than my_time_t
an overflow might happen and events subsystem will not work as
expected.
*/
thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp, thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp,
(my_time_t) thd->query_start()); (my_time_t) thd->query_start());
...@@ -406,12 +413,12 @@ Event_parse_data::init_starts(THD *thd) ...@@ -406,12 +413,12 @@ Event_parse_data::init_starts(THD *thd)
goto wrong_value; goto wrong_value;
/* /*
This may result in a 1970-01-01 date if ltime is > 2037-xx-xx. Again, after 2038 this code won't work. As
CONVERT_TZ has similar problem.
mysql_priv.h currently lists mysql_priv.h currently lists
#define TIMESTAMP_MAX_YEAR 2038 (see TIME_to_timestamp()) #define TIMESTAMP_MAX_YEAR 2038 (see TIME_to_timestamp())
*/ */
my_tz_UTC->gmt_sec_to_TIME(&ltime,t=TIME_to_timestamp(thd, &ltime, &not_used)); my_tz_UTC->gmt_sec_to_TIME(&ltime,t=TIME_to_timestamp(thd, &ltime,
&not_used));
if (!t) if (!t)
goto wrong_value; goto wrong_value;
...@@ -464,13 +471,13 @@ Event_parse_data::init_ends(THD *thd) ...@@ -464,13 +471,13 @@ Event_parse_data::init_ends(THD *thd)
goto error_bad_params; goto error_bad_params;
/* /*
This may result in a 1970-01-01 date if ltime is > 2037-xx-xx. Again, after 2038 this code won't work. As
CONVERT_TZ has similar problem.
mysql_priv.h currently lists mysql_priv.h currently lists
#define TIMESTAMP_MAX_YEAR 2038 (see TIME_to_timestamp()) #define TIMESTAMP_MAX_YEAR 2038 (see TIME_to_timestamp())
*/ */
DBUG_PRINT("info", ("get the UTC time")); DBUG_PRINT("info", ("get the UTC time"));
my_tz_UTC->gmt_sec_to_TIME(&ltime,t=TIME_to_timestamp(thd, &ltime, &not_used)); my_tz_UTC->gmt_sec_to_TIME(&ltime,t=TIME_to_timestamp(thd, &ltime,
&not_used));
if (!t) if (!t)
goto error_bad_params; goto error_bad_params;
......
...@@ -159,7 +159,6 @@ Event_queue::init_queue(THD *thd, Event_db_repository *db_repo) ...@@ -159,7 +159,6 @@ Event_queue::init_queue(THD *thd, Event_db_repository *db_repo)
{ {
sql_print_error("SCHEDULER: sizeof(my_time_t) != sizeof(time_t) ." sql_print_error("SCHEDULER: sizeof(my_time_t) != sizeof(time_t) ."
"The scheduler may not work correctly. Stopping"); "The scheduler may not work correctly. Stopping");
DBUG_ASSERT(0);
goto err; goto err;
} }
......
...@@ -691,6 +691,19 @@ int ha_commit_trans(THD *thd, bool all) ...@@ -691,6 +691,19 @@ int ha_commit_trans(THD *thd, bool all)
ha_rollback_trans(thd, all); ha_rollback_trans(thd, all);
DBUG_RETURN(1); DBUG_RETURN(1);
} }
if ( is_real_trans
&& opt_readonly
&& ! (thd->security_ctx->master_access & SUPER_ACL)
&& ! thd->slave_thread
)
{
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
ha_rollback_trans(thd, all);
error= 1;
goto end;
}
DBUG_EXECUTE_IF("crash_commit_before", abort();); DBUG_EXECUTE_IF("crash_commit_before", abort(););
/* Close all cursors that can not survive COMMIT */ /* Close all cursors that can not survive COMMIT */
......
...@@ -150,6 +150,23 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, ...@@ -150,6 +150,23 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
} }
} }
if ( write_lock_used
&& opt_readonly
&& ! (thd->security_ctx->master_access & SUPER_ACL)
&& ! thd->slave_thread
)
{
/*
Someone has issued SET GLOBAL READ_ONLY=1 and we want a write lock.
We do not wait for READ_ONLY=0, and fail.
*/
reset_lock_data(sql_lock);
my_free((gptr) sql_lock, MYF(0));
sql_lock=0;
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
break;
}
thd->proc_info="System lock"; thd->proc_info="System lock";
DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info)); DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info));
if (lock_external(thd, tables, count)) if (lock_external(thd, tables, count))
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -1193,7 +1193,6 @@ void st_select_lex::init_select() ...@@ -1193,7 +1193,6 @@ void st_select_lex::init_select()
options= 0; options= 0;
sql_cache= SQL_CACHE_UNSPECIFIED; sql_cache= SQL_CACHE_UNSPECIFIED;
braces= 0; braces= 0;
when_list.empty();
expr_list.empty(); expr_list.empty();
interval_list.empty(); interval_list.empty();
use_index.empty(); use_index.empty();
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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