summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/my_pthread.h12
-rwxr-xr-xmysql-test/mysql-test-run.pl1
-rw-r--r--mysql-test/r/events_bugs.result53
-rw-r--r--mysql-test/r/func_if.result3
-rw-r--r--mysql-test/r/im_daemon_life_cycle.result2
-rw-r--r--mysql-test/r/im_options.result10
-rw-r--r--mysql-test/r/im_utils.result2
-rw-r--r--mysql-test/r/init_file.result7
-rw-r--r--mysql-test/r/ndb_dd_basic.result2
-rw-r--r--mysql-test/r/partition_innodb.result4
-rw-r--r--mysql-test/r/ps_1general.result4
-rw-r--r--mysql-test/r/show_check.result14
-rw-r--r--mysql-test/r/sp-vars.result62
-rw-r--r--mysql-test/r/subselect.result155
-rw-r--r--mysql-test/r/view.result155
-rw-r--r--mysql-test/std_data/init_file.dat9
-rw-r--r--mysql-test/t/disabled.def1
-rw-r--r--mysql-test/t/events_bugs.test57
-rw-r--r--mysql-test/t/func_if.test11
-rw-r--r--mysql-test/t/im_cmd_line.imtest4
-rw-r--r--mysql-test/t/im_daemon_life_cycle.imtest19
-rw-r--r--mysql-test/t/init_file.test9
-rwxr-xr-xmysql-test/t/kill_n_check.sh2
-rwxr-xr-xmysql-test/t/log.sh2
-rw-r--r--mysql-test/t/ps_1general.test10
-rw-r--r--mysql-test/t/show_check.test18
-rw-r--r--mysql-test/t/sp-vars.test65
-rw-r--r--mysql-test/t/subselect.test127
-rw-r--r--mysql-test/t/view.test148
-rwxr-xr-xmysql-test/t/wait_for_process.sh2
-rwxr-xr-xmysql-test/t/wait_for_socket.sh2
-rw-r--r--mysys/my_wincond.c149
-rw-r--r--server-tools/instance-manager/IMService.cpp69
-rw-r--r--server-tools/instance-manager/IMService.h7
-rw-r--r--server-tools/instance-manager/Makefile.am4
-rw-r--r--server-tools/instance-manager/WindowsService.cpp27
-rw-r--r--server-tools/instance-manager/WindowsService.h6
-rw-r--r--server-tools/instance-manager/angel.cc406
-rw-r--r--server-tools/instance-manager/angel.h34
-rw-r--r--server-tools/instance-manager/commands.cc6
-rw-r--r--server-tools/instance-manager/listener.cc3
-rw-r--r--server-tools/instance-manager/manager.cc135
-rw-r--r--server-tools/instance-manager/manager.h3
-rw-r--r--server-tools/instance-manager/mysqlmanager.cc375
-rw-r--r--server-tools/instance-manager/priv.cc8
-rw-r--r--server-tools/instance-manager/priv.h2
-rw-r--r--server-tools/instance-manager/thread_registry.cc21
-rw-r--r--server-tools/instance-manager/thread_registry.h3
-rw-r--r--sql/event_data_objects.cc112
-rw-r--r--sql/event_data_objects.h25
-rw-r--r--sql/event_queue.cc247
-rw-r--r--sql/event_queue.h31
-rw-r--r--sql/event_scheduler.cc180
-rw-r--r--sql/event_scheduler.h29
-rw-r--r--sql/events.cc198
-rw-r--r--sql/events.h16
-rw-r--r--sql/ha_ndbcluster.cc1
-rw-r--r--sql/ha_ndbcluster_binlog.cc1
-rw-r--r--sql/ha_partition.cc5
-rw-r--r--sql/handler.cc11
-rw-r--r--sql/item_cmpfunc.cc74
-rw-r--r--sql/item_cmpfunc.h86
-rw-r--r--sql/item_subselect.cc30
-rw-r--r--sql/item_subselect.h20
-rw-r--r--sql/mysql_priv.h2
-rw-r--r--sql/mysqld.cc4
-rw-r--r--sql/set_var.cc2
-rw-r--r--sql/sp_head.cc2
-rw-r--r--sql/sql_base.cc8
-rw-r--r--sql/sql_cache.cc18
-rw-r--r--sql/sql_class.cc1
-rw-r--r--sql/sql_lex.cc11
-rw-r--r--sql/sql_lex.h6
-rw-r--r--sql/sql_parse.cc1175
-rw-r--r--sql/sql_yacc.yy196
-rw-r--r--sql/table.cc1
-rw-r--r--sql/table.h2
77 files changed, 3591 insertions, 1133 deletions
diff --git a/include/my_pthread.h b/include/my_pthread.h
index 349dee8be3d..4df105e1b20 100644
--- a/include/my_pthread.h
+++ b/include/my_pthread.h
@@ -68,7 +68,17 @@ typedef struct st_pthread_link {
typedef struct {
uint32 waiting;
- HANDLE semaphore;
+ CRITICAL_SECTION lock_waiting;
+
+ enum {
+ SIGNAL= 0,
+ BROADCAST= 1,
+ MAX_EVENTS= 2
+ } EVENTS;
+
+ HANDLE events[MAX_EVENTS];
+ HANDLE broadcast_block_event;
+
} pthread_cond_t;
diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl
index c105a69e861..2017da8a9c1 100755
--- a/mysql-test/mysql-test-run.pl
+++ b/mysql-test/mysql-test-run.pl
@@ -3036,6 +3036,7 @@ language = $path_language
character-sets-dir = $path_charsetsdir
basedir = $path_my_basedir
server_id = $server_id
+shutdown-delay = 10
skip-stack-trace
skip-innodb
skip-ndbcluster
diff --git a/mysql-test/r/events_bugs.result b/mysql-test/r/events_bugs.result
index 44b930e0705..a7f0594588d 100644
--- a/mysql-test/r/events_bugs.result
+++ b/mysql-test/r/events_bugs.result
@@ -325,4 +325,57 @@ drop event e22830_3;
drop event e22830_4;
drop table t1;
drop table t2;
+DROP USER mysqltest_u1@localhost;
+CREATE USER mysqltest_u1@localhost;
+GRANT EVENT ON events_test.* TO mysqltest_u1@localhost;
+CREATE EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1;
+SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS;
+event_name definer
+e1 root@localhost
+DROP EVENT e1;
+CREATE DEFINER=CURRENT_USER EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1;
+SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS;
+event_name definer
+e1 root@localhost
+ALTER DEFINER=mysqltest_u1@localhost EVENT e1 ON SCHEDULE EVERY 1 HOUR;
+SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS;
+event_name definer
+e1 mysqltest_u1@localhost
+DROP EVENT e1;
+CREATE DEFINER=CURRENT_USER() EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1;
+SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS;
+event_name definer
+e1 root@localhost
+DROP EVENT e1;
+CREATE DEFINER=mysqltest_u1@localhost EVENT e1 ON SCHEDULE EVERY 1 DAY DO
+SELECT 1;
+SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS;
+event_name definer
+e1 mysqltest_u1@localhost
+DROP EVENT e1;
+CREATE EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1;
+SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS;
+event_name definer
+e1 mysqltest_u1@localhost
+DROP EVENT e1;
+CREATE DEFINER=CURRENT_USER EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1;
+SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS;
+event_name definer
+e1 mysqltest_u1@localhost
+ALTER DEFINER=root@localhost EVENT e1 ON SCHEDULE EVERY 1 HOUR;
+ERROR 42000: Access denied; you need the SUPER privilege for this operation
+SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS;
+event_name definer
+e1 mysqltest_u1@localhost
+DROP EVENT e1;
+CREATE DEFINER=CURRENT_USER() EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1;
+SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS;
+event_name definer
+e1 mysqltest_u1@localhost
+DROP EVENT e1;
+CREATE DEFINER=root@localhost EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1;
+ERROR 42000: Access denied; you need the SUPER privilege for this operation
+DROP EVENT e1;
+ERROR HY000: Unknown event 'e1'
+DROP USER mysqltest_u1@localhost;
drop database events_test;
diff --git a/mysql-test/r/func_if.result b/mysql-test/r/func_if.result
index 81675d46c82..e9a17324397 100644
--- a/mysql-test/r/func_if.result
+++ b/mysql-test/r/func_if.result
@@ -128,3 +128,6 @@ f1 f2 if(f1, 40.0, 5.00)
0 0 5.00
1 1 40.00
drop table t1;
+select if(0, 18446744073709551610, 18446744073709551610);
+if(0, 18446744073709551610, 18446744073709551610)
+18446744073709551610
diff --git a/mysql-test/r/im_daemon_life_cycle.result b/mysql-test/r/im_daemon_life_cycle.result
index a2baeac5f14..b3afb15f207 100644
--- a/mysql-test/r/im_daemon_life_cycle.result
+++ b/mysql-test/r/im_daemon_life_cycle.result
@@ -21,6 +21,6 @@ Success: the process was restarted.
Success: server is ready to accept connection on socket.
SHOW INSTANCE STATUS mysqld1;
instance_name state version_number version mysqld_compatible
-mysqld1 online VERSION_NUMBER VERSION no
+mysqld1 STATE VERSION_NUMBER VERSION no
STOP INSTANCE mysqld2;
Success: the process has been stopped.
diff --git a/mysql-test/r/im_options.result b/mysql-test/r/im_options.result
index f35f226f665..3225db0c8c5 100644
--- a/mysql-test/r/im_options.result
+++ b/mysql-test/r/im_options.result
@@ -1,13 +1,9 @@
---------------------------------------------------------------------
-server_id = 1
-server_id = 2
---------------------------------------------------------------------
SHOW VARIABLES LIKE 'server_id';
Variable_name Value
server_id 1
SHOW INSTANCES;
instance_name state
-mysqld1 starting
+mysqld1 XXXXX
mysqld2 offline
UNSET mysqld1.server_id;
ERROR HY000: The instance is active. Stop the instance first
@@ -86,7 +82,7 @@ UNSET mysqld2.aaa, mysqld3.bbb, mysqld2.ccc, mysqld3.ddd;
--------------------------------------------------------------------
--------------------------------------------------------------------
SET mysqld2.aaa, mysqld3.bbb, mysqld.ccc = 0010;
-ERROR HY000: Bad instance name. Check that the instance with such a name exists
+ERROR HY000: Unknown instance name
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
@@ -98,7 +94,7 @@ ERROR HY000: The instance is active. Stop the instance first
--------------------------------------------------------------------
--------------------------------------------------------------------
UNSET mysqld2.server_id, mysqld3.server_id, mysqld.ccc;
-ERROR HY000: Bad instance name. Check that the instance with such a name exists
+ERROR HY000: Unknown instance name
--------------------------------------------------------------------
server_id = 1
server_id=2
diff --git a/mysql-test/r/im_utils.result b/mysql-test/r/im_utils.result
index b7c68965ada..397050635e1 100644
--- a/mysql-test/r/im_utils.result
+++ b/mysql-test/r/im_utils.result
@@ -19,6 +19,7 @@ language VALUE
character-sets-dir VALUE
basedir VALUE
server_id VALUE
+shutdown-delay VALUE
skip-stack-trace VALUE
skip-innodb VALUE
skip-ndbcluster VALUE
@@ -37,6 +38,7 @@ language VALUE
character-sets-dir VALUE
basedir VALUE
server_id VALUE
+shutdown-delay VALUE
skip-stack-trace VALUE
skip-innodb VALUE
skip-ndbcluster VALUE
diff --git a/mysql-test/r/init_file.result b/mysql-test/r/init_file.result
index 1569f2c3d68..6394014f3e5 100644
--- a/mysql-test/r/init_file.result
+++ b/mysql-test/r/init_file.result
@@ -1,3 +1,10 @@
+INSERT INTO init_file.startup VALUES ( NOW() );
+SELECT * INTO @X FROM init_file.startup limit 0,1;
+SELECT * INTO @Y FROM init_file.startup limit 1,1;
+SELECT YEAR(@X)-YEAR(@Y);
+YEAR(@X)-YEAR(@Y)
+0
+DROP DATABASE init_file;
ok
end of 4.1 tests
select * from t1;
diff --git a/mysql-test/r/ndb_dd_basic.result b/mysql-test/r/ndb_dd_basic.result
index 724b42b6db3..014858e6856 100644
--- a/mysql-test/r/ndb_dd_basic.result
+++ b/mysql-test/r/ndb_dd_basic.result
@@ -11,7 +11,7 @@ ADD UNDOFILE 'undofile02.dat'
INITIAL_SIZE = 4M
ENGINE=XYZ;
Warnings:
-Error 1286 Unknown table engine 'XYZ'
+Warning 1286 Unknown table engine 'XYZ'
Error 1466 Table storage engine 'MyISAM' does not support the create option 'TABLESPACE or LOGFILE GROUP'
CREATE TABLESPACE ts1
ADD DATAFILE 'datafile.dat'
diff --git a/mysql-test/r/partition_innodb.result b/mysql-test/r/partition_innodb.result
index ffc39820340..a9e43653506 100644
--- a/mysql-test/r/partition_innodb.result
+++ b/mysql-test/r/partition_innodb.result
@@ -54,7 +54,7 @@ create table t1 (a int)
engine = x
partition by key (a);
Warnings:
-Error 1286 Unknown table engine 'x'
+Warning 1286 Unknown table engine 'x'
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
@@ -67,7 +67,7 @@ partition by list (a)
(partition p0 values in (0));
alter table t1 engine = x;
Warnings:
-Error 1286 Unknown table engine 'x'
+Warning 1286 Unknown table engine 'x'
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
diff --git a/mysql-test/r/ps_1general.result b/mysql-test/r/ps_1general.result
index 762ceeaa03b..391d22d232b 100644
--- a/mysql-test/r/ps_1general.result
+++ b/mysql-test/r/ps_1general.result
@@ -304,7 +304,9 @@ execute stmt4;
Variable_name Value
sql_mode
prepare stmt4 from ' show engine bdb logs ';
-execute stmt4;
+ERROR 42000: Unknown table engine 'bdb'
+prepare stmt4 from ' show engine foo logs ';
+ERROR 42000: Unknown table engine 'foo'
prepare stmt4 from ' show grants for user ';
prepare stmt4 from ' show create table t2 ';
prepare stmt4 from ' show master status ';
diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result
index 516355839b5..986fe32ff98 100644
--- a/mysql-test/r/show_check.result
+++ b/mysql-test/r/show_check.result
@@ -730,4 +730,18 @@ show keys from `mysqlttest\1`.`a\b`;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment
drop table `mysqlttest\1`.`a\b`;
drop database `mysqlttest\1`;
+show engine foobar status;
+ERROR 42000: Unknown table engine 'foobar'
+show engine foobar logs;
+ERROR 42000: Unknown table engine 'foobar'
+show engine foobar mutex;
+ERROR 42000: Unknown table engine 'foobar'
+show engine mutex status;
+ERROR 42000: Unknown table engine 'mutex'
+show engine csv status;
+Type Name Status
+show engine csv logs;
+Type Name Status
+show engine csv mutex;
+Type Name Status
End of 5.1 tests
diff --git a/mysql-test/r/sp-vars.result b/mysql-test/r/sp-vars.result
index 8b59fa371cc..a9024156c6e 100644
--- a/mysql-test/r/sp-vars.result
+++ b/mysql-test/r/sp-vars.result
@@ -431,17 +431,17 @@ SELECT HEX(v10);
END|
CALL p1();
HEX(v1)
-01
+1
HEX(v2)
-00
+0
HEX(v3)
-05
+5
HEX(v4)
5555555555555555
HEX(v5)
-07
+7
HEX(v6)
-0000000000000005
+5
HEX(v7)
80
HEX(v8)
@@ -748,12 +748,60 @@ HEX(b) b = 0 b = FALSE b IS FALSE b = 1 b = TRUE b IS TRUE
1 0 0 0 1 1 1
call p2();
HEX(vb) vb = 0 vb = FALSE vb IS FALSE vb = 1 vb = TRUE vb IS TRUE
-00 1 1 1 0 0 0
+0 1 1 1 0 0 0
HEX(vb) vb = 0 vb = FALSE vb IS FALSE vb = 1 vb = TRUE vb IS TRUE
-01 0 0 1 1 1 0
+1 0 0 0 1 1 1
DROP TABLE t1;
DROP PROCEDURE p1;
DROP PROCEDURE p2;
+DROP TABLE IF EXISTS table_12976_a;
+DROP TABLE IF EXISTS table_12976_b;
+DROP PROCEDURE IF EXISTS proc_12976_a;
+DROP PROCEDURE IF EXISTS proc_12976_b;
+CREATE TABLE table_12976_a (val bit(1));
+CREATE TABLE table_12976_b(
+appname varchar(15),
+emailperm bit not null default 1,
+phoneperm bit not null default 0);
+insert into table_12976_b values ('A', b'1', b'1'), ('B', b'0', b'0');
+CREATE PROCEDURE proc_12976_a()
+BEGIN
+declare localvar bit(1);
+SELECT val INTO localvar FROM table_12976_a;
+SELECT coalesce(localvar, 1)+1, coalesce(val, 1)+1 FROM table_12976_a;
+END||
+CREATE PROCEDURE proc_12976_b(
+name varchar(15),
+out ep bit,
+out msg varchar(10))
+BEGIN
+SELECT emailperm into ep FROM table_12976_b where (appname = name);
+IF ep is true THEN
+SET msg = 'True';
+ELSE
+SET msg = 'False';
+END IF;
+END||
+INSERT table_12976_a VALUES (0);
+call proc_12976_a();
+coalesce(localvar, 1)+1 coalesce(val, 1)+1
+1 1
+UPDATE table_12976_a set val=1;
+call proc_12976_a();
+coalesce(localvar, 1)+1 coalesce(val, 1)+1
+2 2
+call proc_12976_b('A', @ep, @msg);
+select @ep, @msg;
+@ep @msg
+1 True
+call proc_12976_b('B', @ep, @msg);
+select @ep, @msg;
+@ep @msg
+0 False
+DROP TABLE table_12976_a;
+DROP TABLE table_12976_b;
+DROP PROCEDURE proc_12976_a;
+DROP PROCEDURE proc_12976_b;
---------------------------------------------------------------
BUG#9572
diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result
index 43abef692e9..d59017a7fc5 100644
--- a/mysql-test/r/subselect.result
+++ b/mysql-test/r/subselect.result
@@ -224,7 +224,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 2 100.00
3 DEPENDENT SUBQUERY t3 ALL NULL NULL NULL NULL 3 100.00 Using where
Warnings:
-Note 1276 Field or reference 'test.t4.a' of SELECT #3 was resolved in SELECT #1
+Note 1276 Field or reference 't4.a' of SELECT #3 was resolved in SELECT #1
Note 1003 select `test`.`t4`.`b` AS `b`,(select avg((`test`.`t2`.`a` + (select min(`test`.`t3`.`a`) AS `min(t3.a)` from `test`.`t3` where (`test`.`t3`.`a` >= `test`.`t4`.`a`)))) AS `avg(t2.a+(select min(t3.a) from t3 where t3.a >= t4.a))` from `test`.`t2`) AS `(select avg(t2.a+(select min(t3.a) from t3 where t3.a >= t4.a)) from t2)` from `test`.`t4`
select * from t3 where exists (select * from t2 where t2.b=t3.a);
a
@@ -313,8 +313,8 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
3 DEPENDENT UNION t5 ALL NULL NULL NULL NULL 2 100.00 Using where
NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL NULL
Warnings:
-Note 1276 Field or reference 'test.t2.a' of SELECT #2 was resolved in SELECT #1
-Note 1276 Field or reference 'test.t2.a' of SELECT #3 was resolved in SELECT #1
+Note 1276 Field or reference 't2.a' of SELECT #2 was resolved in SELECT #1
+Note 1276 Field or reference 't2.a' of SELECT #3 was resolved in SELECT #1
Note 1003 select (select `test`.`t1`.`a` AS `a` from `test`.`t1` where (`test`.`t1`.`a` = `test`.`t2`.`a`) union select `test`.`t5`.`a` AS `a` from `test`.`t5` where (`test`.`t5`.`a` = `test`.`t2`.`a`)) AS `(select a from t1 where t1.a=t2.a union select a from t5 where t5.a=t2.a)`,`test`.`t2`.`a` AS `a` from `test`.`t2`
select (select a from t1 where t1.a=t2.a union all select a from t5 where t5.a=t2.a), a from t2;
ERROR 21000: Subquery returns more than 1 row
@@ -330,9 +330,9 @@ patient_uq clinic_uq
explain extended select * from t6 where exists (select * from t7 where uq = clinic_uq);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t6 ALL NULL NULL NULL NULL 4 100.00 Using where
-2 DEPENDENT SUBQUERY t7 eq_ref PRIMARY PRIMARY 4 t6.clinic_uq 1 100.00 Using where; Using index
+2 DEPENDENT SUBQUERY t7 eq_ref PRIMARY PRIMARY 4 test.t6.clinic_uq 1 100.00 Using index
Warnings:
-Note 1276 Field or reference 'test.t6.clinic_uq' of SELECT #2 was resolved in SELECT #1
+Note 1276 Field or reference 'clinic_uq' of SELECT #2 was resolved in SELECT #1
Note 1003 select `test`.`t6`.`patient_uq` AS `patient_uq`,`test`.`t6`.`clinic_uq` AS `clinic_uq` from `test`.`t6` where exists(select 1 AS `Not_used` from `test`.`t7` where (`test`.`t7`.`uq` = `test`.`t6`.`clinic_uq`))
select * from t1 where a= (select a from t2,t4 where t2.b=t4.b);
ERROR 23000: Column 'a' in field list is ambiguous
@@ -868,7 +868,7 @@ explain extended select (select a+1) from t1;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00
Warnings:
-Note 1276 Field or reference 'test.t1.a' of SELECT #2 was resolved in SELECT #1
+Note 1276 Field or reference 'a' of SELECT #2 was resolved in SELECT #1
Note 1249 Select 2 was reduced during optimization
Note 1003 select (`test`.`t1`.`a` + 1) AS `(select a+1)` from `test`.`t1`
select (select a+1) from t1;
@@ -1741,9 +1741,9 @@ Note 1003 select `test`.`t1`.`id` AS `id`,`test`.`t1`.`text` AS `text` from `tes
explain extended select * from t1 as tt where not exists (select id from t1 where id < 8 and (id = tt.id or id is null) having id is not null);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY tt ALL NULL NULL NULL NULL 12 100.00 Using where
-2 DEPENDENT SUBQUERY t1 eq_ref PRIMARY PRIMARY 4 tt.id 1 100.00 Using where; Using index
+2 DEPENDENT SUBQUERY t1 eq_ref PRIMARY PRIMARY 4 test.tt.id 1 100.00 Using where; Using index
Warnings:
-Note 1276 Field or reference 'test.tt.id' of SELECT #2 was resolved in SELECT #1
+Note 1276 Field or reference 'tt.id' of SELECT #2 was resolved in SELECT #1
Note 1003 select `test`.`tt`.`id` AS `id`,`test`.`tt`.`text` AS `text` from `test`.`t1` `tt` where (not(exists(select `test`.`t1`.`id` AS `id` from `test`.`t1` where ((`test`.`t1`.`id` < 8) and (`test`.`t1`.`id` = `test`.`tt`.`id`)) having (`test`.`t1`.`id` is not null))))
insert into t1 (id, text) values (1000, 'text1000'), (1001, 'text1001');
create table t2 (id int not null, text varchar(20) not null default '', primary key (id));
@@ -2279,7 +2279,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY up ALL NULL NULL NULL NULL 2 100.00 Using where
2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 2 100.00 Using where
Warnings:
-Note 1276 Field or reference 'test.up.a' of SELECT #2 was resolved in SELECT #1
+Note 1276 Field or reference 'up.a' of SELECT #2 was resolved in SELECT #1
Note 1003 select `test`.`up`.`a` AS `a`,`test`.`up`.`b` AS `b` from `test`.`t1` `up` where exists(select 1 AS `Not_used` from `test`.`t1` where (`test`.`t1`.`a` = `test`.`up`.`a`))
drop table t1;
CREATE TABLE t1 (t1_a int);
@@ -3718,45 +3718,116 @@ SELECT * FROM t1 WHERE _utf8'a' = ANY (SELECT s1 FROM t1);
s1
a
DROP TABLE t1;
-CREATE TABLE t1(f1 int);
-CREATE TABLE t2(f2 int, f21 int, f3 timestamp);
-INSERT INTO t1 VALUES (1),(1),(2),(2);
-INSERT INTO t2 VALUES (1,1,"2004-02-29 11:11:11"), (2,2,"2004-02-29 11:11:11");
-SELECT ((SELECT f2 FROM t2 WHERE f21=f1 LIMIT 1) * COUNT(f1)) AS sq FROM t1 GROUP BY f1;
-sq
+DROP TABLE IF EXISTS t1;
+DROP TABLE IF EXISTS t2;
+DROP TABLE IF EXISTS t1xt2;
+CREATE TABLE t1 (
+id_1 int(5) NOT NULL,
+t varchar(4) DEFAULT NULL
+);
+CREATE TABLE t2 (
+id_2 int(5) NOT NULL,
+t varchar(4) DEFAULT NULL
+);
+CREATE TABLE t1xt2 (
+id_1 int(5) NOT NULL,
+id_2 int(5) NOT NULL
+);
+INSERT INTO t1 VALUES (1, 'a'), (2, 'b'), (3, 'c'), (4, 'd');
+INSERT INTO t2 VALUES (2, 'bb'), (3, 'cc'), (4, 'dd'), (12, 'aa');
+INSERT INTO t1xt2 VALUES (2, 2), (3, 3), (4, 4);
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 IN (SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1));
+id_1
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 IN ((SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1)));
+id_1
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 IN (((SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1))));
+id_1
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 NOT IN (SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1));
+id_1
+1
2
+3
4
-SELECT (SELECT SUM(1) FROM t2 ttt GROUP BY t2.f3 LIMIT 1) AS tt FROM t2;
-tt
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 NOT IN ((SELECT t1xt2.id_2 FROM t1xt2 where t1.id_1 = t1xt2.id_1)));
+id_1
+1
2
+3
+4
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 NOT IN (((SELECT t1xt2.id_2 FROM t1xt2 where t1.id_1 = t1xt2.id_1))));
+id_1
+1
2
-PREPARE stmt1 FROM 'SELECT ((SELECT f2 FROM t2 WHERE f21=f1 LIMIT 1) * COUNT(f1)) AS sq FROM t1 GROUP BY f1';
-EXECUTE stmt1;
-sq
+3
+4
+insert INTO t1xt2 VALUES (1, 12);
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 IN (SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1));
+id_1
+1
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 IN ((SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1)));
+id_1
+1
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 IN (((SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1))));
+id_1
+1
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 NOT IN (SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1));
+id_1
2
+3
4
-EXECUTE stmt1;
-sq
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 NOT IN ((SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1)));
+id_1
2
+3
+4
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 NOT IN (((SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1))));
+id_1
+2
+3
+4
+insert INTO t1xt2 VALUES (2, 12);
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 IN (SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1));
+id_1
+1
+2
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 IN ((SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1)));
+id_1
+1
+2
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 IN (((SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1))));
+id_1
+1
+2
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 NOT IN (SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1));
+id_1
+3
+4
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 NOT IN ((SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1)));
+id_1
+3
+4
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 NOT IN (((SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1))));
+id_1
+3
4
-DEALLOCATE PREPARE stmt1;
-SELECT f2, AVG(f21),
-(SELECT t.f3 FROM t2 AS t WHERE t2.f2=t.f2 AND t.f3=MAX(t2.f3)) AS test
-FROM t2 GROUP BY f2;
-f2 AVG(f21) test
-1 1.0000 2004-02-29 11:11:11
-2 2.0000 2004-02-29 11:11:11
-DROP TABLE t1,t2;
-CREATE TABLE t1 (a int, b INT, c CHAR(10) NOT NULL);
-INSERT INTO t1 VALUES
-(1,1,'a'), (1,2,'b'), (1,3,'c'), (1,4,'d'), (1,5,'e'),
-(2,1,'f'), (2,2,'g'), (2,3,'h'), (3,4,'i'), (3,3,'j'),
-(3,2,'k'), (3,1,'l'), (1,9,'m');
-SELECT a, MAX(b),
-(SELECT t.c FROM t1 AS t WHERE t1.a=t.a AND t.b=MAX(t1.b)) AS test
-FROM t1 GROUP BY a;
-a MAX(b) test
-1 9 m
-2 3 h
-3 4 i
DROP TABLE t1;
+DROP TABLE t2;
+DROP TABLE t1xt2;
diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result
index 8f83e1acb09..5e81df99d18 100644
--- a/mysql-test/r/view.result
+++ b/mysql-test/r/view.result
@@ -834,14 +834,16 @@ show create view v1;
View Create View
v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select 99999999999999999999999999999999999999999999999999999 AS `col1`
drop view v1;
-create table tü (cü char);
-create view vü as select cü from tü;
-insert into vü values ('ü');
-select * from vü;
-cü
-drop view vü;
-drop table tü;
+set names utf8;
+create table tü (cü char);
+create view vü as select cü from tü;
+insert into vü values ('ü');
+select * from vü;
+cü
+ü
+drop view vü;
+drop table tü;
+set names latin1;
create table t1 (a int, b int);
insert into t1 values (1,2), (1,3), (2,4), (2,5), (3,10);
create view v1(c) as select a+1 from t1 where b >= 4;
@@ -3026,7 +3028,7 @@ View Create View
v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select _latin1'The\ZEnd' AS `TheEnd`
DROP VIEW v1;
CREATE TABLE t1 (mydate DATETIME);
-INSERT INTO t1 VALUES
+INSERT INTO t1 VALUES
('2007-01-01'), ('2007-01-02'), ('2007-01-30'), ('2007-01-31');
CREATE VIEW v1 AS SELECT mydate from t1;
SELECT * FROM t1 WHERE mydate BETWEEN '2007-01-01' AND '2007-01-31';
@@ -3126,6 +3128,141 @@ code COUNT(DISTINCT country)
100 2
DROP VIEW v1;
DROP TABLE t1;
+DROP VIEW IF EXISTS v1;
+SELECT * FROM (SELECT 1) AS t;
+1
+1
+CREATE VIEW v1 AS SELECT * FROM (SELECT 1) AS t;
+ERROR HY000: View's SELECT contains a subquery in the FROM clause
+# Previously the following would fail.
+SELECT * FROM (SELECT 1) AS t;
+1
+1
+drop view if exists view_24532_a;
+drop view if exists view_24532_b;
+drop table if exists table_24532;
+create table table_24532 (
+a int,
+b bigint,
+c int(4),
+d bigint(48)
+);
+create view view_24532_a as
+select
+a IS TRUE,
+a IS NOT TRUE,
+a IS FALSE,
+a IS NOT FALSE,
+a IS UNKNOWN,
+a IS NOT UNKNOWN,
+a is NULL,
+a IS NOT NULL,
+ISNULL(a),
+b IS TRUE,
+b IS NOT TRUE,
+b IS FALSE,
+b IS NOT FALSE,
+b IS UNKNOWN,
+b IS NOT UNKNOWN,
+b is NULL,
+b IS NOT NULL,
+ISNULL(b),
+c IS TRUE,
+c IS NOT TRUE,
+c IS FALSE,
+c IS NOT FALSE,
+c IS UNKNOWN,
+c IS NOT UNKNOWN,
+c is NULL,
+c IS NOT NULL,
+ISNULL(c),
+d IS TRUE,
+d IS NOT TRUE,
+d IS FALSE,
+d IS NOT FALSE,
+d IS UNKNOWN,
+d IS NOT UNKNOWN,
+d is NULL,
+d IS NOT NULL,
+ISNULL(d)
+from table_24532;
+describe view_24532_a;
+Field Type Null Key Default Extra
+a IS TRUE int(1) NO 0
+a IS NOT TRUE int(1) NO 0
+a IS FALSE int(1) NO 0
+a IS NOT FALSE int(1) NO 0
+a IS UNKNOWN int(1) NO 0
+a IS NOT UNKNOWN int(1) NO 0
+a is NULL int(1) NO 0
+a IS NOT NULL int(1) NO 0
+ISNULL(a) int(1) NO 0
+b IS TRUE int(1) NO 0
+b IS NOT TRUE int(1) NO 0
+b IS FALSE int(1) NO 0
+b IS NOT FALSE int(1) NO 0
+b IS UNKNOWN int(1) NO 0
+b IS NOT UNKNOWN int(1) NO 0
+b is NULL int(1) NO 0
+b IS NOT NULL int(1) NO 0
+ISNULL(b) int(1) NO 0
+c IS TRUE int(1) NO 0
+c IS NOT TRUE int(1) NO 0
+c IS FALSE int(1) NO 0
+c IS NOT FALSE int(1) NO 0
+c IS UNKNOWN int(1) NO 0
+c IS NOT UNKNOWN int(1) NO 0
+c is NULL int(1) NO 0
+c IS NOT NULL int(1) NO 0
+ISNULL(c) int(1) NO 0
+d IS TRUE int(1) NO 0
+d IS NOT TRUE int(1) NO 0
+d IS FALSE int(1) NO 0
+d IS NOT FALSE int(1) NO 0
+d IS UNKNOWN int(1) NO 0
+d IS NOT UNKNOWN int(1) NO 0
+d is NULL int(1) NO 0
+d IS NOT NULL int(1) NO 0
+ISNULL(d) int(1) NO 0
+create view view_24532_b as
+select
+a IS TRUE,
+if(ifnull(a, 0), 1, 0) as old_istrue,
+a IS NOT TRUE,
+if(ifnull(a, 0), 0, 1) as old_isnottrue,
+a IS FALSE,
+if(ifnull(a, 1), 0, 1) as old_isfalse,
+a IS NOT FALSE,
+if(ifnull(a, 1), 1, 0) as old_isnotfalse
+from table_24532;
+describe view_24532_b;
+Field Type Null Key Default Extra
+a IS TRUE int(1) NO 0
+old_istrue int(1) NO 0
+a IS NOT TRUE int(1) NO 0
+old_isnottrue int(1) NO 0
+a IS FALSE int(1) NO 0
+old_isfalse int(1) NO 0
+a IS NOT FALSE int(1) NO 0
+old_isnotfalse int(1) NO 0
+show create view view_24532_b;
+View Create View
+view_24532_b CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `view_24532_b` AS select (`table_24532`.`a` is true) AS `a IS TRUE`,if(ifnull(`table_24532`.`a`,0),1,0) AS `old_istrue`,(`table_24532`.`a` is not true) AS `a IS NOT TRUE`,if(ifnull(`table_24532`.`a`,0),0,1) AS `old_isnottrue`,(`table_24532`.`a` is false) AS `a IS FALSE`,if(ifnull(`table_24532`.`a`,1),0,1) AS `old_isfalse`,(`table_24532`.`a` is not false) AS `a IS NOT FALSE`,if(ifnull(`table_24532`.`a`,1),1,0) AS `old_isnotfalse` from `table_24532`
+insert into table_24532 values (0, 0, 0, 0);
+select * from view_24532_b;
+a IS TRUE old_istrue a IS NOT TRUE old_isnottrue a IS FALSE old_isfalse a IS NOT FALSE old_isnotfalse
+0 0 1 1 1 1 0 0
+update table_24532 set a=1;
+select * from view_24532_b;
+a IS TRUE old_istrue a IS NOT TRUE old_isnottrue a IS FALSE old_isfalse a IS NOT FALSE old_isnotfalse
+1 1 0 0 0 0 1 1
+update table_24532 set a=NULL;
+select * from view_24532_b;
+a IS TRUE old_istrue a IS NOT TRUE old_isnottrue a IS FALSE old_isfalse a IS NOT FALSE old_isnotfalse
+0 0 1 1 0 0 1 1
+drop view view_24532_a;
+drop view view_24532_b;
+drop table table_24532;
End of 5.0 tests.
DROP DATABASE IF EXISTS `d-1`;
CREATE DATABASE `d-1`;
diff --git a/mysql-test/std_data/init_file.dat b/mysql-test/std_data/init_file.dat
index 814e968eb31..cb8e0778438 100644
--- a/mysql-test/std_data/init_file.dat
+++ b/mysql-test/std_data/init_file.dat
@@ -27,3 +27,12 @@ insert into t2 values (11), (13);
drop procedure p1;
drop function f1;
drop view v1;
+
+#
+# Bug#23240 --init-file statements with NOW() reports '1970-01-01 11:00:00'as the date time
+#
+CREATE DATABASE IF NOT EXISTS init_file;
+CREATE TABLE IF NOT EXISTS init_file.startup ( startdate DATETIME );
+INSERT INTO init_file.startup VALUES ( NOW() );
+
+
diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def
index ab3892cd5ca..2d37ab0b2c5 100644
--- a/mysql-test/t/disabled.def
+++ b/mysql-test/t/disabled.def
@@ -11,7 +11,6 @@
##############################################################################
user_limits : Bug#23921 random failure of user_limits.test
-im_daemon_life_cycle : Bug#24415 see note: [19 Dec 23:17] Trudy Pelzer
im_options : Bug#20294 2006-07-24 stewart Instance manager test im_options fails randomly
concurrent_innodb : BUG#21579 2006-08-11 mleich innodb_concurrent random failures with varying differences
ndb_autodiscover : BUG#18952 2006-02-16 jmiller Needs to be fixed w.r.t binlog
diff --git a/mysql-test/t/events_bugs.test b/mysql-test/t/events_bugs.test
index 26abf663ce1..0790999d720 100644
--- a/mysql-test/t/events_bugs.test
+++ b/mysql-test/t/events_bugs.test
@@ -364,6 +364,63 @@ drop event e22830_4;
drop table t1;
drop table t2;
+
+#
+# BUG#16425: Events: no DEFINER clause
+#
+--error 0,ER_CANNOT_USER
+DROP USER mysqltest_u1@localhost;
+
+CREATE USER mysqltest_u1@localhost;
+GRANT EVENT ON events_test.* TO mysqltest_u1@localhost;
+
+CREATE EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1;
+SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS;
+DROP EVENT e1;
+
+CREATE DEFINER=CURRENT_USER EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1;
+SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS;
+ALTER DEFINER=mysqltest_u1@localhost EVENT e1 ON SCHEDULE EVERY 1 HOUR;
+SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS;
+DROP EVENT e1;
+
+CREATE DEFINER=CURRENT_USER() EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1;
+SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS;
+DROP EVENT e1;
+
+CREATE DEFINER=mysqltest_u1@localhost EVENT e1 ON SCHEDULE EVERY 1 DAY DO
+ SELECT 1;
+SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS;
+DROP EVENT e1;
+
+connect (conn1, localhost, mysqltest_u1, , events_test);
+
+CREATE EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1;
+SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS;
+DROP EVENT e1;
+
+CREATE DEFINER=CURRENT_USER EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1;
+SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS;
+--error ER_SPECIFIC_ACCESS_DENIED_ERROR
+ALTER DEFINER=root@localhost EVENT e1 ON SCHEDULE EVERY 1 HOUR;
+SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS;
+DROP EVENT e1;
+
+CREATE DEFINER=CURRENT_USER() EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1;
+SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS;
+DROP EVENT e1;
+
+--error ER_SPECIFIC_ACCESS_DENIED_ERROR
+CREATE DEFINER=root@localhost EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1;
+--error ER_EVENT_DOES_NOT_EXIST
+DROP EVENT e1;
+
+disconnect conn1;
+connection default;
+
+DROP USER mysqltest_u1@localhost;
+
+
#
# End of tests
#
diff --git a/mysql-test/t/func_if.test b/mysql-test/t/func_if.test
index beaa371f847..5373ca3fec6 100644
--- a/mysql-test/t/func_if.test
+++ b/mysql-test/t/func_if.test
@@ -97,3 +97,14 @@ create table t1 (f1 int, f2 int);
insert into t1 values(1,1),(0,0);
select f1, f2, if(f1, 40.0, 5.00) from t1 group by f1 order by f2;
drop table t1;
+
+#
+# Bug#24532 (The return data type of IS TRUE is different from similar
+# operations)
+#
+# IF(x, unsigned, unsigned) should be unsigned.
+#
+
+select if(0, 18446744073709551610, 18446744073709551610);
+
+
diff --git a/mysql-test/t/im_cmd_line.imtest b/mysql-test/t/im_cmd_line.imtest
index 8dd348471d0..1de43efe92b 100644
--- a/mysql-test/t/im_cmd_line.imtest
+++ b/mysql-test/t/im_cmd_line.imtest
@@ -26,7 +26,7 @@
--echo
--echo --> Printing out line for 'testuser'...
---exec $IM_EXE --defaults-file="$IM_DEFAULTS_PATH" --print-password-line --username=testuser --password=abc | tail -1
+--exec $IM_EXE --defaults-file="$IM_DEFAULTS_PATH" --print-password-line --username=testuser --password=abc | tail -2 | head -1
--echo
--echo --> Listing users...
@@ -45,7 +45,7 @@
--echo
--echo --> Printing out line for 'testuser'...
---exec $IM_EXE --defaults-file="$IM_DEFAULTS_PATH" --print-password-line --username=testuser --password=xyz | tail -1
+--exec $IM_EXE --defaults-file="$IM_DEFAULTS_PATH" --print-password-line --username=testuser --password=xyz | tail -2 | head -1
--echo
--echo --> Listing users...
diff --git a/mysql-test/t/im_daemon_life_cycle.imtest b/mysql-test/t/im_daemon_life_cycle.imtest
index c5b8ee16d9a..c2eac46c1e4 100644
--- a/mysql-test/t/im_daemon_life_cycle.imtest
+++ b/mysql-test/t/im_daemon_life_cycle.imtest
@@ -23,15 +23,20 @@
# - wait for IM-main to start accepting connections before continue test
# case;
#
+# NOTE: timeout is 55 seconds. Timeout should be more than shutdown-delay
+# specified for managed MySQL instance. Now shutdown-delay is 10 seconds
+# (set in mysql-test-run.pl). So, 55 seconds should be enough to make 5
+# attempts.
+#
###########################################################################
--exec $MYSQL_TEST_DIR/t/log.sh im_daemon_life_cycle Main-test: starting...
--exec $MYSQL_TEST_DIR/t/log.sh im_daemon_life_cycle Killing IM-main...
---exec $MYSQL_TEST_DIR/t/kill_n_check.sh $IM_PATH_PID restarted 30 im_daemon_life_cycle
+--exec $MYSQL_TEST_DIR/t/kill_n_check.sh $IM_PATH_PID restarted 55 im_daemon_life_cycle
--exec $MYSQL_TEST_DIR/t/log.sh im_daemon_life_cycle Waiting for IM-main to start accepting connections...
---exec $MYSQL_TEST_DIR/t/wait_for_socket.sh $EXE_MYSQL $IM_PATH_SOCK $IM_USERNAME $IM_PASSWORD '' 30 im_daemon_life_cycle
+--exec $MYSQL_TEST_DIR/t/wait_for_socket.sh $EXE_MYSQL $IM_PATH_SOCK $IM_USERNAME $IM_PASSWORD '' 55 im_daemon_life_cycle
--exec $MYSQL_TEST_DIR/t/log.sh im_daemon_life_cycle Main-test: done.
@@ -58,23 +63,23 @@
START INSTANCE mysqld2;
--exec $MYSQL_TEST_DIR/t/log.sh im_daemon_life_cycle mysqld2: waiting to start...
---exec $MYSQL_TEST_DIR/t/wait_for_process.sh $IM_MYSQLD2_PATH_PID 30 started im_daemon_life_cycle
+--exec $MYSQL_TEST_DIR/t/wait_for_process.sh $IM_MYSQLD2_PATH_PID 55 started im_daemon_life_cycle
--exec $MYSQL_TEST_DIR/t/log.sh im_daemon_life_cycle mysqld2: started.
# 2. Restart IM-main;
--exec $MYSQL_TEST_DIR/t/log.sh im_daemon_life_cycle Killing IM-main...
---exec $MYSQL_TEST_DIR/t/kill_n_check.sh $IM_PATH_PID restarted 30 im_daemon_life_cycle
+--exec $MYSQL_TEST_DIR/t/kill_n_check.sh $IM_PATH_PID restarted 55 im_daemon_life_cycle
--exec $MYSQL_TEST_DIR/t/log.sh im_daemon_life_cycle Waiting for IM-main to start accepting connections...
---exec $MYSQL_TEST_DIR/t/wait_for_socket.sh $EXE_MYSQL $IM_PATH_SOCK $IM_USERNAME $IM_PASSWORD '' 30 im_daemon_life_cycle
+--exec $MYSQL_TEST_DIR/t/wait_for_socket.sh $EXE_MYSQL $IM_PATH_SOCK $IM_USERNAME $IM_PASSWORD '' 55 im_daemon_life_cycle
# 3. Issue some statement -- connection should be re-established.
--exec $MYSQL_TEST_DIR/t/log.sh im_daemon_life_cycle Checking that IM-main processing commands...
---replace_column 3 VERSION_NUMBER 4 VERSION
+--replace_column 2 STATE 3 VERSION_NUMBER 4 VERSION
SHOW INSTANCE STATUS mysqld1;
# 4. Stop mysqld2, because it will not be stopped by IM, as it is nonguarded.
@@ -85,7 +90,7 @@ SHOW INSTANCE STATUS mysqld1;
STOP INSTANCE mysqld2;
--exec $MYSQL_TEST_DIR/t/log.sh im_daemon_life_cycle mysqld2: waiting to stop...
---exec $MYSQL_TEST_DIR/t/wait_for_process.sh $IM_MYSQLD2_PATH_PID 30 stopped im_daemon_life_cycle
+--exec $MYSQL_TEST_DIR/t/wait_for_process.sh $IM_MYSQLD2_PATH_PID 55 stopped im_daemon_life_cycle
--exec $MYSQL_TEST_DIR/t/log.sh im_daemon_life_cycle mysqld2: stopped.
###########################################################################
diff --git a/mysql-test/t/init_file.test b/mysql-test/t/init_file.test
index 31a6ef5a541..7c580afadda 100644
--- a/mysql-test/t/init_file.test
+++ b/mysql-test/t/init_file.test
@@ -6,6 +6,15 @@
# mysql-test/t/init_file-master.opt for the actual test
#
+#
+# Bug#23240 --init-file statements with NOW() reports '1970-01-01 11:00:00'as the date time
+#
+INSERT INTO init_file.startup VALUES ( NOW() );
+SELECT * INTO @X FROM init_file.startup limit 0,1;
+SELECT * INTO @Y FROM init_file.startup limit 1,1;
+SELECT YEAR(@X)-YEAR(@Y);
+DROP DATABASE init_file;
+
--echo ok
--echo end of 4.1 tests
#
diff --git a/mysql-test/t/kill_n_check.sh b/mysql-test/t/kill_n_check.sh
index 96c402a638c..6f2a0825dcd 100755
--- a/mysql-test/t/kill_n_check.sh
+++ b/mysql-test/t/kill_n_check.sh
@@ -53,7 +53,7 @@ pid_path="$1"
expected_result="$2"
total_timeout="$3"
test_id="$4"
-log_file="$MYSQLTEST_VARDIR/log/$test_id.log"
+log_file="$MYSQLTEST_VARDIR/log/$test_id.script.log"
log_debug "-- $basename: starting --"
log_debug "pid_path: '$pid_path'"
diff --git a/mysql-test/t/log.sh b/mysql-test/t/log.sh
index 29cf8d3e1a3..33ef6d6701f 100755
--- a/mysql-test/t/log.sh
+++ b/mysql-test/t/log.sh
@@ -17,7 +17,7 @@ if [ $# -lt 2 ]; then
fi
test_id="$1"
-log_file="$MYSQLTEST_VARDIR/log/$test_id.log"
+log_file="$MYSQLTEST_VARDIR/log/$test_id.script.log"
shift
diff --git a/mysql-test/t/ps_1general.test b/mysql-test/t/ps_1general.test
index a9d4488b1be..df3d1f0791c 100644
--- a/mysql-test/t/ps_1general.test
+++ b/mysql-test/t/ps_1general.test
@@ -321,14 +321,10 @@ prepare stmt4 from ' show status like ''Threads_running'' ';
execute stmt4;
prepare stmt4 from ' show variables like ''sql_mode'' ';
execute stmt4;
-# The output depends on the bdb being enabled and on the history
-# history (actions of the bdb engine).
-# That is the reason why, we switch the output here off.
-# (The real output will be tested in ps_6bdb.test)
---disable_result_log
+--error ER_UNKNOWN_STORAGE_ENGINE
prepare stmt4 from ' show engine bdb logs ';
-execute stmt4;
---enable_result_log
+--error ER_UNKNOWN_STORAGE_ENGINE
+prepare stmt4 from ' show engine foo logs ';
prepare stmt4 from ' show grants for user ';
prepare stmt4 from ' show create table t2 ';
prepare stmt4 from ' show master status ';
diff --git a/mysql-test/t/show_check.test b/mysql-test/t/show_check.test
index 938baec51d1..0626d9f8c44 100644
--- a/mysql-test/t/show_check.test
+++ b/mysql-test/t/show_check.test
@@ -563,4 +563,22 @@ show keys from `mysqlttest\1`.`a\b`;
drop table `mysqlttest\1`.`a\b`;
drop database `mysqlttest\1`;
+#
+# Bug#24392: SHOW ENGINE MUTEX STATUS is a synonym for SHOW INNODB STATUS
+#
+
+--error ER_UNKNOWN_STORAGE_ENGINE
+show engine foobar status;
+--error ER_UNKNOWN_STORAGE_ENGINE
+show engine foobar logs;
+--error ER_UNKNOWN_STORAGE_ENGINE
+show engine foobar mutex;
+
+--error ER_UNKNOWN_STORAGE_ENGINE
+show engine mutex status;
+
+show engine csv status;
+show engine csv logs;
+show engine csv mutex;
+
--echo End of 5.1 tests
diff --git a/mysql-test/t/sp-vars.test b/mysql-test/t/sp-vars.test
index 7cf92dc5d0d..0014dc1f6af 100644
--- a/mysql-test/t/sp-vars.test
+++ b/mysql-test/t/sp-vars.test
@@ -500,8 +500,6 @@ DROP PROCEDURE p1;
#
# Test case for BUG#12976: Boolean values reversed in stored procedures?
#
-# TODO: test case failed.
-#
###########################################################################
--echo
@@ -566,13 +564,8 @@ BEGIN
END|
delimiter ;|
-# The expected and correct result.
-
call p1();
-# The wrong result. Note that only hex(vb) works, but is printed with two
-# digits for some reason in this case.
-
call p2();
#
@@ -583,6 +576,64 @@ DROP TABLE t1;
DROP PROCEDURE p1;
DROP PROCEDURE p2;
+# Additional tests for Bug#12976
+
+--disable_warnings
+DROP TABLE IF EXISTS table_12976_a;
+DROP TABLE IF EXISTS table_12976_b;
+DROP PROCEDURE IF EXISTS proc_12976_a;
+DROP PROCEDURE IF EXISTS proc_12976_b;
+--enable_warnings
+
+CREATE TABLE table_12976_a (val bit(1));
+
+CREATE TABLE table_12976_b(
+ appname varchar(15),
+ emailperm bit not null default 1,
+ phoneperm bit not null default 0);
+
+insert into table_12976_b values ('A', b'1', b'1'), ('B', b'0', b'0');
+
+delimiter ||;
+CREATE PROCEDURE proc_12976_a()
+BEGIN
+ declare localvar bit(1);
+ SELECT val INTO localvar FROM table_12976_a;
+ SELECT coalesce(localvar, 1)+1, coalesce(val, 1)+1 FROM table_12976_a;
+END||
+
+CREATE PROCEDURE proc_12976_b(
+ name varchar(15),
+ out ep bit,
+ out msg varchar(10))
+BEGIN
+ SELECT emailperm into ep FROM table_12976_b where (appname = name);
+ IF ep is true THEN
+ SET msg = 'True';
+ ELSE
+ SET msg = 'False';
+ END IF;
+END||
+
+delimiter ;||
+
+INSERT table_12976_a VALUES (0);
+call proc_12976_a();
+UPDATE table_12976_a set val=1;
+call proc_12976_a();
+
+call proc_12976_b('A', @ep, @msg);
+select @ep, @msg;
+
+call proc_12976_b('B', @ep, @msg);
+select @ep, @msg;
+
+DROP TABLE table_12976_a;
+DROP TABLE table_12976_b;
+DROP PROCEDURE proc_12976_a;
+DROP PROCEDURE proc_12976_b;
+
+
###########################################################################
#
# Test case for BUG#9572: Stored procedures: variable type declarations
diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test
index 0ce3d1d78c6..74f79894595 100644
--- a/mysql-test/t/subselect.test
+++ b/mysql-test/t/subselect.test
@@ -2599,6 +2599,7 @@ SELECT * FROM t1
WHERE EXISTS (SELECT t2.c FROM t2 JOIN t3 ON t2.c=t3.c WHERE t2.c=1
UNION
SELECT c from t2 WHERE c=t1.c);
+
DROP TABLE t1,t2,t3;
#
@@ -2609,30 +2610,102 @@ INSERT INTO t1 VALUES ('a');
SELECT * FROM t1 WHERE _utf8'a' = ANY (SELECT s1 FROM t1);
DROP TABLE t1;
-#
-# Bug#23800: Outer fields in correlated subqueries is used in a temporary
-# table created for sorting.
-#
-CREATE TABLE t1(f1 int);
-CREATE TABLE t2(f2 int, f21 int, f3 timestamp);
-INSERT INTO t1 VALUES (1),(1),(2),(2);
-INSERT INTO t2 VALUES (1,1,"2004-02-29 11:11:11"), (2,2,"2004-02-29 11:11:11");
-SELECT ((SELECT f2 FROM t2 WHERE f21=f1 LIMIT 1) * COUNT(f1)) AS sq FROM t1 GROUP BY f1;
-SELECT (SELECT SUM(1) FROM t2 ttt GROUP BY t2.f3 LIMIT 1) AS tt FROM t2;
-PREPARE stmt1 FROM 'SELECT ((SELECT f2 FROM t2 WHERE f21=f1 LIMIT 1) * COUNT(f1)) AS sq FROM t1 GROUP BY f1';
-EXECUTE stmt1;
-EXECUTE stmt1;
-DEALLOCATE PREPARE stmt1;
-SELECT f2, AVG(f21),
- (SELECT t.f3 FROM t2 AS t WHERE t2.f2=t.f2 AND t.f3=MAX(t2.f3)) AS test
- FROM t2 GROUP BY f2;
-DROP TABLE t1,t2;
-CREATE TABLE t1 (a int, b INT, c CHAR(10) NOT NULL);
-INSERT INTO t1 VALUES
- (1,1,'a'), (1,2,'b'), (1,3,'c'), (1,4,'d'), (1,5,'e'),
- (2,1,'f'), (2,2,'g'), (2,3,'h'), (3,4,'i'), (3,3,'j'),
- (3,2,'k'), (3,1,'l'), (1,9,'m');
-SELECT a, MAX(b),
- (SELECT t.c FROM t1 AS t WHERE t1.a=t.a AND t.b=MAX(t1.b)) AS test
- FROM t1 GROUP BY a;
-DROP TABLE t1;
+#
+# Bug#21904 (parser problem when using IN with a double "(())")
+#
+
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+DROP TABLE IF EXISTS t2;
+DROP TABLE IF EXISTS t1xt2;
+--enable_warnings
+
+CREATE TABLE t1 (
+ id_1 int(5) NOT NULL,
+ t varchar(4) DEFAULT NULL
+);
+
+CREATE TABLE t2 (
+ id_2 int(5) NOT NULL,
+ t varchar(4) DEFAULT NULL
+);
+
+CREATE TABLE t1xt2 (
+ id_1 int(5) NOT NULL,
+ id_2 int(5) NOT NULL
+);
+
+INSERT INTO t1 VALUES (1, 'a'), (2, 'b'), (3, 'c'), (4, 'd');
+
+INSERT INTO t2 VALUES (2, 'bb'), (3, 'cc'), (4, 'dd'), (12, 'aa');
+
+INSERT INTO t1xt2 VALUES (2, 2), (3, 3), (4, 4);
+
+# subselect returns 0 rows
+
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 IN (SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1));
+
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 IN ((SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1)));
+
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 IN (((SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1))));
+
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 NOT IN (SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1));
+
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 NOT IN ((SELECT t1xt2.id_2 FROM t1xt2 where t1.id_1 = t1xt2.id_1)));
+
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 NOT IN (((SELECT t1xt2.id_2 FROM t1xt2 where t1.id_1 = t1xt2.id_1))));
+
+insert INTO t1xt2 VALUES (1, 12);
+
+# subselect returns 1 row
+
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 IN (SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1));
+
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 IN ((SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1)));
+
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 IN (((SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1))));
+
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 NOT IN (SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1));
+
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 NOT IN ((SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1)));
+
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 NOT IN (((SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1))));
+
+insert INTO t1xt2 VALUES (2, 12);
+
+# subselect returns more than 1 row
+
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 IN (SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1));
+
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 IN ((SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1)));
+
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 IN (((SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1))));
+
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 NOT IN (SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1));
+
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 NOT IN ((SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1)));
+
+SELECT DISTINCT t1.id_1 FROM t1 WHERE
+(12 NOT IN (((SELECT t1xt2.id_2 FROM t1xt2 WHERE t1.id_1 = t1xt2.id_1))));
+
+DROP TABLE t1;
+DROP TABLE t2;
+DROP TABLE t1xt2;
+
diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test
index 67415499a61..520babafb7e 100644
--- a/mysql-test/t/view.test
+++ b/mysql-test/t/view.test
@@ -748,12 +748,14 @@ drop view v1;
#
# VIEWs with national characters
#
-create table tü (cü char);
-create view vü as select cü from tü;
-insert into vü values ('ü');
-select * from vü;
-drop view vü;
-drop table tü;
+set names utf8;
+create table tü (cü char);
+create view vü as select cü from tü;
+insert into vü values ('ü');
+select * from vü;
+drop view vü;
+drop table tü;
+set names latin1;
#
# problem with used_tables() of outer reference resolved in VIEW
@@ -2962,19 +2964,19 @@ SELECT * FROM v;
DROP VIEW v;
-#
-# BUG#24293: '\Z' token is not handled correctly in views
-#
-
---disable_warnings
-DROP VIEW IF EXISTS v1;
---enable_warnings
-
-CREATE VIEW v1 AS SELECT 'The\ZEnd';
-SELECT * FROM v1;
-
-SHOW CREATE VIEW v1;
-
+#
+# BUG#24293: '\Z' token is not handled correctly in views
+#
+
+--disable_warnings
+DROP VIEW IF EXISTS v1;
+--enable_warnings
+
+CREATE VIEW v1 AS SELECT 'The\ZEnd';
+SELECT * FROM v1;
+
+SHOW CREATE VIEW v1;
+
DROP VIEW v1;
#
@@ -2982,7 +2984,7 @@ DROP VIEW v1;
#
CREATE TABLE t1 (mydate DATETIME);
-INSERT INTO t1 VALUES
+INSERT INTO t1 VALUES
('2007-01-01'), ('2007-01-02'), ('2007-01-30'), ('2007-01-31');
CREATE VIEW v1 AS SELECT mydate from t1;
@@ -3030,7 +3032,7 @@ drop view v1;
drop table t1;
#
-# Bug#26209: queries with GROUP BY and ORDER BY using views
+# Bug#26209: queries with GROUP BY and ORDER BY using views
#
CREATE TABLE t1 (
@@ -3049,6 +3051,110 @@ SELECT code, COUNT(DISTINCT country) FROM v1 GROUP BY code ORDER BY MAX(id);
DROP VIEW v1;
DROP TABLE t1;
+#
+# BUG#25897: Some queries are no longer possible after a CREATE VIEW
+# fails
+#
+--disable_warnings
+DROP VIEW IF EXISTS v1;
+--enable_warnings
+
+let $query = SELECT * FROM (SELECT 1) AS t;
+
+eval $query;
+--error ER_VIEW_SELECT_DERIVED
+eval CREATE VIEW v1 AS $query;
+--echo # Previously the following would fail.
+eval $query;
+
+#
+# Bug#24532: The return data type of IS TRUE is different from similar
+# operations
+#
+
+--disable_warnings
+drop view if exists view_24532_a;
+drop view if exists view_24532_b;
+drop table if exists table_24532;
+--enable_warnings
+
+create table table_24532 (
+ a int,
+ b bigint,
+ c int(4),
+ d bigint(48)
+);
+
+create view view_24532_a as
+select
+ a IS TRUE,
+ a IS NOT TRUE,
+ a IS FALSE,
+ a IS NOT FALSE,
+ a IS UNKNOWN,
+ a IS NOT UNKNOWN,
+ a is NULL,
+ a IS NOT NULL,
+ ISNULL(a),
+ b IS TRUE,
+ b IS NOT TRUE,
+ b IS FALSE,
+ b IS NOT FALSE,
+ b IS UNKNOWN,
+ b IS NOT UNKNOWN,
+ b is NULL,
+ b IS NOT NULL,
+ ISNULL(b),
+ c IS TRUE,
+ c IS NOT TRUE,
+ c IS FALSE,
+ c IS NOT FALSE,
+ c IS UNKNOWN,
+ c IS NOT UNKNOWN,
+ c is NULL,
+ c IS NOT NULL,
+ ISNULL(c),
+ d IS TRUE,
+ d IS NOT TRUE,
+ d IS FALSE,
+ d IS NOT FALSE,
+ d IS UNKNOWN,
+ d IS NOT UNKNOWN,
+ d is NULL,
+ d IS NOT NULL,
+ ISNULL(d)
+from table_24532;
+
+describe view_24532_a;
+
+create view view_24532_b as
+select
+ a IS TRUE,
+ if(ifnull(a, 0), 1, 0) as old_istrue,
+ a IS NOT TRUE,
+ if(ifnull(a, 0), 0, 1) as old_isnottrue,
+ a IS FALSE,
+ if(ifnull(a, 1), 0, 1) as old_isfalse,
+ a IS NOT FALSE,
+ if(ifnull(a, 1), 1, 0) as old_isnotfalse
+from table_24532;
+
+describe view_24532_b;
+
+show create view view_24532_b;
+
+insert into table_24532 values (0, 0, 0, 0);
+select * from view_24532_b;
+update table_24532 set a=1;
+select * from view_24532_b;
+update table_24532 set a=NULL;
+select * from view_24532_b;
+
+drop view view_24532_a;
+drop view view_24532_b;
+drop table table_24532;
+
+
--echo End of 5.0 tests.
#
diff --git a/mysql-test/t/wait_for_process.sh b/mysql-test/t/wait_for_process.sh
index 4c2d89cfea6..2143ab2002f 100755
--- a/mysql-test/t/wait_for_process.sh
+++ b/mysql-test/t/wait_for_process.sh
@@ -63,7 +63,7 @@ pid_path="$1"
total_attempts="$2"
event="$3"
test_id="$4"
-log_file="$MYSQLTEST_VARDIR/log/$test_id.log"
+log_file="$MYSQLTEST_VARDIR/log/$test_id.script.log"
log_debug "-- $basename: starting --"
log_debug "pid_path: '$pid_path'"
diff --git a/mysql-test/t/wait_for_socket.sh b/mysql-test/t/wait_for_socket.sh
index 1bce74dfd3a..8c17c8ac0ac 100755
--- a/mysql-test/t/wait_for_socket.sh
+++ b/mysql-test/t/wait_for_socket.sh
@@ -30,7 +30,7 @@ password="$4"
db="$5"
total_timeout="$6"
test_id="$7"
-log_file="$MYSQLTEST_VARDIR/log/$test_id.log"
+log_file="$MYSQLTEST_VARDIR/log/$test_id.script.log"
log_debug "-- $basename: starting --"
log_debug "client_exe: '$client_exe'"
diff --git a/mysys/my_wincond.c b/mysys/my_wincond.c
index ed8b715cb85..353b2fced4e 100644
--- a/mysys/my_wincond.c
+++ b/mysys/my_wincond.c
@@ -27,27 +27,48 @@
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr)
{
- cond->waiting=0;
- cond->semaphore=CreateSemaphore(NULL,0,0x7FFFFFFF,NullS);
- if (!cond->semaphore)
+ cond->waiting= 0;
+ InitializeCriticalSection(&cond->lock_waiting);
+
+ cond->events[SIGNAL]= CreateEvent(NULL, /* no security */
+ FALSE, /* auto-reset event */
+ FALSE, /* non-signaled initially */
+ NULL); /* unnamed */
+
+ /* Create a manual-reset event. */
+ cond->events[BROADCAST]= CreateEvent(NULL, /* no security */
+ TRUE, /* manual-reset */
+ FALSE, /* non-signaled initially */
+ NULL); /* unnamed */
+
+
+ cond->broadcast_block_event= CreateEvent(NULL, /* no security */
+ TRUE, /* manual-reset */
+ TRUE, /* signaled initially */
+ NULL); /* unnamed */
+
+ if( cond->events[SIGNAL] == NULL ||
+ cond->events[BROADCAST] == NULL ||
+ cond->broadcast_block_event == NULL )
return ENOMEM;
return 0;
}
int pthread_cond_destroy(pthread_cond_t *cond)
{
- return CloseHandle(cond->semaphore) ? 0 : EINVAL;
+ DeleteCriticalSection(&cond->lock_waiting);
+
+ if (CloseHandle(cond->events[SIGNAL]) == 0 ||
+ CloseHandle(cond->events[BROADCAST]) == 0 ||
+ CloseHandle(cond->broadcast_block_event) == 0)
+ return EINVAL;
+ return 0;
}
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
{
- InterlockedIncrement(&cond->waiting);
- LeaveCriticalSection(mutex);
- WaitForSingleObject(cond->semaphore,INFINITE);
- InterlockedDecrement(&cond->waiting);
- EnterCriticalSection(mutex);
- return 0 ;
+ return pthread_cond_timedwait(cond,mutex,NULL);
}
@@ -57,52 +78,104 @@ int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
int result;
long timeout;
union ft64 now;
-
- GetSystemTimeAsFileTime(&now.ft);
- /*
- Calculate time left to abstime
- - subtract start time from current time(values are in 100ns units)
- - convert to millisec by dividing with 10000
- */
- timeout= (long)((abstime->tv.i64 - now.i64) / 10000);
-
- /* Don't allow the timeout to be negative */
- if (timeout < 0)
- timeout= 0L;
+ if( abstime != NULL )
+ {
+ GetSystemTimeAsFileTime(&now.ft);
+
+ /*
+ Calculate time left to abstime
+ - subtract start time from current time(values are in 100ns units)
+ - convert to millisec by dividing with 10000
+ */
+ timeout= (long)((abstime->tv.i64 - now.i64) / 10000);
+
+ /* Don't allow the timeout to be negative */
+ if (timeout < 0)
+ timeout= 0L;
+
+ /*
+ Make sure the calucated timeout does not exceed original timeout
+ value which could cause "wait for ever" if system time changes
+ */
+ if (timeout > abstime->max_timeout_msec)
+ timeout= abstime->max_timeout_msec;
- /*
- Make sure the calucated timeout does not exceed original timeout
- value which could cause "wait for ever" if system time changes
+ }
+ else
+ {
+ /* No time specified; don't expire */
+ timeout= INFINITE;
+ }
+
+ /*
+ Block access if previous broadcast hasn't finished.
+ This is just for safety and should normally not
+ affect the total time spent in this function.
*/
- if (timeout > abstime->max_timeout_msec)
- timeout= abstime->max_timeout_msec;
+ WaitForSingleObject(cond->broadcast_block_event, INFINITE);
+
+ EnterCriticalSection(&cond->lock_waiting);
+ cond->waiting++;
+ LeaveCriticalSection(&cond->lock_waiting);
- InterlockedIncrement(&cond->waiting);
LeaveCriticalSection(mutex);
- result= WaitForSingleObject(cond->semaphore,timeout);
- InterlockedDecrement(&cond->waiting);
+
+ result= WaitForMultipleObjects(2, cond->events, FALSE, timeout);
+
+ EnterCriticalSection(&cond->lock_waiting);
+ cond->waiting--;
+
+ if (cond->waiting == 0 && result == (WAIT_OBJECT_0+BROADCAST))
+ {
+ /*
+ We're the last waiter to be notified or to stop waiting, so
+ reset the manual event.
+ */
+ /* Close broadcast gate */
+ ResetEvent(cond->events[BROADCAST]);
+ /* Open block gate */
+ SetEvent(cond->broadcast_block_event);
+ }
+ LeaveCriticalSection(&cond->lock_waiting);
+
EnterCriticalSection(mutex);
return result == WAIT_TIMEOUT ? ETIMEDOUT : 0;
}
-
int pthread_cond_signal(pthread_cond_t *cond)
{
- long prev_count;
- if (cond->waiting)
- ReleaseSemaphore(cond->semaphore,1,&prev_count);
+ EnterCriticalSection(&cond->lock_waiting);
+
+ if(cond->waiting > 0)
+ SetEvent(cond->events[SIGNAL]);
+
+ LeaveCriticalSection(&cond->lock_waiting);
+
return 0;
}
int pthread_cond_broadcast(pthread_cond_t *cond)
{
- long prev_count;
- if (cond->waiting)
- ReleaseSemaphore(cond->semaphore,cond->waiting,&prev_count);
- return 0 ;
+ EnterCriticalSection(&cond->lock_waiting);
+ /*
+ The mutex protect us from broadcasting if
+ there isn't any thread waiting to open the
+ block gate after this call has closed it.
+ */
+ if(cond->waiting > 0)
+ {
+ /* Close block gate */
+ ResetEvent(cond->broadcast_block_event);
+ /* Open broadcast gate */
+ SetEvent(cond->events[BROADCAST]);
+ }
+
+ LeaveCriticalSection(&cond->lock_waiting);
+
+ return 0;
}
diff --git a/server-tools/instance-manager/IMService.cpp b/server-tools/instance-manager/IMService.cpp
index 679a30ec4e4..feccaadbecc 100644
--- a/server-tools/instance-manager/IMService.cpp
+++ b/server-tools/instance-manager/IMService.cpp
@@ -15,17 +15,19 @@
#include <winsock2.h>
#include <signal.h>
-#include "log.h"
-#include "options.h"
+
#include "IMService.h"
+
+#include "log.h"
#include "manager.h"
+#include "options.h"
+
+static const char * const IM_SVC_USERNAME= NULL;
+static const char * const IM_SVC_PASSWORD= NULL;
IMService::IMService(void)
+ :WindowsService("MySqlManager", "MySQL Manager")
{
- serviceName= "MySqlManager";
- displayName= "MySQL Manager";
- username= NULL;
- password= NULL;
}
IMService::~IMService(void)
@@ -60,50 +62,63 @@ void IMService::Log(const char *msg)
log_info(msg);
}
-int HandleServiceOptions()
+int IMService::main()
{
- int ret_val= 0;
-
IMService winService;
if (Options::Service::install_as_service)
{
if (winService.IsInstalled())
+ {
log_info("Service is already installed.");
- else if (winService.Install())
+ return 1;
+ }
+
+ if (winService.Install(IM_SVC_USERNAME, IM_SVC_PASSWORD))
+ {
log_info("Service installed successfully.");
+ return 0;
+ }
else
{
log_error("Service failed to install.");
- ret_val= 1;
+ return 1;
}
}
- else if (Options::Service::remove_service)
+
+ if (Options::Service::remove_service)
{
- if (! winService.IsInstalled())
+ if (!winService.IsInstalled())
+ {
log_info("Service is not installed.");
- else if (winService.Remove())
+ return 1;
+ }
+
+ if (winService.Remove())
+ {
log_info("Service removed successfully.");
+ return 0;
+ }
else
{
log_error("Service failed to remove.");
- ret_val= 1;
+ return 1;
}
}
- else
+
+ log_info("Initializing Instance Manager service...");
+
+ if (!winService.Init())
{
- log_info("Initializing Instance Manager service...");
+ log_error("Service failed to initialize.");
- if (!winService.Init())
- {
- log_error("Service failed to initialize.");
- fprintf(stderr,
- "The service should be started by Windows Service Manager.\n"
- "The MySQL Manager should be started with '--standalone'\n"
- "to run from command line.");
- ret_val= 1;
- }
+ fprintf(stderr,
+ "The service should be started by Windows Service Manager.\n"
+ "The MySQL Manager should be started with '--standalone'\n"
+ "to run from command line.");
+
+ return 1;
}
- return ret_val;
+ return 0;
}
diff --git a/server-tools/instance-manager/IMService.h b/server-tools/instance-manager/IMService.h
index 52e36695028..aceafb2fca6 100644
--- a/server-tools/instance-manager/IMService.h
+++ b/server-tools/instance-manager/IMService.h
@@ -14,11 +14,14 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#pragma once
-#include "windowsservice.h"
+#include "WindowsService.h"
class IMService: public WindowsService
{
public:
+ static int main();
+
+private:
IMService(void);
~IMService(void);
@@ -27,5 +30,3 @@ protected:
void Stop();
void Run(DWORD argc, LPTSTR *argv);
};
-
-extern int HandleServiceOptions();
diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am
index 6a974bc992d..19c4ac8de19 100644
--- a/server-tools/instance-manager/Makefile.am
+++ b/server-tools/instance-manager/Makefile.am
@@ -80,7 +80,9 @@ mysqlmanager_SOURCES= command.cc command.h mysqlmanager.cc \
portability.h \
exit_codes.h \
user_management_commands.h \
- user_management_commands.cc
+ user_management_commands.cc \
+ angel.h \
+ angel.cc
mysqlmanager_LDADD= @CLIENT_EXTRA_LDFLAGS@ \
liboptions.la \
diff --git a/server-tools/instance-manager/WindowsService.cpp b/server-tools/instance-manager/WindowsService.cpp
index d36ed3a3f2f..7c5641167e4 100644
--- a/server-tools/instance-manager/WindowsService.cpp
+++ b/server-tools/instance-manager/WindowsService.cpp
@@ -14,19 +14,30 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#include <windows.h>
-#include <assert.h>
-#include ".\windowsservice.h"
+
+#include "my_global.h"
+#include "WindowsService.h"
static WindowsService *gService;
-WindowsService::WindowsService(void) :
+WindowsService::WindowsService(const char *p_serviceName,
+ const char *p_displayName) :
statusCheckpoint(0),
- serviceName(NULL),
+ serviceName(p_serviceName),
+ displayName(p_displayName),
inited(FALSE),
dwAcceptedControls(SERVICE_ACCEPT_STOP),
debugging(FALSE)
{
+ DBUG_ASSERT(serviceName != NULL);
+
+ /* TODO: shouldn't we check displayName too (can it really be NULL)? */
+
+ /* WindowsService is assumed to be singleton. Let's assure this. */
+ DBUG_ASSERT(gService == NULL);
+
gService= this;
+
status.dwServiceType= SERVICE_WIN32_OWN_PROCESS;
status.dwServiceSpecificExitCode= 0;
}
@@ -35,7 +46,7 @@ WindowsService::~WindowsService(void)
{
}
-BOOL WindowsService::Install()
+BOOL WindowsService::Install(const char *username, const char *password)
{
bool ret_val= FALSE;
SC_HANDLE newService;
@@ -70,7 +81,7 @@ BOOL WindowsService::Install()
BOOL WindowsService::Init()
{
- assert(serviceName != NULL);
+ DBUG_ASSERT(serviceName != NULL);
if (inited)
return TRUE;
@@ -207,7 +218,7 @@ void WindowsService::HandleControlCode(DWORD opcode)
void WINAPI WindowsService::ServiceMain(DWORD argc, LPTSTR *argv)
{
- assert(gService != NULL);
+ DBUG_ASSERT(gService != NULL);
// register our service control handler:
gService->RegisterAndRun(argc, argv);
@@ -215,7 +226,7 @@ void WINAPI WindowsService::ServiceMain(DWORD argc, LPTSTR *argv)
void WINAPI WindowsService::ControlHandler(DWORD opcode)
{
- assert(gService != NULL);
+ DBUG_ASSERT(gService != NULL);
return gService->HandleControlCode(opcode);
}
diff --git a/server-tools/instance-manager/WindowsService.h b/server-tools/instance-manager/WindowsService.h
index 033e02ecb7f..02a499e5f0c 100644
--- a/server-tools/instance-manager/WindowsService.h
+++ b/server-tools/instance-manager/WindowsService.h
@@ -21,8 +21,6 @@ protected:
bool inited;
const char *serviceName;
const char *displayName;
- const char *username;
- const char *password;
SERVICE_STATUS_HANDLE statusHandle;
DWORD statusCheckpoint;
SERVICE_STATUS status;
@@ -30,10 +28,10 @@ protected:
bool debugging;
public:
- WindowsService(void);
+ WindowsService(const char *p_serviceName, const char *p_displayName);
~WindowsService(void);
- BOOL Install();
+ BOOL Install(const char *username, const char *password);
BOOL Remove();
BOOL Init();
BOOL IsInstalled();
diff --git a/server-tools/instance-manager/angel.cc b/server-tools/instance-manager/angel.cc
new file mode 100644
index 00000000000..7c90f8915da
--- /dev/null
+++ b/server-tools/instance-manager/angel.cc
@@ -0,0 +1,406 @@
+/* Copyright (C) 2003-2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef __WIN__
+
+#include "angel.h"
+
+#include <sys/wait.h>
+/*
+ sys/wait.h is needed for waitpid(). Unfortunately, there is no MySQL
+ include file, that can serve for this. Include it before MySQL system
+ headers so that we can change system defines if needed.
+*/
+
+#include "my_global.h"
+#include "my_alarm.h"
+#include "my_dir.h"
+#include "my_sys.h"
+
+/* Include other IM files. */
+
+#include "log.h"
+#include "manager.h"
+#include "options.h"
+#include "priv.h"
+
+/************************************************************************/
+
+enum { CHILD_OK= 0, CHILD_NEED_RESPAWN, CHILD_EXIT_ANGEL };
+
+static int log_fd;
+
+static volatile sig_atomic_t child_status= CHILD_OK;
+static volatile sig_atomic_t child_exit_code= 0;
+static volatile sig_atomic_t shutdown_request_signo= 0;
+
+
+/************************************************************************/
+/**
+ Open log file.
+
+ @return
+ TRUE on error;
+ FALSE on success.
+*************************************************************************/
+
+static bool open_log_file()
+{
+ log_info("Angel: opening log file '%s'...",
+ (const char *) Options::Daemon::log_file_name);
+
+ log_fd= open(Options::Daemon::log_file_name,
+ O_WRONLY | O_CREAT | O_APPEND | O_NOCTTY,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+
+ if (log_fd < 0)
+ {
+ log_error("Can not open log file '%s': %s.",
+ (const char *) Options::Daemon::log_file_name,
+ (const char *) strerror(errno));
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/************************************************************************/
+/**
+ Detach the process from controlling tty.
+
+ @return
+ TRUE on error;
+ FALSE on success.
+*************************************************************************/
+
+static bool detach_process()
+{
+ /*
+ Become a session leader (the goal is not to have a controlling tty).
+
+ setsid() must succeed because child is guaranteed not to be a process
+ group leader (it belongs to the process group of the parent).
+
+ NOTE: if we now don't have a controlling tty we will not receive
+ tty-related signals - no need to ignore them.
+ */
+
+ if (setsid() < 0)
+ {
+ log_error("setsid() failed: %s.", (const char *) strerror(errno));
+ return -1;
+ }
+
+ /* Close STDIN. */
+
+ log_info("Angel: preparing standard streams.");
+
+ if (close(STDIN_FILENO) < 0)
+ {
+ log_error("Warning: can not close stdin (%s)."
+ "Trying to continue...",
+ (const char *) strerror(errno));
+ }
+
+ /* Dup STDOUT and STDERR to the log file. */
+
+ if (dup2(log_fd, STDOUT_FILENO) < 0 ||
+ dup2(log_fd, STDERR_FILENO) < 0)
+ {
+ log_error("Can not redirect stdout and stderr to the log file: %s.",
+ (const char *) strerror(errno));
+
+ return TRUE;
+ }
+
+ if (log_fd != STDOUT_FILENO && log_fd != STDERR_FILENO)
+ {
+ if (close(log_fd) < 0)
+ {
+ log_error("Can not close original log file handler (%d): %s. "
+ "Trying to continue...",
+ (int) log_fd,
+ (const char *) strerror(errno));
+ }
+ }
+
+ return FALSE;
+}
+
+
+/************************************************************************/
+/**
+ Create PID file.
+
+ @return
+ TRUE on error;
+ FALSE on success.
+*************************************************************************/
+
+static bool create_pid_file()
+{
+ if (create_pid_file(Options::Daemon::angel_pid_file_name, getpid()))
+ {
+ log_error("Angel: can not create pid file (%s).",
+ (const char *) Options::Daemon::angel_pid_file_name);
+
+ return TRUE;
+ }
+
+ log_info("Angel: pid file (%s) created.",
+ (const char *) Options::Daemon::angel_pid_file_name);
+
+ return FALSE;
+}
+
+
+/************************************************************************/
+/**
+ SIGCHLD handler.
+
+ Reap child, analyze child exit code, and set child_status
+ appropriately.
+*************************************************************************/
+
+void reap_child(int __attribute__((unused)) signo)
+{
+ /* NOTE: As we have only one child, no need to cycle waitpid(). */
+
+ int exit_code;
+
+ if (waitpid(0, &exit_code, WNOHANG) > 0)
+ {
+ child_exit_code= exit_code;
+ child_status= exit_code ? CHILD_NEED_RESPAWN : CHILD_EXIT_ANGEL;
+ }
+}
+
+
+/************************************************************************/
+/**
+ SIGTERM, SIGHUP, SIGINT handler.
+
+ Set termination status and return.
+*************************************************************************/
+
+void terminate(int signo)
+{
+ shutdown_request_signo= signo;
+}
+
+
+/************************************************************************/
+/**
+ Angel main loop.
+
+ @return
+ The function returns exit status for global main():
+ 0 -- program completed successfully;
+ !0 -- error occurred.
+*************************************************************************/
+
+static int angel_main_loop()
+{
+ /*
+ Install signal handlers.
+
+ NOTE: Although signal handlers are needed only for parent process
+ (IM-angel), we should install them before fork() in order to avoid race
+ condition (i.e. to be sure, that IM-angel will receive SIGCHLD in any
+ case).
+ */
+
+ sigset_t wait_for_signals_mask;
+
+ struct sigaction sa_chld;
+ struct sigaction sa_term;
+ struct sigaction sa_chld_orig;
+ struct sigaction sa_term_orig;
+ struct sigaction sa_int_orig;
+ struct sigaction sa_hup_orig;
+
+ log_info("Angel: setting necessary signal actions...");
+
+ sigemptyset(&wait_for_signals_mask);
+
+ sigemptyset(&sa_chld.sa_mask);
+ sa_chld.sa_handler= reap_child;
+ sa_chld.sa_flags= SA_NOCLDSTOP;
+
+ sigemptyset(&sa_term.sa_mask);
+ sa_term.sa_handler= terminate;
+ sa_term.sa_flags= 0;
+
+ /* NOTE: sigaction() fails only if arguments are wrong. */
+
+ sigaction(SIGCHLD, &sa_chld, &sa_chld_orig);
+ sigaction(SIGTERM, &sa_term, &sa_term_orig);
+ sigaction(SIGINT, &sa_term, &sa_int_orig);
+ sigaction(SIGHUP, &sa_term, &sa_hup_orig);
+
+ /* The main Angel loop. */
+
+ while (true)
+ {
+ /* Spawn a new Manager. */
+
+ log_info("Angel: forking Manager process...");
+
+ switch (fork()) {
+ case -1:
+ log_error("Angel: can not fork IM-main: %s.",
+ (const char *) strerror(errno));
+
+ return -1;
+
+ case 0:
+ /*
+ We are in child process, which will be IM-main:
+ - Restore default signal actions to let the IM-main work with
+ signals as he wishes;
+ - Call Manager::main();
+ */
+
+ log_info("Angel: Manager process created successfully.");
+
+ /* NOTE: sigaction() fails only if arguments are wrong. */
+
+ sigaction(SIGCHLD, &sa_chld_orig, NULL);
+ sigaction(SIGTERM, &sa_term_orig, NULL);
+ sigaction(SIGINT, &sa_int_orig, NULL);
+ sigaction(SIGHUP, &sa_hup_orig, NULL);
+
+ log_info("Angel: executing Manager...");
+
+ return Manager::main();
+ }
+
+ /* Wait for signals. */
+
+ log_info("Angel: waiting for signals...");
+
+ while (child_status == CHILD_OK && shutdown_request_signo == 0)
+ sigsuspend(&wait_for_signals_mask);
+
+ /* Exit if one of shutdown signals has been caught. */
+
+ if (shutdown_request_signo)
+ {
+ log_info("Angel: received shutdown signal (%d). Exiting...",
+ (int) shutdown_request_signo);
+
+ return 0;
+ }
+
+ /* Manager process died. Respawn it if it was a failure. */
+
+ if (child_status == CHILD_NEED_RESPAWN)
+ {
+ child_status= CHILD_OK;
+
+ log_error("Angel: Manager exited abnormally (exit code: %d).",
+ (int) child_exit_code);
+
+ log_info("Angel: sleeping 1 second...");
+
+ sleep(1); /* don't respawn too fast */
+
+ log_info("Angel: respawning Manager...");
+
+ continue;
+ }
+
+ /* Delete IM-angel PID file. */
+
+ my_delete(Options::Daemon::angel_pid_file_name, MYF(0));
+
+ /* IM-angel finished. */
+
+ log_info("Angel: Manager exited normally. Exiting...");
+
+ return 0;
+ }
+}
+
+
+/************************************************************************/
+/**
+ Angel main function.
+
+ @return
+ The function returns exit status for global main():
+ 0 -- program completed successfully;
+ !0 -- error occurred.
+*************************************************************************/
+
+int Angel::main()
+{
+ int ret_status;
+
+ log_info("Angel: started.");
+
+ /* Open log file. */
+
+ if (open_log_file())
+ return -1;
+
+ /* Fork a new process. */
+
+ log_info("Angel: daemonizing...");
+
+ switch (fork()) {
+ case -1:
+ /*
+ This is the main Instance Manager process, fork() failed.
+ Log an error and bail out with error code.
+ */
+
+ log_error("fork() failed: %s.", (const char *) strerror(errno));
+ return -1;
+
+ case 0:
+ /* We are in child process. Continue Angel::main() execution. */
+
+ break;
+
+ default:
+ /*
+ We are in the parent process. Return 0 so that parent exits
+ successfully.
+ */
+
+ log_info("Angel: exiting from the original process...");
+
+ return 0;
+ }
+
+ /* Detach child from controlling tty. */
+
+ if (detach_process())
+ return -1;
+
+ /* Create PID file. */
+
+ if (create_pid_file())
+ return -1;
+
+ /* Start Angel main loop. */
+
+ return angel_main_loop();
+}
+
+#endif // __WIN__
diff --git a/server-tools/instance-manager/angel.h b/server-tools/instance-manager/angel.h
new file mode 100644
index 00000000000..db21c250972
--- /dev/null
+++ b/server-tools/instance-manager/angel.h
@@ -0,0 +1,34 @@
+/* Copyright (C) 2003-2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef INCLUDES_MYSQL_ANGEL_H
+#define INCLUDES_MYSQL_ANGEL_H
+
+#ifndef __WIN__
+
+#if defined(__GNUC__) && defined(USE_PRAGMA_INTERFACE)
+#pragma interface
+#endif
+
+#include <my_global.h>
+
+class Angel
+{
+public:
+ static int main();
+};
+
+#endif // INCLUDES_MYSQL_ANGEL_H
+#endif // __WIN__
diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc
index 1be64ec4969..393aceadca9 100644
--- a/server-tools/instance-manager/commands.cc
+++ b/server-tools/instance-manager/commands.cc
@@ -210,8 +210,10 @@ int Show_instances::write_data(st_net *net)
int Flush_instances::execute(st_net *net, ulong connection_id)
{
- if (Manager::flush_instances())
- return ER_OUT_OF_RESOURCES;
+ int err_status= Manager::flush_instances();
+
+ if (err_status)
+ return err_status;
return net_send_ok(net, connection_id, NULL) ? ER_OUT_OF_RESOURCES : 0;
}
diff --git a/server-tools/instance-manager/listener.cc b/server-tools/instance-manager/listener.cc
index 43cc3f66c94..e68ba2fe8ce 100644
--- a/server-tools/instance-manager/listener.cc
+++ b/server-tools/instance-manager/listener.cc
@@ -177,10 +177,13 @@ void Listener::run()
return;
err:
+ log_error("Listener: failed to initialize. Initiate shutdown...");
+
// we have to close the ip sockets in case of error
for (i= 0; i < num_sockets; i++)
closesocket(sockets[i]);
+ thread_registry->set_error_status();
thread_registry->unregister_thread(&thread_info);
thread_registry->request_shutdown();
return;
diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc
index e126b407522..792461e41a9 100644
--- a/server-tools/instance-manager/manager.cc
+++ b/server-tools/instance-manager/manager.cc
@@ -29,6 +29,8 @@
#include "guardian.h"
#include "instance_map.h"
#include "listener.h"
+#include "mysql_manager_error.h"
+#include "mysqld_error.h"
#include "log.h"
#include "options.h"
#include "priv.h"
@@ -179,6 +181,56 @@ void Manager::stop_all_threads()
/* Stop all threads. */
p_thread_registry->deliver_shutdown();
+
+ /* Set error status in the thread registry. */
+ p_thread_registry->set_error_status();
+}
+
+
+/**
+ Initialize user map and load password file.
+
+ SYNOPSIS
+ init_user_map()
+
+ RETURN
+ FALSE on success
+ TRUE on failure
+*/
+
+bool Manager::init_user_map(User_map *user_map)
+{
+ int err_code;
+ const char *err_msg;
+
+ if (user_map->init())
+ {
+ log_error("Manager: can not initialize user list: out of memory.");
+ return TRUE;
+ }
+
+ err_code= user_map->load(Options::Main::password_file_name, &err_msg);
+
+ if (!err_code)
+ return FALSE;
+
+ if (err_code == ERR_PASSWORD_FILE_DOES_NOT_EXIST &&
+ Options::Main::mysqld_safe_compatible)
+ {
+ /*
+ The password file does not exist, but we are running in
+ mysqld_safe-compatible mode. Continue, but complain in log.
+ */
+
+ log_info("Warning: password file does not exist, "
+ "nobody will be able to connect to Instance Manager.");
+
+ return FALSE;
+ }
+
+ log_error("Manager: %s.", (const char *) err_msg);
+
+ return TRUE;
}
@@ -194,25 +246,25 @@ void Manager::stop_all_threads()
See also comments in mysqlmanager.cc to picture general Instance Manager
architecture.
- TODO: how about returning error status.
+ RETURNS
+ main() returns exit status (exit code).
*/
int Manager::main()
{
- int err_code;
- int rc= 1;
- const char *err_msg;
bool shutdown_complete= FALSE;
pid_t manager_pid= getpid();
+ log_info("Manager: initializing...");
+
#ifndef __WIN__
if (check_if_linux_threads(&linux_threads))
{
- log_error("Can not determine thread model.");
+ log_error("Manager: can not determine thread model.");
return 1;
}
- log_info("Detected threads model: %s.",
+ log_info("Manager: detected threads model: %s.",
(const char *) (linux_threads ? "LINUX threads" : "POSIX threads"));
#endif // __WIN__
@@ -250,47 +302,23 @@ int Manager::main()
if (instance_map.init())
{
- log_error("Can not initialize instance list: out of memory.");
+ log_error("Manager: can not initialize instance list: out of memory.");
return 1;
}
- /* Initialize user map and load password file. */
+ /* Initialize user db. */
- if (user_map.init())
- {
- log_error("Can not initialize user list: out of memory.");
- return 1;
- }
-
- if ((err_code= user_map.load(Options::Main::password_file_name, &err_msg)))
- {
- if (err_code == ERR_PASSWORD_FILE_DOES_NOT_EXIST &&
- Options::Main::mysqld_safe_compatible)
- {
- /*
- The password file does not exist, but we are running in
- mysqld_safe-compatible mode. Continue, but complain in log.
- */
-
- log_info("Warning: password file does not exist, "
- "nobody will be able to connect to Instance Manager.");
- }
- else
- {
- log_error("%s.", (const char *) err_msg);
- return 1;
- }
- }
+ if (init_user_map(&user_map))
+ return 1; /* logging has been already done. */
/* Write Instance Manager pid file. */
- log_info("IM pid file: '%s'; PID: %d.",
- (const char *) Options::Main::pid_file_name,
- (int) manager_pid);
-
if (create_pid_file(Options::Main::pid_file_name, manager_pid))
return 1; /* necessary logging has been already done. */
+ log_info("Manager: pid file (%s) created.",
+ (const char *) Options::Main::pid_file_name);
+
/*
Initialize signals and alarm-infrastructure.
@@ -326,7 +354,7 @@ int Manager::main()
if (guardian.start(Thread::DETACHED))
{
- log_error("Can not start Guardian thread.");
+ log_error("Manager: can not start Guardian thread.");
goto err;
}
@@ -334,7 +362,7 @@ int Manager::main()
if (Manager::flush_instances())
{
- log_error("Can not init instances repository.");
+ log_error("Manager: can not init instances repository.");
stop_all_threads();
goto err;
}
@@ -343,7 +371,7 @@ int Manager::main()
if (listener.start(Thread::DETACHED))
{
- log_error("Can not start Listener thread.");
+ log_error("Manager: can not start Listener thread.");
stop_all_threads();
goto err;
}
@@ -366,7 +394,7 @@ int Manager::main()
if ((status= my_sigwait(&mask, &signo)) != 0)
{
- log_error("sigwait() failed");
+ log_error("Manager: sigwait() failed");
stop_all_threads();
goto err;
}
@@ -417,8 +445,6 @@ int Manager::main()
log_info("Manager: finished.");
- rc= 0;
-
err:
/* delete the pid file */
my_delete(Options::Main::pid_file_name, MYF(0));
@@ -426,9 +452,9 @@ err:
#ifndef __WIN__
/* free alarm structures */
end_thr_alarm(1);
- /* don't pthread_exit to kill all threads who did not shut down in time */
#endif
- return rc;
+
+ return thread_registry.get_error_status() ? 1 : 0;
}
@@ -460,34 +486,41 @@ err:
In order to avoid such side effects one should never call
FLUSH INSTANCES without prior stop of all running instances.
+
+ RETURN
+ 0 On success
+ ER_OUT_OF_RESOURCES Not enough resources to complete the operation
+ ER_THERE_IS_ACTIVE_INSTACE If there is an active instance
*/
-bool Manager::flush_instances()
+int Manager::flush_instances()
{
p_instance_map->lock();
if (p_instance_map->is_there_active_instance())
{
p_instance_map->unlock();
- return TRUE;
+ return ER_THERE_IS_ACTIVE_INSTACE;
}
if (p_instance_map->reset())
{
p_instance_map->unlock();
- return TRUE;
+ return ER_OUT_OF_RESOURCES;
}
if (p_instance_map->load())
{
p_instance_map->unlock();
- return TRUE; /* Don't init guardian if we failed to load instances. */
+
+ /* Don't init guardian if we failed to load instances. */
+ return ER_OUT_OF_RESOURCES;
}
- get_guardian()->init(); /* TODO: check error status. */
+ get_guardian()->init();
get_guardian()->ping();
p_instance_map->unlock();
- return FALSE;
+ return 0;
}
diff --git a/server-tools/instance-manager/manager.h b/server-tools/instance-manager/manager.h
index 16322ddb71f..e6956884603 100644
--- a/server-tools/instance-manager/manager.h
+++ b/server-tools/instance-manager/manager.h
@@ -32,7 +32,7 @@ class Manager
public:
static int main();
- static bool flush_instances();
+ static int flush_instances();
public:
/**
@@ -51,6 +51,7 @@ public:
private:
static void stop_all_threads();
+ static bool init_user_map(User_map *user_map);
private:
static Guardian *p_guardian;
diff --git a/server-tools/instance-manager/mysqlmanager.cc b/server-tools/instance-manager/mysqlmanager.cc
index 225861037dd..75769af631a 100644
--- a/server-tools/instance-manager/mysqlmanager.cc
+++ b/server-tools/instance-manager/mysqlmanager.cc
@@ -14,143 +14,142 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <my_global.h>
+#include <my_dir.h>
#include <my_sys.h>
#include <string.h>
-#include <signal.h>
-#include <sys/types.h>
-#include <sys/stat.h>
#ifndef __WIN__
#include <pwd.h>
#include <grp.h>
-#include <sys/wait.h>
#endif
+#include "angel.h"
#include "log.h"
#include "manager.h"
#include "options.h"
-#include "priv.h"
#include "user_management_commands.h"
#ifdef __WIN__
#include "IMService.h"
-#include "WindowsService.h"
#endif
/*
- Few notes about Instance Manager architecture:
- Instance Manager consisits of two processes: the angel process, and the
- instance manager process. Responsibilities of the angel process is to
- monitor the instance manager process, and restart it in case of
- failure/shutdown. The angel process is started only if startup option
- '--run-as-service' is provided.
- The Instance Manager process consists of several
- subsystems (thread sets):
- - the signal handling thread: it's responsibilities are to handle
- user signals and propogate them to the other threads. All other threads
- are accounted in the signal handler thread Thread Registry.
- - the listener: listens all sockets. There is a listening
- socket for each (mysql, http, snmp, rendezvous (?)) subsystem.
- - mysql subsystem: Instance Manager acts like an ordinary MySQL Server,
- but with very restricted command set. Each MySQL client connection is
- handled in a separate thread. All MySQL client connections threads
- constitute mysql subsystem.
- - http subsystem: it is also possible to talk with Instance Manager via
- http. One thread per http connection is used. Threads are pooled.
- - 'snmp' connections (FIXME: I know nothing about it yet)
- - rendezvous threads
+ Instance Manager consists of two processes: the angel process (IM-angel),
+ and the manager process (IM-main). Responsibilities of IM-angel is to
+ monitor IM-main, and restart it in case of failure/shutdown. IM-angel is
+ started only if startup option '--run-as-service' is provided.
+
+ IM-main consists of several subsystems (thread sets):
+
+ - the signal handling thread
+
+ The signal thread handles user signals and propagates them to the
+ other threads. All other threads are accounted in the signal handler
+ thread Thread Registry.
+
+ - the listener
+
+ The listener listens to all sockets. There is a listening socket for
+ each subsystem (TCP/IP, UNIX socket).
+
+ - mysql subsystem
+
+ Instance Manager acts like an ordinary MySQL Server, but with very
+ restricted command set. Each MySQL client connection is handled in a
+ separate thread. All MySQL client connections threads constitute
+ mysql subsystem.
*/
-static void init_environment(char *progname);
+static int main_impl(int argc, char *argv[]);
#ifndef __WIN__
-static void daemonize(const char *log_file_name);
-static void angel();
-static struct passwd *check_user(const char *user);
-static int set_user(const char *user, struct passwd *user_info);
+static struct passwd *check_user();
+static bool switch_user();
#endif
-/*
- main, entry point
- - init environment
- - handle options
- - daemonize and run angel process (if necessary)
- - run manager process
-*/
+/************************************************************************/
+/**
+ The entry point.
+*************************************************************************/
int main(int argc, char *argv[])
{
- int return_value= 1;
- init_environment(argv[0]);
+ int return_value;
- if ((return_value= Options::load(argc, argv)))
- goto main_end;
+ /* Initialize. */
- if (Options::User_management::cmd)
- {
- return_value= Options::User_management::cmd->execute();
+ MY_INIT(argv[0]);
+ log_init();
+ umask(0117);
+ srand((unsigned int) time(0));
- goto main_end;
- }
+ /* Main function. */
-#ifndef __WIN__
+ log_info("IM: started.");
- struct passwd *user_info;
+ return_value= main_impl(argc, argv);
- if ((user_info= check_user(Options::Daemon::user)))
- {
- if (set_user(Options::Daemon::user, user_info))
- {
- return_value= 1;
- goto main_end;
- }
- }
+ log_info("IM: finished.");
- if (Options::Daemon::run_as_service)
- {
- /* forks, and returns only in child */
- daemonize(Options::Daemon::log_file_name);
- /* forks again, and returns only in child: parent becomes angel */
- angel();
- }
+ /* Cleanup. */
- (void) Manager::main(); /* ignore the return value for now */
+ Options::cleanup();
+ my_end(0);
-#else
+ return return_value;
+}
- if (!Options::Service::stand_alone)
- {
- if (HandleServiceOptions())
- {
- return_value= 1;
- goto main_end;
- }
- }
- else
- {
- (void) Manager::main(); /* ignore the return value for now */
- }
-#endif
+/************************************************************************/
+/**
+ Instance Manager main functionality.
+*************************************************************************/
- return_value= 0;
+int main_impl(int argc, char *argv[])
+{
+ int rc;
-main_end:
- Options::cleanup();
- my_end(0);
- return return_value;
+ if ((rc= Options::load(argc, argv)))
+ return rc;
+
+ if (Options::User_management::cmd)
+ return Options::User_management::cmd->execute();
+
+#ifndef __WIN__
+
+ if (switch_user())
+ return 1;
+
+ return Options::Daemon::run_as_service ?
+ Angel::main() :
+ Manager::main();
+
+#else
+
+ return Options::Service::stand_alone ?
+ Manager::main() :
+ IMService::main();
+
+#endif
}
-/******************* Auxilary functions implementation **********************/
+/**************************************************************************
+ OS-specific functions implementation.
+**************************************************************************/
#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
-/* Change to run as another user if started with --user */
-static struct passwd *check_user(const char *user)
+/************************************************************************/
+/**
+ Change to run as another user if started with --user.
+*************************************************************************/
+
+static struct passwd *check_user()
{
+ const char *user= Options::Daemon::user;
struct passwd *user_info;
uid_t user_id= geteuid();
@@ -195,200 +194,36 @@ err:
return NULL;
}
-static int set_user(const char *user, struct passwd *user_info)
+
+/************************************************************************/
+/**
+ Switch user.
+*************************************************************************/
+
+static bool switch_user()
{
- DBUG_ASSERT(user_info);
+ struct passwd *user_info= check_user();
+
+ if (!user_info)
+ return FALSE;
+
#ifdef HAVE_INITGROUPS
- initgroups((char*) user,user_info->pw_gid);
+ initgroups(Options::Daemon::user, user_info->pw_gid);
#endif
+
if (setgid(user_info->pw_gid) == -1)
{
log_error("setgid() failed");
- return 1;
+ return TRUE;
}
+
if (setuid(user_info->pw_uid) == -1)
{
log_error("setuid() failed");
- return 1;
- }
- return 0;
-}
-#endif
-
-
-/*
- Init environment, common for daemon and non-daemon
-*/
-
-static void init_environment(char *progname)
-{
- MY_INIT(progname);
- log_init();
- umask(0117);
- srand((uint) time(0));
-}
-
-
-#ifndef __WIN__
-/*
- Become a UNIX service
- SYNOPSIS
- daemonize()
-*/
-
-static void daemonize(const char *log_file_name)
-{
- pid_t pid= fork();
- switch (pid) {
- case -1: // parent, fork error
- die("daemonize(): fork failed, %s", strerror(errno));
- case 0: // child, fork ok
- int fd;
- /*
- Become a session leader: setsid must succeed because child is
- guaranteed not to be a process group leader (it belongs to the
- process group of the parent.)
- The goal is not to have a controlling terminal.
- */
- setsid();
- /*
- As we now don't have a controlling terminal we will not receive
- tty-related signals - no need to ignore them.
- */
-
- close(STDIN_FILENO);
-
- fd= open(log_file_name, O_WRONLY | O_CREAT | O_APPEND | O_NOCTTY,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
- if (fd < 0)
- die("daemonize(): failed to open log file %s, %s", log_file_name,
- strerror(errno));
- dup2(fd, STDOUT_FILENO);
- dup2(fd, STDERR_FILENO);
- if (fd != STDOUT_FILENO && fd != STDERR_FILENO)
- close(fd);
-
- /* TODO: chroot() and/or chdir() here */
- break;
- default:
- /* successfully exit from parent */
- exit(0);
- }
-}
-
-
-enum { CHILD_OK= 0, CHILD_NEED_RESPAWN, CHILD_EXIT_ANGEL };
-
-static volatile sig_atomic_t child_status= CHILD_OK;
-
-/*
- Signal handler for SIGCHLD: reap child, analyze child exit status, and set
- child_status appropriately.
-*/
-
-void reap_child(int __attribute__((unused)) signo)
-{
- int child_exit_status;
- /* As we have only one child, no need to cycle waitpid */
- if (waitpid(0, &child_exit_status, WNOHANG) > 0)
- {
- if (WIFSIGNALED(child_exit_status))
- child_status= CHILD_NEED_RESPAWN;
- else
- /*
- As reap_child is not called for SIGSTOP, we should be here only
- if the child exited normally.
- */
- child_status= CHILD_EXIT_ANGEL;
+ return TRUE;
}
-}
-
-static volatile sig_atomic_t is_terminated= 0;
-/*
- Signal handler for terminate signals - SIGTERM, SIGHUP, SIGINT.
- Set termination status and return.
- (q) do we need to handle SIGQUIT?
-*/
-
-void terminate(int signo)
-{
- is_terminated= signo;
+ return FALSE;
}
-
-/*
- Fork a child and monitor it.
- User can explicitly kill the angel process with SIGTERM/SIGHUP/SIGINT.
- Angel process will exit silently if mysqlmanager exits normally.
-*/
-
-static void angel()
-{
- /* install signal handlers */
- sigset_t zeromask; // to sigsuspend in parent
- struct sigaction sa_chld, sa_term;
- struct sigaction sa_chld_out, sa_term_out, sa_int_out, sa_hup_out;
-
- sigemptyset(&zeromask);
- sigemptyset(&sa_chld.sa_mask);
- sigemptyset(&sa_term.sa_mask);
-
- sa_chld.sa_handler= reap_child;
- sa_chld.sa_flags= SA_NOCLDSTOP;
- sa_term.sa_handler= terminate;
- sa_term.sa_flags= 0;
-
- /* sigaction can fail only on wrong arguments */
- sigaction(SIGCHLD, &sa_chld, &sa_chld_out);
- sigaction(SIGTERM, &sa_term, &sa_term_out);
- sigaction(SIGINT, &sa_term, &sa_int_out);
- sigaction(SIGHUP, &sa_term, &sa_hup_out);
-
- /* spawn a child */
-spawn:
- pid_t pid= fork();
- switch (pid) {
- case -1:
- die("angel(): fork failed, %s", strerror(errno));
- case 0: // child, success
- /*
- restore default actions for signals to let the manager work with
- signals as he wishes
- */
- sigaction(SIGCHLD, &sa_chld_out, 0);
- sigaction(SIGTERM, &sa_term_out, 0);
- sigaction(SIGINT, &sa_int_out, 0);
- sigaction(SIGHUP, &sa_hup_out, 0);
- /* Here we return to main, and fall into manager */
- break;
- default: // parent, success
- pid= getpid(); /* Get our pid. */
-
- log_info("Angel pid file: '%s'; PID: %d.",
- (const char *) Options::Daemon::angel_pid_file_name,
- (int) pid);
-
- create_pid_file(Options::Daemon::angel_pid_file_name, pid);
-
- while (child_status == CHILD_OK && is_terminated == 0)
- sigsuspend(&zeromask);
-
- if (is_terminated)
- log_info("angel got signal %d, exiting", is_terminated);
- else if (child_status == CHILD_NEED_RESPAWN)
- {
- child_status= CHILD_OK;
- log_error("angel(): mysqlmanager exited abnormally: respawning...");
- sleep(1); /* don't respawn too fast */
- goto spawn;
- }
- /*
- mysqlmanager successfully exited, let's silently evaporate
- If we return to main we will fall into the manager functionality,
- so let's simply exit().
- */
- exit(0);
- }
-}
#endif
diff --git a/server-tools/instance-manager/priv.cc b/server-tools/instance-manager/priv.cc
index 7c63b30cbf9..74263934924 100644
--- a/server-tools/instance-manager/priv.cc
+++ b/server-tools/instance-manager/priv.cc
@@ -47,7 +47,7 @@ unsigned long open_files_limit;
-int create_pid_file(const char *pid_file_name, int pid)
+bool create_pid_file(const char *pid_file_name, int pid)
{
FILE *pid_file;
@@ -58,7 +58,7 @@ int create_pid_file(const char *pid_file_name, int pid)
(const char *) pid_file_name,
(const char *) strerror(errno),
(int) errno);
- return 1;
+ return TRUE;
}
if (fprintf(pid_file, "%d\n", (int) pid) <= 0)
@@ -67,10 +67,10 @@ int create_pid_file(const char *pid_file_name, int pid)
(const char *) pid_file_name,
(const char *) strerror(errno),
(int) errno);
- return 1;
+ return TRUE;
}
my_fclose(pid_file, MYF(0));
- return 0;
+ return FALSE;
}
diff --git a/server-tools/instance-manager/priv.h b/server-tools/instance-manager/priv.h
index f8ccf130d91..5bf47e1e234 100644
--- a/server-tools/instance-manager/priv.h
+++ b/server-tools/instance-manager/priv.h
@@ -94,6 +94,6 @@ extern unsigned long bytes_sent, bytes_received;
extern unsigned long mysqld_net_retry_count;
extern unsigned long open_files_limit;
-int create_pid_file(const char *pid_file_name, int pid);
+bool create_pid_file(const char *pid_file_name, int pid);
#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_PRIV_H
diff --git a/server-tools/instance-manager/thread_registry.cc b/server-tools/instance-manager/thread_registry.cc
index bdbdb9caf88..f3a67c5e127 100644
--- a/server-tools/instance-manager/thread_registry.cc
+++ b/server-tools/instance-manager/thread_registry.cc
@@ -52,6 +52,7 @@ void Thread_info::init(bool send_signal_on_shutdown_arg)
Thread_registry::Thread_registry() :
shutdown_in_progress(FALSE)
,sigwait_thread_pid(pthread_self())
+ ,error_status(FALSE)
{
pthread_mutex_init(&LOCK_thread_registry, 0);
pthread_cond_init(&COND_thread_registry_is_empty, 0);
@@ -391,3 +392,23 @@ bool Thread::join()
return pthread_join(id, NULL) != 0;
}
+
+
+int Thread_registry::get_error_status()
+{
+ int ret_error_status;
+
+ pthread_mutex_lock(&LOCK_thread_registry);
+ ret_error_status= error_status;
+ pthread_mutex_unlock(&LOCK_thread_registry);
+
+ return ret_error_status;
+}
+
+
+void Thread_registry::set_error_status()
+{
+ pthread_mutex_lock(&LOCK_thread_registry);
+ error_status= TRUE;
+ pthread_mutex_unlock(&LOCK_thread_registry);
+}
diff --git a/server-tools/instance-manager/thread_registry.h b/server-tools/instance-manager/thread_registry.h
index 17028f56fdb..d04c8442e44 100644
--- a/server-tools/instance-manager/thread_registry.h
+++ b/server-tools/instance-manager/thread_registry.h
@@ -143,6 +143,8 @@ public:
pthread_mutex_t *mutex);
int cond_timedwait(Thread_info *info, pthread_cond_t *cond,
pthread_mutex_t *mutex, struct timespec *wait_time);
+ int get_error_status();
+ void set_error_status();
private:
void interrupt_threads();
@@ -154,6 +156,7 @@ private:
pthread_mutex_t LOCK_thread_registry;
pthread_cond_t COND_thread_registry_is_empty;
pthread_t sigwait_thread_pid;
+ bool error_status;
private:
Thread_registry(const Thread_registry &);
diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc
index 07575a6d33a..2cd5b371850 100644
--- a/sql/event_data_objects.cc
+++ b/sql/event_data_objects.cc
@@ -20,6 +20,8 @@
#include "event_db_repository.h"
#include "sp_head.h"
+/* That's a provisional solution */
+extern Event_db_repository events_event_db_repository;
#define EVEX_MAX_INTERVAL_VALUE 1000000000L
@@ -30,6 +32,47 @@ event_change_security_context(THD *thd, LEX_STRING user, LEX_STRING host,
static void
event_restore_security_context(THD *thd, Security_context *backup);
+
+/*
+ Initiliazes dbname and name of an Event_queue_element_for_exec
+ object
+
+ SYNOPSIS
+ Event_queue_element_for_exec::init()
+
+ RETURN VALUE
+ FALSE OK
+ TRUE Error (OOM)
+*/
+
+bool
+Event_queue_element_for_exec::init(LEX_STRING db, LEX_STRING n)
+{
+ if (!(dbname.str= my_strndup(db.str, dbname.length= db.length, MYF(MY_WME))))
+ return TRUE;
+ if (!(name.str= my_strndup(n.str, name.length= n.length, MYF(MY_WME))))
+ {
+ my_free((gptr) dbname.str, MYF(0));
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/*
+ Destructor
+
+ SYNOPSIS
+ Event_queue_element_for_exec::~Event_queue_element_for_exec()
+*/
+
+Event_queue_element_for_exec::~Event_queue_element_for_exec()
+{
+ my_free((gptr) dbname.str, MYF(0));
+ my_free((gptr) name.str, MYF(0));
+}
+
+
/*
Returns a new instance
@@ -568,16 +611,18 @@ Event_parse_data::check_parse_data(THD *thd)
void
Event_parse_data::init_definer(THD *thd)
{
- int definer_user_len;
- int definer_host_len;
DBUG_ENTER("Event_parse_data::init_definer");
- DBUG_PRINT("info",("init definer_user thd->mem_root: 0x%lx "
- "thd->sec_ctx->priv_user: 0x%lx", (long) thd->mem_root,
- (long) thd->security_ctx->priv_user));
+ DBUG_ASSERT(thd->lex->definer);
+
+ const char *definer_user= thd->lex->definer->user.str;
+ const char *definer_host= thd->lex->definer->host.str;
+ int definer_user_len= thd->lex->definer->user.length;
+ int definer_host_len= thd->lex->definer->host.length;
- definer_user_len= strlen(thd->security_ctx->priv_user);
- definer_host_len= strlen(thd->security_ctx->priv_host);
+ DBUG_PRINT("info",("init definer_user thd->mem_root: 0x%lx "
+ "definer_user: 0x%lx", (long) thd->mem_root,
+ (long) definer_user));
/* + 1 for @ */
DBUG_PRINT("info",("init definer as whole"));
@@ -585,12 +630,11 @@ Event_parse_data::init_definer(THD *thd)
definer.str= thd->alloc(definer.length + 1);
DBUG_PRINT("info",("copy the user"));
- memcpy(definer.str, thd->security_ctx->priv_user, definer_user_len);
+ memcpy(definer.str, definer_user, definer_user_len);
definer.str[definer_user_len]= '@';
DBUG_PRINT("info",("copy the host"));
- memcpy(definer.str + definer_user_len + 1, thd->security_ctx->priv_host,
- definer_host_len);
+ memcpy(definer.str + definer_user_len + 1, definer_host, definer_host_len);
definer.str[definer.length]= '\0';
DBUG_PRINT("info",("definer [%s] initted", definer.str));
@@ -743,7 +787,7 @@ Event_timed::~Event_timed()
*/
Event_job_data::Event_job_data()
- :thd(NULL), sphead(NULL), sql_mode(0)
+ :sphead(NULL), sql_mode(0)
{
}
@@ -1239,6 +1283,7 @@ Event_queue_element::compute_next_execution_time()
DBUG_PRINT("info", ("Dropped: %d", dropped));
status= Event_queue_element::DISABLED;
status_changed= TRUE;
+ dropped= TRUE;
goto ret;
}
@@ -1447,32 +1492,6 @@ Event_queue_element::mark_last_executed(THD *thd)
/*
- Drops the event
-
- SYNOPSIS
- Event_queue_element::drop()
- thd thread context
-
- RETURN VALUE
- 0 OK
- -1 Cannot open mysql.event
- -2 Cannot find the event in mysql.event (already deleted?)
-
- others return code from SE in case deletion of the event row
- failed.
-*/
-
-int
-Event_queue_element::drop(THD *thd)
-{
- DBUG_ENTER("Event_queue_element::drop");
-
- DBUG_RETURN(Events::get_instance()->
- drop_event(thd, dbname, name, FALSE, TRUE));
-}
-
-
-/*
Saves status and last_executed_at to the disk if changed.
SYNOPSIS
@@ -1503,13 +1522,13 @@ Event_queue_element::update_timing_fields(THD *thd)
thd->reset_n_backup_open_tables_state(&backup);
- if (Events::get_instance()->open_event_table(thd, TL_WRITE, &table))
+ if (events_event_db_repository.open_event_table(thd, TL_WRITE, &table))
{
ret= TRUE;
goto done;
}
fields= table->field;
- if ((ret= Events::get_instance()->db_repository->
+ if ((ret= events_event_db_repository.
find_named_event(thd, dbname, name, table)))
goto done;
@@ -1792,16 +1811,21 @@ Event_job_data::compile(THD *thd, MEM_ROOT *mem_root)
DBUG_PRINT("error", ("error during compile or thd->is_fatal_error: %d",
thd->is_fatal_error));
/*
- Free lex associated resources
- QQ: Do we really need all this stuff here?
+ The first thing we do after parse error is freeing sp_head to
+ ensure that we have restored original memroot.
*/
+ if (lex.sphead)
+ {
+ /* Clean up after failed stored procedure/function */
+ delete lex.sphead;
+ lex.sphead= NULL;
+ }
+ lex.unit.cleanup();
+
sql_print_error("SCHEDULER: Error during compilation of %s.%s or "
"thd->is_fatal_error: %d",
dbname.str, name.str, thd->is_fatal_error);
- lex.unit.cleanup();
- delete lex.sphead;
- sphead= lex.sphead= NULL;
ret= EVEX_COMPILE_ERROR;
goto done;
}
diff --git a/sql/event_data_objects.h b/sql/event_data_objects.h
index e00b0b94eaf..4346b0eb5b8 100644
--- a/sql/event_data_objects.h
+++ b/sql/event_data_objects.h
@@ -27,6 +27,27 @@ class sp_head;
class Sql_alloc;
+class Event_queue_element_for_exec
+{
+public:
+ Event_queue_element_for_exec(){};
+ ~Event_queue_element_for_exec();
+
+ bool
+ init(LEX_STRING dbname, LEX_STRING name);
+
+ LEX_STRING dbname;
+ LEX_STRING name;
+ bool dropped;
+ THD *thd;
+
+private:
+ /* Prevent use of these */
+ Event_queue_element_for_exec(const Event_queue_element_for_exec &);
+ void operator=(Event_queue_element_for_exec &);
+};
+
+
class Event_basic
{
protected:
@@ -96,9 +117,6 @@ public:
bool
compute_next_execution_time();
- int
- drop(THD *thd);
-
void
mark_last_executed(THD *thd);
@@ -160,7 +178,6 @@ public:
class Event_job_data : public Event_basic
{
public:
- THD *thd;
sp_head *sphead;
LEX_STRING body;
diff --git a/sql/event_queue.cc b/sql/event_queue.cc
index 068abbe3408..7b06e8b2cf5 100644
--- a/sql/event_queue.cc
+++ b/sql/event_queue.cc
@@ -16,7 +16,6 @@
#include "mysql_priv.h"
#include "event_queue.h"
#include "event_data_objects.h"
-#include "event_db_repository.h"
#define EVENT_QUEUE_INITIAL_SIZE 30
@@ -136,14 +135,12 @@ Event_queue::deinit_mutexes()
*/
bool
-Event_queue::init_queue(THD *thd, Event_db_repository *db_repo)
+Event_queue::init_queue(THD *thd)
{
- bool res;
DBUG_ENTER("Event_queue::init_queue");
DBUG_PRINT("enter", ("this: 0x%lx", (long) this));
LOCK_QUEUE_DATA();
- db_repository= db_repo;
if (init_queue_ex(&queue, EVENT_QUEUE_INITIAL_SIZE , 0 /*offset*/,
0 /*max_on_top*/, event_queue_element_compare_q,
@@ -160,12 +157,8 @@ Event_queue::init_queue(THD *thd, Event_db_repository *db_repo)
goto err;
}
- res= load_events_from_db(thd);
UNLOCK_QUEUE_DATA();
- if (res)
- deinit_queue();
-
- DBUG_RETURN(res);
+ DBUG_RETURN(FALSE);
err:
UNLOCK_QUEUE_DATA();
@@ -202,37 +195,29 @@ Event_queue::deinit_queue()
Event_queue::create_event()
dbname The schema of the new event
name The name of the new event
-
- RETURN VALUE
- OP_OK OK or scheduler not working
- OP_LOAD_ERROR Error during loading from disk
*/
-int
-Event_queue::create_event(THD *thd, LEX_STRING dbname, LEX_STRING name)
+void
+Event_queue::create_event(THD *thd, Event_queue_element *new_element)
{
- int res;
- Event_queue_element *new_element;
DBUG_ENTER("Event_queue::create_event");
- DBUG_PRINT("enter", ("thd: 0x%lx et=%s.%s", (long) thd, dbname.str, name.str));
+ DBUG_PRINT("enter", ("thd=0x%lx et=%s.%s",thd,
+ new_element->dbname.str, new_element->name.str));
- new_element= new Event_queue_element();
- res= db_repository->load_named_event(thd, dbname, name, new_element);
- if (res || new_element->status == Event_queue_element::DISABLED)
+ if (new_element->status == Event_queue_element::DISABLED)
delete new_element;
else
{
new_element->compute_next_execution_time();
+ DBUG_PRINT("info", ("new event in the queue 0x%lx", new_element));
LOCK_QUEUE_DATA();
- DBUG_PRINT("info", ("new event in the queue: 0x%lx", (long) new_element));
queue_insert_safe(&queue, (byte *) new_element);
dbug_dump_queue(thd->query_start());
pthread_cond_broadcast(&COND_queue_state);
UNLOCK_QUEUE_DATA();
}
-
- DBUG_RETURN(res);
+ DBUG_VOID_RETURN;
}
@@ -246,32 +231,16 @@ Event_queue::create_event(THD *thd, LEX_STRING dbname, LEX_STRING name)
name Name of the event
new_schema New schema, in case of RENAME TO, otherwise NULL
new_name New name, in case of RENAME TO, otherwise NULL
-
- RETURN VALUE
- OP_OK OK or scheduler not working
- OP_LOAD_ERROR Error during loading from disk
*/
-int
+void
Event_queue::update_event(THD *thd, LEX_STRING dbname, LEX_STRING name,
- LEX_STRING *new_schema, LEX_STRING *new_name)
+ Event_queue_element *new_element)
{
- int res;
- Event_queue_element *new_element;
-
DBUG_ENTER("Event_queue::update_event");
DBUG_PRINT("enter", ("thd: 0x%lx et=[%s.%s]", (long) thd, dbname.str, name.str));
- new_element= new Event_queue_element();
-
- res= db_repository->load_named_event(thd, new_schema ? *new_schema:dbname,
- new_name ? *new_name:name, new_element);
- if (res)
- {
- delete new_element;
- goto end;
- }
- else if (new_element->status == Event_queue_element::DISABLED)
+ if (new_element->status == Event_queue_element::DISABLED)
{
DBUG_PRINT("info", ("The event is disabled."));
/*
@@ -298,9 +267,7 @@ Event_queue::update_event(THD *thd, LEX_STRING dbname, LEX_STRING name,
dbug_dump_queue(thd->query_start());
UNLOCK_QUEUE_DATA();
-end:
- DBUG_PRINT("info", ("res=%d", res));
- DBUG_RETURN(res);
+ DBUG_VOID_RETURN;
}
@@ -452,133 +419,6 @@ Event_queue::find_n_remove_event(LEX_STRING db, LEX_STRING name)
/*
- Loads all ENABLED events from mysql.event into the prioritized
- queue. Called during scheduler main thread initialization. Compiles
- the events. Creates Event_queue_element instances for every ENABLED event
- from mysql.event.
-
- SYNOPSIS
- Event_queue::load_events_from_db()
- thd - Thread context. Used for memory allocation in some cases.
-
- RETURN VALUE
- 0 OK
- !0 Error (EVEX_OPEN_TABLE_FAILED, EVEX_MICROSECOND_UNSUP,
- EVEX_COMPILE_ERROR) - in all these cases mysql.event was
- tampered.
-
- NOTES
- Reports the error to the console
-*/
-
-int
-Event_queue::load_events_from_db(THD *thd)
-{
- TABLE *table;
- READ_RECORD read_record_info;
- int ret= -1;
- uint count= 0;
- bool clean_the_queue= TRUE;
-
- DBUG_ENTER("Event_queue::load_events_from_db");
- DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd));
-
- if ((ret= db_repository->open_event_table(thd, TL_READ, &table)))
- {
- sql_print_error("SCHEDULER: Table mysql.event is damaged. Can not open");
- DBUG_RETURN(EVEX_OPEN_TABLE_FAILED);
- }
-
- init_read_record(&read_record_info, thd, table ,NULL,1,0);
- while (!(read_record_info.read_record(&read_record_info)))
- {
- Event_queue_element *et;
- if (!(et= new Event_queue_element))
- {
- DBUG_PRINT("info", ("Out of memory"));
- break;
- }
- DBUG_PRINT("info", ("Loading event from row."));
-
- if ((ret= et->load_from_row(table)))
- {
- sql_print_error("SCHEDULER: Error while loading from mysql.event. "
- "Table probably corrupted");
- break;
- }
- if (et->status != Event_queue_element::ENABLED)
- {
- DBUG_PRINT("info",("%s is disabled",et->name.str));
- delete et;
- continue;
- }
-
- /* let's find when to be executed */
- if (et->compute_next_execution_time())
- {
- sql_print_error("SCHEDULER: Error while computing execution time of %s.%s."
- " Skipping", et->dbname.str, et->name.str);
- continue;
- }
-
- {
- Event_job_data temp_job_data;
- DBUG_PRINT("info", ("Event %s loaded from row. ", et->name.str));
-
- temp_job_data.load_from_row(table);
-
- /*
- We load only on scheduler root just to check whether the body
- compiles.
- */
- switch (ret= temp_job_data.compile(thd, thd->mem_root)) {
- case EVEX_MICROSECOND_UNSUP:
- sql_print_error("SCHEDULER: mysql.event is tampered. MICROSECOND is not "
- "supported but found in mysql.event");
- break;
- case EVEX_COMPILE_ERROR:
- sql_print_error("SCHEDULER: Error while compiling %s.%s. Aborting load",
- et->dbname.str, et->name.str);
- break;
- default:
- break;
- }
- thd->end_statement();
- thd->cleanup_after_query();
- }
- if (ret)
- {
- delete et;
- goto end;
- }
-
- queue_insert_safe(&queue, (byte *) et);
- count++;
- }
- clean_the_queue= FALSE;
-end:
- end_read_record(&read_record_info);
-
- if (clean_the_queue)
- {
- empty_queue();
- ret= -1;
- }
- else
- {
- ret= 0;
- sql_print_information("SCHEDULER: Loaded %d event%s", count,
- (count == 1)?"":"s");
- }
-
- close_thread_tables(thd);
-
- DBUG_PRINT("info", ("Status code %d. Loaded %d event(s)", ret, count));
- DBUG_RETURN(ret);
-}
-
-
-/*
Recalculates activation times in the queue. There is one reason for
that. Because the values (execute_at) by which the queue is ordered are
changed by calls to compute_next_execution_time() on a request from the
@@ -627,7 +467,7 @@ Event_queue::empty_queue()
{
uint i;
DBUG_ENTER("Event_queue::empty_queue");
- DBUG_PRINT("enter", ("Purging the queue. %d element(s)", queue.elements));
+ DBUG_PRINT("enter", ("Purging the queue. %u element(s)", queue.elements));
sql_print_information("SCHEDULER: Purging queue. %u events", queue.elements);
/* empty the queue */
for (i= 0; i < queue.elements; ++i)
@@ -688,31 +528,27 @@ static const char *queue_wait_msg= "Waiting for next activation";
SYNOPSIS
Event_queue::get_top_for_execution_if_time()
- thd [in] Thread
- job_data [out] The object to execute
+ thd [in] Thread
+ event_name [out] The object to execute
RETURN VALUE
- FALSE No error. If *job_data==NULL then top not elligible for execution.
- Could be that there is no top.
- TRUE Error
-
+ FALSE No error. event_name != NULL
+ TRUE Serious error
*/
bool
-Event_queue::get_top_for_execution_if_time(THD *thd, Event_job_data **job_data)
+Event_queue::get_top_for_execution_if_time(THD *thd,
+ Event_queue_element_for_exec **event_name)
{
bool ret= FALSE;
struct timespec top_time;
- Event_queue_element *top= NULL;
- bool to_free= FALSE;
- bool to_drop= FALSE;
- *job_data= NULL;
+ *event_name= NULL;
DBUG_ENTER("Event_queue::get_top_for_execution_if_time");
LOCK_QUEUE_DATA();
for (;;)
{
- int res;
+ Event_queue_element *top= NULL;
/* Break loop if thd has been killed */
if (thd->killed)
@@ -751,39 +587,30 @@ Event_queue::get_top_for_execution_if_time(THD *thd, Event_job_data **job_data)
continue;
}
- DBUG_PRINT("info", ("Ready for execution"));
- if (!(*job_data= new Event_job_data()))
- {
- ret= TRUE;
- break;
- }
- if ((res= db_repository->load_named_event(thd, top->dbname, top->name,
- *job_data)))
+ if (!(*event_name= new Event_queue_element_for_exec()) ||
+ (*event_name)->init(top->dbname, top->name))
{
- DBUG_PRINT("error", ("Got %d from load_named_event", res));
- delete *job_data;
- *job_data= NULL;
ret= TRUE;
break;
}
+ DBUG_PRINT("info", ("Ready for execution"));
top->mark_last_executed(thd);
if (top->compute_next_execution_time())
top->status= Event_queue_element::DISABLED;
DBUG_PRINT("info", ("event %s status is %d", top->name.str, top->status));
- (*job_data)->execution_count= top->execution_count;
+ top->execution_count++;
+ (*event_name)->dropped= top->dropped;
top->update_timing_fields(thd);
- if (((top->execute_at.year && !top->expression) || top->execute_at_null) ||
- (top->status == Event_queue_element::DISABLED))
+ if (top->status == Event_queue_element::DISABLED)
{
DBUG_PRINT("info", ("removing from the queue"));
sql_print_information("SCHEDULER: Last execution of %s.%s. %s",
top->dbname.str, top->name.str,
top->dropped? "Dropping.":"");
- to_free= TRUE;
- to_drop= top->dropped;
+ delete top;
queue_remove(&queue, 0);
}
else
@@ -794,19 +621,13 @@ Event_queue::get_top_for_execution_if_time(THD *thd, Event_job_data **job_data)
}
end:
UNLOCK_QUEUE_DATA();
- if (to_drop)
- {
- DBUG_PRINT("info", ("Dropping from disk"));
- top->drop(thd);
- }
- if (to_free)
- delete top;
- DBUG_PRINT("info", ("returning %d et_new: 0x%lx ", ret, (long) *job_data));
+ DBUG_PRINT("info", ("returning %d et_new: 0x%lx ",
+ ret, (long) *event_name));
- if (*job_data)
- DBUG_PRINT("info", ("db: %s name: %s definer=%s", (*job_data)->dbname.str,
- (*job_data)->name.str, (*job_data)->definer.str));
+ if (*event_name)
+ DBUG_PRINT("info", ("db: %s name: %s",
+ (*event_name)->dbname.str, (*event_name)->name.str));
DBUG_RETURN(ret);
}
diff --git a/sql/event_queue.h b/sql/event_queue.h
index 9f48da4914f..a1237e1b52c 100644
--- a/sql/event_queue.h
+++ b/sql/event_queue.h
@@ -16,12 +16,10 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
class Event_basic;
-class Event_db_repository;
-class Event_job_data;
class Event_queue_element;
+class Event_queue_element_for_exec;
class THD;
-class Event_scheduler;
class Event_queue
{
@@ -35,19 +33,19 @@ public:
deinit_mutexes();
bool
- init_queue(THD *thd, Event_db_repository *db_repo);
+ init_queue(THD *thd);
void
deinit_queue();
/* Methods for queue management follow */
- int
- create_event(THD *thd, LEX_STRING dbname, LEX_STRING name);
+ void
+ create_event(THD *thd, Event_queue_element *new_element);
- int
+ void
update_event(THD *thd, LEX_STRING dbname, LEX_STRING name,
- LEX_STRING *new_schema, LEX_STRING *new_name);
+ Event_queue_element *new_element);
void
drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name);
@@ -59,14 +57,15 @@ public:
recalculate_activation_times(THD *thd);
bool
- get_top_for_execution_if_time(THD *thd, Event_job_data **job_data);
+ get_top_for_execution_if_time(THD *thd,
+ Event_queue_element_for_exec **event_name);
+
void
dump_internal_status();
- int
- load_events_from_db(THD *thd);
-
+ void
+ empty_queue();
protected:
void
find_n_remove_event(LEX_STRING db, LEX_STRING name);
@@ -76,8 +75,6 @@ protected:
drop_matching_events(THD *thd, LEX_STRING pattern,
bool (*)(LEX_STRING, Event_basic *));
- void
- empty_queue();
void
dbug_dump_queue(time_t now);
@@ -86,11 +83,7 @@ protected:
pthread_mutex_t LOCK_event_queue;
pthread_cond_t COND_queue_state;
- Event_db_repository *db_repository;
-
- Event_scheduler *scheduler;
-
- /* The sorted queue with the Event_job_data objects */
+ /* The sorted queue with the Event_queue_element objects */
QUEUE queue;
TIME next_activation_at;
diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc
index aeacaef0b6d..accf2ad03be 100644
--- a/sql/event_scheduler.cc
+++ b/sql/event_scheduler.cc
@@ -18,6 +18,7 @@
#include "event_data_objects.h"
#include "event_scheduler.h"
#include "event_queue.h"
+#include "event_db_repository.h"
#ifdef __GNUC__
#if __GNUC__ >= 2
@@ -34,6 +35,11 @@
extern pthread_attr_t connection_attrib;
+
+Event_db_repository *Event_worker_thread::db_repository;
+Events *Event_worker_thread::events_facade;
+
+
static
const LEX_STRING scheduler_states_names[] =
{
@@ -60,8 +66,8 @@ struct scheduler_param {
et The event itself
*/
-static void
-evex_print_warnings(THD *thd, Event_job_data *et)
+void
+Event_worker_thread::print_warnings(THD *thd, Event_job_data *et)
{
MYSQL_ERROR *err;
DBUG_ENTER("evex_print_warnings");
@@ -104,22 +110,25 @@ evex_print_warnings(THD *thd, Event_job_data *et)
SYNOPSIS
post_init_event_thread()
thd Thread
-
- NOTES
- Before this is called, one should not do any DBUG_XXX() calls.
-
*/
bool
post_init_event_thread(THD *thd)
{
- (void) init_new_connection_handler_thread();
+ my_thread_init();
+ pthread_detach_this_thread();
+ thd->real_id= pthread_self();
if (init_thr_lock() || thd->store_globals())
{
thd->cleanup();
return TRUE;
}
+#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
+ sigset_t set;
+ VOID(sigemptyset(&set)); // Get mask in use
+ VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
+#endif
pthread_mutex_lock(&LOCK_thread_count);
threads.append(thd);
thread_count++;
@@ -184,7 +193,7 @@ pre_init_event_thread(THD* thd)
thd->options|= OPTION_AUTO_IS_NULL;
thd->client_capabilities|= CLIENT_MULTI_RESULTS;
pthread_mutex_lock(&LOCK_thread_count);
- thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
+ thd->thread_id= thread_id++;
pthread_mutex_unlock(&LOCK_thread_count);
/*
@@ -215,20 +224,20 @@ pthread_handler_t
event_scheduler_thread(void *arg)
{
/* needs to be first for thread_stack */
- THD *thd= (THD *) ((struct scheduler_param *) arg)->thd;
+ THD *thd= (THD *)((struct scheduler_param *) arg)->thd;
Event_scheduler *scheduler= ((struct scheduler_param *) arg)->scheduler;
- bool res;
- thd->thread_stack= (char*) &thd; // remember where our stack is
- res= post_init_event_thread(thd);
+ my_free((char*)arg, MYF(0));
+
+ thd->thread_stack= (char *)&thd; // remember where our stack is
DBUG_ENTER("event_scheduler_thread");
- my_free((char*)arg, MYF(0));
- if (!res)
+
+ if (!post_init_event_thread(thd))
scheduler->run(thd);
deinit_event_thread(thd);
- pthread_exit(0);
+
DBUG_RETURN(0); // Against gcc warnings
}
@@ -250,51 +259,99 @@ event_worker_thread(void *arg)
{
/* needs to be first for thread_stack */
THD *thd;
- Event_job_data *event= (Event_job_data *)arg;
- int ret;
- bool res;
+ Event_queue_element_for_exec *event= (Event_queue_element_for_exec *)arg;
thd= event->thd;
thd->thread_stack= (char *) &thd; // remember where our stack is
- res= post_init_event_thread(thd);
- DBUG_ENTER("event_worker_thread");
- if (!res)
+ Event_worker_thread worker_thread;
+ worker_thread.run(thd, (Event_queue_element_for_exec *)arg);
+
+ deinit_event_thread(thd);
+
+ return 0; // Can't return anything here
+}
+
+
+/*
+ Function that executes an event in a child thread. Setups the
+ environment for the event execution and cleans after that.
+
+ SYNOPSIS
+ Event_worker_thread::run()
+ thd Thread context
+ event The Event_queue_element_for_exec object to be processed
+*/
+
+void
+Event_worker_thread::run(THD *thd, Event_queue_element_for_exec *event)
+{
+ int ret;
+ Event_job_data *job_data= NULL;
+ DBUG_ENTER("Event_worker_thread::run");
+ DBUG_PRINT("info", ("Baikonur, time is %d, BURAN reporting and operational."
+ "THD=0x%lx", time(NULL), thd));
+
+ if (post_init_event_thread(thd))
+ goto end;
+
+ if (!(job_data= new Event_job_data()))
+ goto end;
+ else if ((ret= db_repository->
+ load_named_event(thd, event->dbname, event->name, job_data)))
{
- DBUG_PRINT("info", ("Baikonur, time is %ld, BURAN reporting and operational."
- "THD: 0x%lx",
- (long) time(NULL), (long) thd));
-
- sql_print_information("SCHEDULER: [%s.%s of %s] executing in thread %lu. "
- "Execution %u",
- event->dbname.str, event->name.str,
- event->definer.str, thd->thread_id,
- event->execution_count);
-
- thd->enable_slow_log= TRUE;
-
- ret= event->execute(thd);
-
- evex_print_warnings(thd, event);
-
- sql_print_information("SCHEDULER: [%s.%s of %s] executed in thread %lu. "
- "RetCode=%d", event->dbname.str, event->name.str,
- event->definer.str, thd->thread_id, ret);
- if (ret == EVEX_COMPILE_ERROR)
- sql_print_information("SCHEDULER: COMPILE ERROR for event %s.%s of %s",
- event->dbname.str, event->name.str,
- event->definer.str);
- else if (ret == EVEX_MICROSECOND_UNSUP)
- sql_print_information("SCHEDULER: MICROSECOND is not supported");
+ DBUG_PRINT("error", ("Got %d from load_named_event", ret));
+ goto end;
+ }
+
+ sql_print_information("SCHEDULER: [%s.%s of %s] executing in thread %lu. ",
+ job_data->dbname.str, job_data->name.str,
+ job_data->definer.str, thd->thread_id);
+
+ thd->enable_slow_log= TRUE;
+
+ ret= job_data->execute(thd);
+
+ print_warnings(thd, job_data);
+
+ sql_print_information("SCHEDULER: [%s.%s of %s] executed in thread %lu. "
+ "RetCode=%d", job_data->dbname.str, job_data->name.str,
+ job_data->definer.str, thd->thread_id, ret);
+ if (ret == EVEX_COMPILE_ERROR)
+ sql_print_information("SCHEDULER: COMPILE ERROR for event %s.%s of %s",
+ job_data->dbname.str, job_data->name.str,
+ job_data->definer.str);
+ else if (ret == EVEX_MICROSECOND_UNSUP)
+ sql_print_information("SCHEDULER: MICROSECOND is not supported");
+
+end:
+ delete job_data;
+
+ if (event->dropped)
+ {
+ sql_print_information("SCHEDULER: Dropping %s.%s", event->dbname.str,
+ event->name.str);
+ /*
+ Using db_repository can lead to a race condition because we access
+ the table without holding LOCK_metadata.
+ Scenario:
+ 1. CREATE EVENT xyz AT ... (conn thread)
+ 2. execute xyz (worker)
+ 3. CREATE EVENT XYZ EVERY ... (conn thread)
+ 4. drop xyz (worker)
+ 5. XYZ was just created on disk but `drop xyz` of the worker dropped it.
+ A consequent load to create Event_queue_element will fail.
+
+ If all operations are performed under LOCK_metadata there is no such
+ problem. However, this comes at the price of introduction bi-directional
+ association between class Events and class Event_worker_thread.
+ */
+ events_facade->drop_event(thd, event->dbname, event->name, FALSE);
}
DBUG_PRINT("info", ("BURAN %s.%s is landing!", event->dbname.str,
event->name.str));
- delete event;
-
- deinit_event_thread(thd);
- pthread_exit(0);
- DBUG_RETURN(0); // Can't return anything here
+ delete event;
}
@@ -440,7 +497,6 @@ bool
Event_scheduler::run(THD *thd)
{
int res= FALSE;
- Event_job_data *job_data;
DBUG_ENTER("Event_scheduler::run");
sql_print_information("SCHEDULER: Manager thread started with id %lu",
@@ -453,18 +509,20 @@ Event_scheduler::run(THD *thd)
while (is_running())
{
+ Event_queue_element_for_exec *event_name;
+
/* Gets a minimized version */
- if (queue->get_top_for_execution_if_time(thd, &job_data))
+ if (queue->get_top_for_execution_if_time(thd, &event_name))
{
sql_print_information("SCHEDULER: Serious error during getting next "
"event to execute. Stopping");
break;
}
- DBUG_PRINT("info", ("get_top returned job_data: 0x%lx", (long) job_data));
- if (job_data)
+ DBUG_PRINT("info", ("get_top returned job_data=0x%lx", event_name));
+ if (event_name)
{
- if ((res= execute_top(thd, job_data)))
+ if ((res= execute_top(thd, event_name)))
break;
}
else
@@ -498,7 +556,7 @@ Event_scheduler::run(THD *thd)
*/
bool
-Event_scheduler::execute_top(THD *thd, Event_job_data *job_data)
+Event_scheduler::execute_top(THD *thd, Event_queue_element_for_exec *event_name)
{
THD *new_thd;
pthread_t th;
@@ -509,13 +567,13 @@ Event_scheduler::execute_top(THD *thd, Event_job_data *job_data)
pre_init_event_thread(new_thd);
new_thd->system_thread= SYSTEM_THREAD_EVENT_WORKER;
- job_data->thd= new_thd;
+ event_name->thd= new_thd;
DBUG_PRINT("info", ("BURAN %s@%s ready for start t-3..2..1..0..ignition",
- job_data->dbname.str, job_data->name.str));
+ event_name->dbname.str, event_name->name.str));
/* Major failure */
if ((res= pthread_create(&th, &connection_attrib, event_worker_thread,
- job_data)))
+ event_name)))
goto error;
++started_events;
@@ -536,7 +594,7 @@ error:
delete new_thd;
pthread_mutex_unlock(&LOCK_thread_count);
}
- delete job_data;
+ delete event_name;
DBUG_RETURN(TRUE);
}
diff --git a/sql/event_scheduler.h b/sql/event_scheduler.h
index 18625ef35f3..2ab21464057 100644
--- a/sql/event_scheduler.h
+++ b/sql/event_scheduler.h
@@ -15,8 +15,11 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
class Event_queue;
class Event_job_data;
+class Event_db_repository;
+class Events;
void
pre_init_event_thread(THD* thd);
@@ -27,6 +30,29 @@ post_init_event_thread(THD* thd);
void
deinit_event_thread(THD *thd);
+
+class Event_worker_thread
+{
+public:
+ static void
+ init(Events *events, Event_db_repository *db_repo)
+ {
+ db_repository= db_repo;
+ events_facade= events;
+ }
+
+ void
+ run(THD *thd, Event_queue_element_for_exec *event);
+
+private:
+ void
+ print_warnings(THD *thd, Event_job_data *et);
+
+ static Event_db_repository *db_repository;
+ static Events *events_facade;
+};
+
+
class Event_scheduler
{
public:
@@ -71,10 +97,9 @@ private:
uint
workers_count();
-
/* helper functions */
bool
- execute_top(THD *thd, Event_job_data *job_data);
+ execute_top(THD *thd, Event_queue_element_for_exec *event_name);
/* helper functions for working with mutexes & conditionals */
void
diff --git a/sql/events.cc b/sql/events.cc
index e6224915d6b..425e288dfb7 100644
--- a/sql/events.cc
+++ b/sql/events.cc
@@ -97,7 +97,7 @@ Event_queue events_event_queue;
static
Event_scheduler events_event_scheduler;
-static
+
Event_db_repository events_event_db_repository;
Events Events::singleton;
@@ -296,29 +296,6 @@ Events::Events()
/*
- Opens mysql.event table with specified lock
-
- SYNOPSIS
- Events::open_event_table()
- thd Thread context
- lock_type How to lock the table
- table We will store the open table here
-
- RETURN VALUE
- 1 Cannot lock table
- 2 The table is corrupted - different number of fields
- 0 OK
-*/
-
-int
-Events::open_event_table(THD *thd, enum thr_lock_type lock_type,
- TABLE **table)
-{
- return db_repository->open_event_table(thd, lock_type, table);
-}
-
-
-/*
The function exported to the world for creating of events.
SYNOPSIS
@@ -351,16 +328,24 @@ Events::create_event(THD *thd, Event_parse_data *parse_data, bool if_not_exists)
/* On error conditions my_error() is called so no need to handle here */
if (!(ret= db_repository->create_event(thd, parse_data, if_not_exists)))
{
- if ((ret= event_queue->create_event(thd, parse_data->dbname,
- parse_data->name)))
+ Event_queue_element *new_element;
+
+ if (!(new_element= new Event_queue_element()))
+ ret= TRUE; // OOM
+ else if ((ret= db_repository->load_named_event(thd, parse_data->dbname,
+ parse_data->name,
+ new_element)))
{
DBUG_ASSERT(ret == OP_LOAD_ERROR);
- my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0));
+ delete new_element;
}
+ else
+ event_queue->create_event(thd, new_element);
}
pthread_mutex_unlock(&LOCK_event_metadata);
DBUG_RETURN(ret);
+
}
@@ -387,6 +372,7 @@ bool
Events::update_event(THD *thd, Event_parse_data *parse_data, sp_name *rename_to)
{
int ret;
+ Event_queue_element *new_element;
DBUG_ENTER("Events::update_event");
LEX_STRING *new_dbname= rename_to ? &rename_to->m_db : NULL;
LEX_STRING *new_name= rename_to ? &rename_to->m_name : NULL;
@@ -400,12 +386,20 @@ Events::update_event(THD *thd, Event_parse_data *parse_data, sp_name *rename_to)
/* On error conditions my_error() is called so no need to handle here */
if (!(ret= db_repository->update_event(thd, parse_data, new_dbname, new_name)))
{
- if ((ret= event_queue->update_event(thd, parse_data->dbname,
- parse_data->name, new_dbname, new_name)))
+ LEX_STRING dbname= new_dbname ? *new_dbname : parse_data->dbname;
+ LEX_STRING name= new_name ? *new_name : parse_data->name;
+
+ if (!(new_element= new Event_queue_element()))
+ ret= TRUE; // OOM
+ else if ((ret= db_repository->load_named_event(thd, dbname, name,
+ new_element)))
{
DBUG_ASSERT(ret == OP_LOAD_ERROR);
- my_error(ER_EVENT_MODIFY_QUEUE_ERROR, MYF(0));
+ delete new_element;
}
+ else
+ event_queue->update_event(thd, parse_data->dbname, parse_data->name,
+ new_element);
}
pthread_mutex_unlock(&LOCK_event_metadata);
@@ -423,10 +417,6 @@ Events::update_event(THD *thd, Event_parse_data *parse_data, sp_name *rename_to)
name [in] Event's name
if_exists [in] When set and the event does not exist =>
warning onto the stack
- only_from_disk [in] Whether to remove the event from the queue too.
- In case of Event_job_data::drop() it's needed to
- do only disk drop because Event_queue will handle
- removal from memory queue.
RETURN VALUE
FALSE OK
@@ -434,8 +424,7 @@ Events::update_event(THD *thd, Event_parse_data *parse_data, sp_name *rename_to)
*/
bool
-Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists,
- bool only_from_disk)
+Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists)
{
int ret;
DBUG_ENTER("Events::drop_event");
@@ -448,10 +437,7 @@ Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists,
pthread_mutex_lock(&LOCK_event_metadata);
/* On error conditions my_error() is called so no need to handle here */
if (!(ret= db_repository->drop_event(thd, dbname, name, if_exists)))
- {
- if (!only_from_disk)
- event_queue->drop_event(thd, dbname, name);
- }
+ event_queue->drop_event(thd, dbname, name);
pthread_mutex_unlock(&LOCK_event_metadata);
DBUG_RETURN(ret);
}
@@ -655,11 +641,12 @@ Events::init()
}
check_system_tables_error= FALSE;
- if (event_queue->init_queue(thd, db_repository))
+ if (event_queue->init_queue(thd) || load_events_from_db(thd))
{
sql_print_error("SCHEDULER: Error while loading from disk.");
goto end;
}
+
scheduler->init_scheduler(event_queue);
DBUG_ASSERT(opt_event_scheduler == Events::EVENTS_ON ||
@@ -667,6 +654,7 @@ Events::init()
if (opt_event_scheduler == Events::EVENTS_ON)
res= scheduler->start();
+ Event_worker_thread::init(this, db_repository);
end:
delete thd;
/* Remember that we don't have a THD */
@@ -903,3 +891,131 @@ Events::check_system_tables(THD *thd)
DBUG_RETURN(ret);
}
+
+
+/*
+ Loads all ENABLED events from mysql.event into the prioritized
+ queue. Called during scheduler main thread initialization. Compiles
+ the events. Creates Event_queue_element instances for every ENABLED event
+ from mysql.event.
+
+ SYNOPSIS
+ Events::load_events_from_db()
+ thd Thread context. Used for memory allocation in some cases.
+
+ RETURN VALUE
+ 0 OK
+ !0 Error (EVEX_OPEN_TABLE_FAILED, EVEX_MICROSECOND_UNSUP,
+ EVEX_COMPILE_ERROR) - in all these cases mysql.event was
+ tampered.
+
+ NOTES
+ Reports the error to the console
+*/
+
+int
+Events::load_events_from_db(THD *thd)
+{
+ TABLE *table;
+ READ_RECORD read_record_info;
+ int ret= -1;
+ uint count= 0;
+ bool clean_the_queue= TRUE;
+
+ DBUG_ENTER("Events::load_events_from_db");
+ DBUG_PRINT("enter", ("thd=0x%lx", thd));
+
+ if ((ret= db_repository->open_event_table(thd, TL_READ, &table)))
+ {
+ sql_print_error("SCHEDULER: Table mysql.event is damaged. Can not open");
+ DBUG_RETURN(EVEX_OPEN_TABLE_FAILED);
+ }
+
+ init_read_record(&read_record_info, thd, table ,NULL,1,0);
+ while (!(read_record_info.read_record(&read_record_info)))
+ {
+ Event_queue_element *et;
+ if (!(et= new Event_queue_element))
+ {
+ DBUG_PRINT("info", ("Out of memory"));
+ break;
+ }
+ DBUG_PRINT("info", ("Loading event from row."));
+
+ if ((ret= et->load_from_row(table)))
+ {
+ sql_print_error("SCHEDULER: Error while loading from mysql.event. "
+ "Table probably corrupted");
+ break;
+ }
+ if (et->status != Event_queue_element::ENABLED)
+ {
+ DBUG_PRINT("info",("%s is disabled",et->name.str));
+ delete et;
+ continue;
+ }
+
+ /* let's find when to be executed */
+ if (et->compute_next_execution_time())
+ {
+ sql_print_error("SCHEDULER: Error while computing execution time of %s.%s."
+ " Skipping", et->dbname.str, et->name.str);
+ continue;
+ }
+
+ {
+ Event_job_data temp_job_data;
+ DBUG_PRINT("info", ("Event %s loaded from row. ", et->name.str));
+
+ temp_job_data.load_from_row(table);
+
+ /*
+ We load only on scheduler root just to check whether the body
+ compiles.
+ */
+ switch (ret= temp_job_data.compile(thd, thd->mem_root)) {
+ case EVEX_MICROSECOND_UNSUP:
+ sql_print_error("SCHEDULER: mysql.event is tampered. MICROSECOND is not "
+ "supported but found in mysql.event");
+ break;
+ case EVEX_COMPILE_ERROR:
+ sql_print_error("SCHEDULER: Error while compiling %s.%s. Aborting load",
+ et->dbname.str, et->name.str);
+ break;
+ default:
+ break;
+ }
+ thd->end_statement();
+ thd->cleanup_after_query();
+ }
+ if (ret)
+ {
+ delete et;
+ goto end;
+ }
+
+ DBUG_PRINT("load_events_from_db", ("Adding 0x%lx to the exec list."));
+ event_queue->create_event(thd, et);
+ count++;
+ }
+ clean_the_queue= FALSE;
+end:
+ end_read_record(&read_record_info);
+
+ if (clean_the_queue)
+ {
+ event_queue->empty_queue();
+ ret= -1;
+ }
+ else
+ {
+ ret= 0;
+ sql_print_information("SCHEDULER: Loaded %d event%s", count,
+ (count == 1)?"":"s");
+ }
+
+ close_thread_tables(thd);
+
+ DBUG_PRINT("info", ("Status code %d. Loaded %d event(s)", ret, count));
+ DBUG_RETURN(ret);
+}
diff --git a/sql/events.h b/sql/events.h
index 621ab0ffca5..35ee3c569d0 100644
--- a/sql/events.h
+++ b/sql/events.h
@@ -42,13 +42,6 @@ sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs);
class Events
{
public:
- /*
- Quite NOT the best practice and will be removed once
- Event_timed::drop() and Event_timed is fixed not do drop directly
- or other scheme will be found.
- */
- friend class Event_queue_element;
-
/* The order should match the order in opt_typelib */
enum enum_opt_event_scheduler
{
@@ -92,15 +85,11 @@ public:
update_event(THD *thd, Event_parse_data *parse_data, sp_name *rename_to);
bool
- drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists,
- bool only_from_disk);
+ drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists);
void
drop_schema_events(THD *thd, char *db);
- int
- open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table);
-
bool
show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name);
@@ -119,6 +108,9 @@ private:
bool
check_system_tables(THD *thd);
+ int
+ load_events_from_db(THD *thd);
+
/* Singleton DP is used */
Events();
~Events(){}
diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc
index 9c3959f3feb..872d97a6fa8 100644
--- a/sql/ha_ndbcluster.cc
+++ b/sql/ha_ndbcluster.cc
@@ -8547,7 +8547,6 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
goto ndb_util_thread_fail;
thd->init_for_queries();
thd->version=refresh_version;
- thd->set_time();
thd->main_security_ctx.host_or_ip= "";
thd->client_capabilities = 0;
my_net_init(&thd->net, 0);
diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc
index 73363328078..2615732e7ee 100644
--- a/sql/ha_ndbcluster_binlog.cc
+++ b/sql/ha_ndbcluster_binlog.cc
@@ -3503,7 +3503,6 @@ pthread_handler_t ndb_binlog_thread_func(void *arg)
thd->command= COM_DAEMON;
thd->system_thread= SYSTEM_THREAD_NDBCLUSTER_BINLOG;
thd->version= refresh_version;
- thd->set_time();
thd->main_security_ctx.host_or_ip= "";
thd->client_capabilities= 0;
my_net_init(&thd->net, 0);
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
index 952a305c5a4..db0d118c2e0 100644
--- a/sql/ha_partition.cc
+++ b/sql/ha_partition.cc
@@ -4482,7 +4482,7 @@ void ha_partition::get_dynamic_partition_info(PARTITION_INFO *stat_info,
2) It is called from close_thread_table which in turn is called from
close_thread_tables except in the case where the tables are locked
in which case ha_commit_stmt is called instead.
- It is only called from here if flush_version hasn't changed and the
+ It is only called from here if refresh_version hasn't changed and the
table is not an old table when calling close_thread_table.
close_thread_tables is called from many places as a general clean up
function after completing a query.
@@ -4503,8 +4503,9 @@ void ha_partition::get_dynamic_partition_info(PARTITION_INFO *stat_info,
The handler will set HA_KEYREAD_ONLY in its table flags to indicate this
feature is supported.
HA_EXTRA_FLUSH:
- Indication to flush tables to disk, called at close_thread_table to
+ Indication to flush tables to disk, is supposed to be used to
ensure disk based tables are flushed at end of query execution.
+ Currently is never used.
2) Parameters used by some non-MyISAM handlers
----------------------------------------------
diff --git a/sql/handler.cc b/sql/handler.cc
index 2244aaa5311..c1a28943278 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -2311,7 +2311,7 @@ int handler::check_old_types()
}
-static bool update_frm_version(TABLE *table, bool needs_lock)
+static bool update_frm_version(TABLE *table)
{
char path[FN_REFLEN];
File file;
@@ -2323,9 +2323,6 @@ static bool update_frm_version(TABLE *table, bool needs_lock)
strxmov(path, table->s->normalized_path.str, reg_ext, NullS);
- if (needs_lock)
- pthread_mutex_lock(&LOCK_open);
-
if ((file= my_open(path, O_RDWR|O_BINARY, MYF(MY_WME))) >= 0)
{
uchar version[4];
@@ -2347,8 +2344,6 @@ static bool update_frm_version(TABLE *table, bool needs_lock)
err:
if (file >= 0)
VOID(my_close(file,MYF(MY_WME)));
- if (needs_lock)
- pthread_mutex_unlock(&LOCK_open);
DBUG_RETURN(result);
}
@@ -2465,7 +2460,7 @@ int handler::ha_check(THD *thd, HA_CHECK_OPT *check_opt)
}
if ((error= check(thd, check_opt)))
return error;
- return update_frm_version(table, 0);
+ return update_frm_version(table);
}
@@ -2474,7 +2469,7 @@ int handler::ha_repair(THD* thd, HA_CHECK_OPT* check_opt)
int result;
if ((result= repair(thd, check_opt)))
return result;
- return update_frm_version(table, 0);
+ return update_frm_version(table);
}
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 415c91772fc..e78550598f5 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -840,6 +840,59 @@ int Arg_comparator::compare_e_row()
}
+void Item_func_truth::fix_length_and_dec()
+{
+ maybe_null= 0;
+ null_value= 0;
+ decimals= 0;
+ max_length= 1;
+}
+
+
+void Item_func_truth::print(String *str)
+{
+ str->append('(');
+ args[0]->print(str);
+ str->append(STRING_WITH_LEN(" is "));
+ if (! affirmative)
+ str->append(STRING_WITH_LEN("not "));
+ if (value)
+ str->append(STRING_WITH_LEN("true"));
+ else
+ str->append(STRING_WITH_LEN("false"));
+ str->append(')');
+}
+
+
+bool Item_func_truth::val_bool()
+{
+ bool val= args[0]->val_bool();
+ if (args[0]->null_value)
+ {
+ /*
+ NULL val IS {TRUE, FALSE} --> FALSE
+ NULL val IS NOT {TRUE, FALSE} --> TRUE
+ */
+ return (! affirmative);
+ }
+
+ if (affirmative)
+ {
+ /* {TRUE, FALSE} val IS {TRUE, FALSE} value */
+ return (val == value);
+ }
+
+ /* {TRUE, FALSE} val IS NOT {TRUE, FALSE} value */
+ return (val != value);
+}
+
+
+longlong Item_func_truth::val_int()
+{
+ return (val_bool() ? 1 : 0);
+}
+
+
bool Item_in_optimizer::fix_left(THD *thd, Item **ref)
{
if (!args[0]->fixed && args[0]->fix_fields(thd, args) ||
@@ -1579,6 +1632,7 @@ Item_func_if::fix_length_and_dec()
{
maybe_null=args[1]->maybe_null || args[2]->maybe_null;
decimals= max(args[1]->decimals, args[2]->decimals);
+ unsigned_flag=args[1]->unsigned_flag && args[2]->unsigned_flag;
enum Item_result arg1_type=args[1]->result_type();
enum Item_result arg2_type=args[2]->result_type();
@@ -1608,12 +1662,20 @@ Item_func_if::fix_length_and_dec()
collation.set(&my_charset_bin); // Number
}
}
- max_length=
- (cached_result_type == DECIMAL_RESULT || cached_result_type == INT_RESULT) ?
- (max(args[1]->max_length - args[1]->decimals,
- args[2]->max_length - args[2]->decimals) + decimals +
- (unsigned_flag ? 0 : 1) ) :
- max(args[1]->max_length, args[2]->max_length);
+
+ if ((cached_result_type == DECIMAL_RESULT )
+ || (cached_result_type == INT_RESULT))
+ {
+ int len1= args[1]->max_length - args[1]->decimals
+ - (args[1]->unsigned_flag ? 0 : 1);
+
+ int len2= args[2]->max_length - args[2]->decimals
+ - (args[2]->unsigned_flag ? 0 : 1);
+
+ max_length=max(len1, len2) + decimals + (unsigned_flag ? 0 : 1);
+ }
+ else
+ max_length= max(args[1]->max_length, args[2]->max_length);
}
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 08a411ae39a..3b08036368c 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -101,6 +101,92 @@ public:
uint decimal_precision() const { return 1; }
};
+
+/**
+ Abstract Item class, to represent <code>X IS [NOT] (TRUE | FALSE)</code>
+ boolean predicates.
+*/
+
+class Item_func_truth : public Item_bool_func
+{
+public:
+ virtual bool val_bool();
+ virtual longlong val_int();
+ virtual void fix_length_and_dec();
+ virtual void print(String *str);
+
+protected:
+ Item_func_truth(Item *a, bool a_value, bool a_affirmative)
+ : Item_bool_func(a), value(a_value), affirmative(a_affirmative)
+ {}
+
+ ~Item_func_truth()
+ {}
+private:
+ /**
+ True for <code>X IS [NOT] TRUE</code>,
+ false for <code>X IS [NOT] FALSE</code> predicates.
+ */
+ const bool value;
+ /**
+ True for <code>X IS Y</code>, false for <code>X IS NOT Y</code> predicates.
+ */
+ const bool affirmative;
+};
+
+
+/**
+ This Item represents a <code>X IS TRUE</code> boolean predicate.
+*/
+
+class Item_func_istrue : public Item_func_truth
+{
+public:
+ Item_func_istrue(Item *a) : Item_func_truth(a, true, true) {}
+ ~Item_func_istrue() {}
+ virtual const char* func_name() const { return "istrue"; }
+};
+
+
+/**
+ This Item represents a <code>X IS NOT TRUE</code> boolean predicate.
+*/
+
+class Item_func_isnottrue : public Item_func_truth
+{
+public:
+ Item_func_isnottrue(Item *a) : Item_func_truth(a, true, false) {}
+ ~Item_func_isnottrue() {}
+ virtual const char* func_name() const { return "isnottrue"; }
+};
+
+
+/**
+ This Item represents a <code>X IS FALSE</code> boolean predicate.
+*/
+
+class Item_func_isfalse : public Item_func_truth
+{
+public:
+ Item_func_isfalse(Item *a) : Item_func_truth(a, false, true) {}
+ ~Item_func_isfalse() {}
+ virtual const char* func_name() const { return "isfalse"; }
+};
+
+
+/**
+ This Item represents a <code>X IS NOT FALSE</code> boolean predicate.
+*/
+
+class Item_func_isnotfalse : public Item_func_truth
+{
+public:
+ Item_func_isnotfalse(Item *a) : Item_func_truth(a, false, false) {}
+ ~Item_func_isnotfalse() {}
+ virtual const char* func_name() const { return "isnotfalse"; }
+};
+
+
class Item_cache;
#define UNKNOWN ((my_bool)-1)
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index de67a314631..135ed33feec 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -51,6 +51,10 @@ Item_subselect::Item_subselect():
void Item_subselect::init(st_select_lex *select_lex,
select_subselect *result)
{
+ /*
+ Please see Item_singlerow_subselect::invalidate_and_restore_select_lex(),
+ which depends on alterations to the parse tree implemented here.
+ */
DBUG_ENTER("Item_subselect::init");
DBUG_PRINT("enter", ("select_lex: 0x%lx", (long) select_lex));
@@ -91,6 +95,12 @@ void Item_subselect::init(st_select_lex *select_lex,
DBUG_VOID_RETURN;
}
+st_select_lex *
+Item_subselect::get_select_lex()
+{
+ return unit->first_select();
+}
+
void Item_subselect::cleanup()
{
DBUG_ENTER("Item_subselect::cleanup");
@@ -311,6 +321,26 @@ Item_singlerow_subselect::Item_singlerow_subselect(st_select_lex *select_lex)
DBUG_VOID_RETURN;
}
+st_select_lex *
+Item_singlerow_subselect::invalidate_and_restore_select_lex()
+{
+ DBUG_ENTER("Item_singlerow_subselect::invalidate_and_restore_select_lex");
+ st_select_lex *result= get_select_lex();
+
+ DBUG_ASSERT(result);
+
+ /*
+ This code restore the parse tree in it's state before the execution of
+ Item_singlerow_subselect::Item_singlerow_subselect(),
+ and in particular decouples this object from the SELECT_LEX,
+ so that the SELECT_LEX can be used with a different flavor
+ or Item_subselect instead, as part of query rewriting.
+ */
+ unit->item= NULL;
+
+ DBUG_RETURN(result);
+}
+
Item_maxmin_subselect::Item_maxmin_subselect(THD *thd_param,
Item_subselect *parent,
st_select_lex *select_lex,
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index fdf3708cabb..37264f2136f 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -127,6 +127,12 @@ public:
enum_parsing_place place() { return parsing_place; }
bool walk(Item_processor processor, bool walk_subquery, byte *arg);
+ /**
+ Get the SELECT_LEX structure associated with this Item.
+ @return the SELECT_LEX structure associated with this Item
+ */
+ st_select_lex* get_select_lex();
+
friend class select_subselect;
friend class Item_in_optimizer;
friend bool Item_field::fix_fields(THD *, Item **);
@@ -170,6 +176,20 @@ public:
bool null_inside();
void bring_value();
+ /**
+ This method is used to implement a special case of semantic tree
+ rewriting, mandated by a SQL:2003 exception in the specification.
+ The only caller of this method is handle_sql2003_note184_exception(),
+ see the code there for more details.
+ Note that this method breaks the object internal integrity, by
+ removing it's association with the corresponding SELECT_LEX,
+ making this object orphan from the parse tree.
+ No other method, beside the destructor, should be called on this
+ object, as it is now invalid.
+ @return the SELECT_LEX structure that was given in the constructor.
+ */
+ st_select_lex* invalidate_and_restore_select_lex();
+
friend class select_singlerow_subselect;
};
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index ae9549f93d2..ec4ee360122 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -1579,7 +1579,7 @@ extern double log_01[32];
extern ulonglong log_10_int[20];
extern ulonglong keybuff_size;
extern ulonglong thd_startup_options;
-extern ulong refresh_version,flush_version, thread_id;
+extern ulong refresh_version, thread_id;
extern ulong binlog_cache_use, binlog_cache_disk_use;
extern ulong aborted_threads,aborted_connects;
extern ulong delayed_insert_timeout;
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index a6525c1c78c..4fd7063a8f6 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -476,7 +476,7 @@ ulong slave_net_timeout, slave_trans_retries;
ulong thread_cache_size=0, thread_pool_size= 0;
ulong binlog_cache_size=0, max_binlog_cache_size=0;
ulong query_cache_size=0;
-ulong refresh_version, flush_version; /* Increments on each reload */
+ulong refresh_version; /* Increments on each reload */
query_id_t global_query_id;
ulong aborted_threads, aborted_connects;
ulong delayed_insert_timeout, delayed_insert_limit, delayed_queue_size;
@@ -7105,7 +7105,7 @@ static void mysql_init_variables(void)
OPTION_QUOTE_SHOW_CREATE | OPTION_SQL_NOTES);
protocol_version= PROTOCOL_VERSION;
what_to_log= ~ (1L << (uint) COM_TIME);
- refresh_version= flush_version= 1L; /* Increments on each reload */
+ refresh_version= 1L; /* Increments on each reload */
global_query_id= thread_id= 1L;
strmov(server_version, MYSQL_SERVER_VERSION);
myisam_recover_options_str= sql_mode_str= "OFF";
diff --git a/sql/set_var.cc b/sql/set_var.cc
index ad5559165f3..df9297917b3 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -2626,7 +2626,7 @@ bool update_sys_var_str_path(THD *thd, sys_var_str *var_str,
{
switch (log_type) {
case QUERY_LOG_SLOW:
- file_log->open_slow_log(sys_var_general_log_path.value);
+ file_log->open_slow_log(sys_var_slow_log_path.value);
break;
case QUERY_LOG_GENERAL:
file_log->open_query_log(sys_var_general_log_path.value);
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index b77d0cc9a0c..2f141068b70 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -36,6 +36,7 @@ Item_result
sp_map_result_type(enum enum_field_types type)
{
switch (type) {
+ case MYSQL_TYPE_BIT:
case MYSQL_TYPE_TINY:
case MYSQL_TYPE_SHORT:
case MYSQL_TYPE_LONG:
@@ -58,6 +59,7 @@ Item::Type
sp_map_item_type(enum enum_field_types type)
{
switch (type) {
+ case MYSQL_TYPE_BIT:
case MYSQL_TYPE_TINY:
case MYSQL_TYPE_SHORT:
case MYSQL_TYPE_LONG:
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 02f7044a4bf..ac1222e6bd6 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -1141,12 +1141,7 @@ bool close_thread_table(THD *thd, TABLE **table_ptr)
}
else
{
- if (table->s->flush_version != flush_version)
- {
- table->s->flush_version= flush_version;
- table->file->extra(HA_EXTRA_FLUSH);
- }
- // Free memory and reset for next loop
+ /* Free memory and reset for next loop */
table->file->ha_reset();
table->in_use=0;
if (unused_tables)
@@ -1788,7 +1783,6 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list)
share= table->s;
share->version=0;
- share->flush_version=0;
table->in_use = thd;
check_unused();
table->next = thd->open_tables;
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index 8c0cb72e1f4..c06d7161ec1 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -1761,8 +1761,18 @@ void Query_cache::free_cache()
{
DBUG_ENTER("Query_cache::free_cache");
if (query_cache_size > 0)
- {
flush_cache();
+ /*
+ There may be two free_cache() calls in progress, because we
+ release 'structure_guard_mutex' in flush_cache(). When the second
+ flush_cache() wakes up from the wait on 'COND_flush_finished', the
+ first call to free_cache() has done its job. So we have to test
+ 'query_cache_size > 0' the second time to see if the cache wasn't
+ reset by other thread, or if it was reset and was re-enabled then.
+ If the cache was reset, then we have nothing to do here.
+ */
+ if (query_cache_size > 0)
+ {
#ifndef DBUG_OFF
if (bins[0].free_blocks == 0)
{
@@ -1804,6 +1814,12 @@ void Query_cache::free_cache()
flush_in_progress flag and releases the lock, so other threads may
proceed skipping the cache as if it is disabled. Concurrent
flushes are performed in turn.
+
+ After flush_cache() call, the cache is flushed, all the freed
+ memory is accumulated in bin[0], and the 'structure_guard_mutex'
+ is locked. However, since we could release the mutex during
+ execution, the rest of the cache state could have been changed,
+ and should not be relied on.
*/
void Query_cache::flush_cache()
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 039fd71d670..107e646f740 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -354,6 +354,7 @@ void THD::init(void)
void THD::init_for_queries()
{
+ set_time();
ha_enable_transaction(this,TRUE);
reset_root_defaults(mem_root, variables.query_alloc_block_size,
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index c7cad2761bf..6a403dd82d9 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -100,6 +100,16 @@ void lex_free(void)
}
+void
+st_parsing_options::reset()
+{
+ allows_variable= TRUE;
+ allows_select_into= TRUE;
+ allows_select_procedure= TRUE;
+ allows_derived= TRUE;
+}
+
+
/*
This is called before every query that is to be parsed.
Because of this, it's critical to not do too much things here.
@@ -150,6 +160,7 @@ void lex_start(THD *thd, const uchar *buf, uint length)
lex->safe_to_cache_query= 1;
lex->time_zone_tables_used= 0;
lex->leaf_tables_insert= 0;
+ lex->parsing_options.reset();
lex->empty_field_list_on_rset= 0;
lex->select_lex.select_number= 1;
lex->next_state=MY_LEX_START;
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index fa0f9d7cbbb..a3368454faf 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -916,10 +916,8 @@ struct st_parsing_options
bool allows_select_procedure;
bool allows_derived;
- st_parsing_options()
- : allows_variable(TRUE), allows_select_into(TRUE),
- allows_select_procedure(TRUE), allows_derived(TRUE)
- {}
+ st_parsing_options() { reset(); }
+ void reset();
};
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 11eb510d6c8..bfe71ce271c 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -28,6 +28,24 @@
#include "events.h"
#include "event_data_objects.h"
+#ifdef HAVE_OPENSSL
+/*
+ Without SSL the handshake consists of one packet. This packet
+ has both client capabilites and scrambled password.
+ With SSL the handshake might consist of two packets. If the first
+ packet (client capabilities) has CLIENT_SSL flag set, we have to
+ switch to SSL and read the second packet. The scrambled password
+ is in the second packet and client_capabilites field will be ignored.
+ Maybe it is better to accept flags other than CLIENT_SSL from the
+ second packet?
+*/
+#define SSL_HANDSHAKE_SIZE 2
+#define NORMAL_HANDSHAKE_SIZE 6
+#define MIN_HANDSHAKE_SIZE 2
+#else
+#define MIN_HANDSHAKE_SIZE 6
+#endif /* HAVE_OPENSSL */
+
/* Used in error handling only */
#define SP_TYPE_STRING(LP) \
((LP)->sphead->m_type == TYPE_ENUM_FUNCTION ? "FUNCTION" : "PROCEDURE")
@@ -38,6 +56,11 @@
(LP)->sql_command == SQLCOM_DROP_FUNCTION ? \
"FUNCTION" : "PROCEDURE")
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+static void time_out_user_resource_limits(THD *thd, USER_CONN *uc);
+static int check_for_max_user_connections(THD *thd, USER_CONN *uc);
+static void decrease_user_connections(USER_CONN *uc);
+#endif /* NO_EMBEDDED_ACCESS_CHECKS */
static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables);
const char *any_db="*any*"; // Special symbol for check_access
@@ -80,6 +103,20 @@ const char *xa_state_names[]={
"NON-EXISTING", "ACTIVE", "IDLE", "PREPARED"
};
+#ifdef __WIN__
+static void test_signal(int sig_ptr)
+{
+#if !defined( DBUG_OFF)
+ MessageBox(NULL,"Test signal","DBUG",MB_OK);
+#endif
+}
+static void init_signals(void)
+{
+ int signals[7] = {SIGINT,SIGILL,SIGFPE,SIGSEGV,SIGTERM,SIGBREAK,SIGABRT } ;
+ for (int i=0 ; i < 7 ; i++)
+ signal( signals[i], test_signal) ;
+}
+#endif
static void unlock_locked_tables(THD *thd)
{
@@ -123,7 +160,6 @@ bool end_active_trans(THD *thd)
DBUG_RETURN(error);
}
-
bool begin_trans(THD *thd)
{
int error=0;
@@ -175,6 +211,413 @@ static bool some_non_temp_table_to_be_updated(THD *thd, TABLE_LIST *tables)
return 0;
}
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+static HASH hash_user_connections;
+
+static int get_or_create_user_conn(THD *thd, const char *user,
+ const char *host,
+ USER_RESOURCES *mqh)
+{
+ int return_val= 0;
+ uint temp_len, user_len;
+ char temp_user[USER_HOST_BUFF_SIZE];
+ struct user_conn *uc;
+
+ DBUG_ASSERT(user != 0);
+ DBUG_ASSERT(host != 0);
+
+ user_len= strlen(user);
+ temp_len= (strmov(strmov(temp_user, user)+1, host) - temp_user)+1;
+ (void) pthread_mutex_lock(&LOCK_user_conn);
+ if (!(uc = (struct user_conn *) hash_search(&hash_user_connections,
+ (byte*) temp_user, temp_len)))
+ {
+ /* First connection for user; Create a user connection object */
+ if (!(uc= ((struct user_conn*)
+ my_malloc(sizeof(struct user_conn) + temp_len+1,
+ MYF(MY_WME)))))
+ {
+ net_send_error(thd, 0, NullS); // Out of memory
+ return_val= 1;
+ goto end;
+ }
+ uc->user=(char*) (uc+1);
+ memcpy(uc->user,temp_user,temp_len+1);
+ uc->host= uc->user + user_len + 1;
+ uc->len= temp_len;
+ uc->connections= uc->questions= uc->updates= uc->conn_per_hour= 0;
+ uc->user_resources= *mqh;
+ uc->intime= thd->thr_create_time;
+ if (my_hash_insert(&hash_user_connections, (byte*) uc))
+ {
+ my_free((char*) uc,0);
+ net_send_error(thd, 0, NullS); // Out of memory
+ return_val= 1;
+ goto end;
+ }
+ }
+ thd->user_connect=uc;
+ uc->connections++;
+end:
+ (void) pthread_mutex_unlock(&LOCK_user_conn);
+ return return_val;
+
+}
+#endif /* !NO_EMBEDDED_ACCESS_CHECKS */
+
+
+/*
+ Check if user exist and password supplied is correct.
+
+ SYNOPSIS
+ check_user()
+ thd thread handle, thd->security_ctx->{host,user,ip} are used
+ command originator of the check: now check_user is called
+ during connect and change user procedures; used for
+ logging.
+ passwd scrambled password received from client
+ passwd_len length of scrambled password
+ db database name to connect to, may be NULL
+ check_count dont know exactly
+
+ Note, that host, user and passwd may point to communication buffer.
+ Current implementation does not depend on that, but future changes
+ should be done with this in mind; 'thd' is INOUT, all other params
+ are 'IN'.
+
+ RETURN VALUE
+ 0 OK; thd->security_ctx->user/master_access/priv_user/db_access and
+ thd->db are updated; OK is sent to client;
+ -1 access denied or handshake error; error is sent to client;
+ >0 error, not sent to client
+*/
+
+int check_user(THD *thd, enum enum_server_command command,
+ const char *passwd, uint passwd_len, const char *db,
+ bool check_count)
+{
+ DBUG_ENTER("check_user");
+
+#ifdef NO_EMBEDDED_ACCESS_CHECKS
+ thd->main_security_ctx.master_access= GLOBAL_ACLS; // Full rights
+ /* Change database if necessary */
+ if (db && db[0])
+ {
+ /*
+ thd->db is saved in caller and needs to be freed by caller if this
+ function returns 0
+ */
+ thd->reset_db(NULL, 0);
+ if (mysql_change_db(thd, db, FALSE))
+ {
+ /* Send the error to the client */
+ net_send_error(thd);
+ DBUG_RETURN(-1);
+ }
+ }
+ send_ok(thd);
+ DBUG_RETURN(0);
+#else
+
+ my_bool opt_secure_auth_local;
+ pthread_mutex_lock(&LOCK_global_system_variables);
+ opt_secure_auth_local= opt_secure_auth;
+ pthread_mutex_unlock(&LOCK_global_system_variables);
+
+ /*
+ If the server is running in secure auth mode, short scrambles are
+ forbidden.
+ */
+ if (opt_secure_auth_local && passwd_len == SCRAMBLE_LENGTH_323)
+ {
+ net_printf_error(thd, ER_NOT_SUPPORTED_AUTH_MODE);
+ general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
+ DBUG_RETURN(-1);
+ }
+ if (passwd_len != 0 &&
+ passwd_len != SCRAMBLE_LENGTH &&
+ passwd_len != SCRAMBLE_LENGTH_323)
+ DBUG_RETURN(ER_HANDSHAKE_ERROR);
+
+ /*
+ Clear thd->db as it points to something, that will be freed when
+ connection is closed. We don't want to accidentally free a wrong pointer
+ if connect failed. Also in case of 'CHANGE USER' failure, current
+ database will be switched to 'no database selected'.
+ */
+ thd->reset_db(NULL, 0);
+
+ USER_RESOURCES ur;
+ int res= acl_getroot(thd, &ur, passwd, passwd_len);
+#ifndef EMBEDDED_LIBRARY
+ if (res == -1)
+ {
+ /*
+ This happens when client (new) sends password scrambled with
+ scramble(), but database holds old value (scrambled with
+ scramble_323()). Here we please client to send scrambled_password
+ in old format.
+ */
+ NET *net= &thd->net;
+ if (opt_secure_auth_local)
+ {
+ net_printf_error(thd, ER_SERVER_IS_IN_SECURE_AUTH_MODE,
+ thd->main_security_ctx.user,
+ thd->main_security_ctx.host_or_ip);
+ general_log_print(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE),
+ thd->main_security_ctx.user,
+ thd->main_security_ctx.host_or_ip);
+ DBUG_RETURN(-1);
+ }
+ /* We have to read very specific packet size */
+ if (send_old_password_request(thd) ||
+ my_net_read(net) != SCRAMBLE_LENGTH_323 + 1)
+ {
+ inc_host_errors(&thd->remote.sin_addr);
+ DBUG_RETURN(ER_HANDSHAKE_ERROR);
+ }
+ /* Final attempt to check the user based on reply */
+ /* So as passwd is short, errcode is always >= 0 */
+ res= acl_getroot(thd, &ur, (char *) net->read_pos, SCRAMBLE_LENGTH_323);
+ }
+#endif /*EMBEDDED_LIBRARY*/
+ /* here res is always >= 0 */
+ if (res == 0)
+ {
+ if (!(thd->main_security_ctx.master_access &
+ NO_ACCESS)) // authentication is OK
+ {
+ DBUG_PRINT("info",
+ ("Capabilities: %lu packet_length: %ld Host: '%s' "
+ "Login user: '%s' Priv_user: '%s' Using password: %s "
+ "Access: %lu db: '%s'",
+ thd->client_capabilities,
+ thd->max_client_packet_length,
+ thd->main_security_ctx.host_or_ip,
+ thd->main_security_ctx.user,
+ thd->main_security_ctx.priv_user,
+ passwd_len ? "yes": "no",
+ thd->main_security_ctx.master_access,
+ (thd->db ? thd->db : "*none*")));
+
+ if (check_count)
+ {
+ VOID(pthread_mutex_lock(&LOCK_thread_count));
+ bool count_ok= thread_count <= max_connections + delayed_insert_threads
+ || (thd->main_security_ctx.master_access & SUPER_ACL);
+ VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ if (!count_ok)
+ { // too many connections
+ net_send_error(thd, ER_CON_COUNT_ERROR);
+ DBUG_RETURN(-1);
+ }
+ }
+
+ /*
+ Log the command before authentication checks, so that the user can
+ check the log for the tried login tried and also to detect
+ break-in attempts.
+ */
+ general_log_print(thd, command,
+ (thd->main_security_ctx.priv_user ==
+ thd->main_security_ctx.user ?
+ (char*) "%s@%s on %s" :
+ (char*) "%s@%s as anonymous on %s"),
+ thd->main_security_ctx.user,
+ thd->main_security_ctx.host_or_ip,
+ db ? db : (char*) "");
+
+ /*
+ This is the default access rights for the current database. It's
+ set to 0 here because we don't have an active database yet (and we
+ may not have an active database to set.
+ */
+ thd->main_security_ctx.db_access=0;
+
+ /* Don't allow user to connect if he has done too many queries */
+ if ((ur.questions || ur.updates || ur.conn_per_hour || ur.user_conn ||
+ max_user_connections) &&
+ get_or_create_user_conn(thd,
+ (opt_old_style_user_limits ? thd->main_security_ctx.user :
+ thd->main_security_ctx.priv_user),
+ (opt_old_style_user_limits ? thd->main_security_ctx.host_or_ip :
+ thd->main_security_ctx.priv_host),
+ &ur))
+ DBUG_RETURN(-1);
+ if (thd->user_connect &&
+ (thd->user_connect->user_resources.conn_per_hour ||
+ thd->user_connect->user_resources.user_conn ||
+ max_user_connections) &&
+ check_for_max_user_connections(thd, thd->user_connect))
+ DBUG_RETURN(-1);
+
+ /* Change database if necessary */
+ if (db && db[0])
+ {
+ if (mysql_change_db(thd, db, FALSE))
+ {
+ /* Send error to the client */
+ net_send_error(thd);
+ if (thd->user_connect)
+ decrease_user_connections(thd->user_connect);
+ DBUG_RETURN(-1);
+ }
+ }
+ send_ok(thd);
+ thd->password= test(passwd_len); // remember for error messages
+ /* Ready to handle queries */
+ DBUG_RETURN(0);
+ }
+ }
+ else if (res == 2) // client gave short hash, server has long hash
+ {
+ net_printf_error(thd, ER_NOT_SUPPORTED_AUTH_MODE);
+ general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
+ DBUG_RETURN(-1);
+ }
+ net_printf_error(thd, ER_ACCESS_DENIED_ERROR,
+ thd->main_security_ctx.user,
+ thd->main_security_ctx.host_or_ip,
+ passwd_len ? ER(ER_YES) : ER(ER_NO));
+ general_log_print(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR),
+ thd->main_security_ctx.user,
+ thd->main_security_ctx.host_or_ip,
+ passwd_len ? ER(ER_YES) : ER(ER_NO));
+ DBUG_RETURN(-1);
+#endif /* NO_EMBEDDED_ACCESS_CHECKS */
+}
+
+/*
+ Check for maximum allowable user connections, if the mysqld server is
+ started with corresponding variable that is greater then 0.
+*/
+
+extern "C" byte *get_key_conn(user_conn *buff, uint *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length=buff->len;
+ return (byte*) buff->user;
+}
+
+extern "C" void free_user(struct user_conn *uc)
+{
+ my_free((char*) uc,MYF(0));
+}
+
+void init_max_user_conn(void)
+{
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ (void) hash_init(&hash_user_connections,system_charset_info,max_connections,
+ 0,0,
+ (hash_get_key) get_key_conn, (hash_free_key) free_user,
+ 0);
+#endif
+}
+
+
+/*
+ check if user has already too many connections
+
+ SYNOPSIS
+ check_for_max_user_connections()
+ thd Thread handle
+ uc User connect object
+
+ NOTES
+ If check fails, we decrease user connection count, which means one
+ shouldn't call decrease_user_connections() after this function.
+
+ RETURN
+ 0 ok
+ 1 error
+*/
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+
+static int check_for_max_user_connections(THD *thd, USER_CONN *uc)
+{
+ int error=0;
+ DBUG_ENTER("check_for_max_user_connections");
+
+ (void) pthread_mutex_lock(&LOCK_user_conn);
+ if (max_user_connections && !uc->user_resources.user_conn &&
+ max_user_connections < (uint) uc->connections)
+ {
+ net_printf_error(thd, ER_TOO_MANY_USER_CONNECTIONS, uc->user);
+ error=1;
+ goto end;
+ }
+ time_out_user_resource_limits(thd, uc);
+ if (uc->user_resources.user_conn &&
+ uc->user_resources.user_conn < uc->connections)
+ {
+ net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user,
+ "max_user_connections",
+ (long) uc->user_resources.user_conn);
+ error= 1;
+ goto end;
+ }
+ if (uc->user_resources.conn_per_hour &&
+ uc->user_resources.conn_per_hour <= uc->conn_per_hour)
+ {
+ net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user,
+ "max_connections_per_hour",
+ (long) uc->user_resources.conn_per_hour);
+ error=1;
+ goto end;
+ }
+ uc->conn_per_hour++;
+
+ end:
+ if (error)
+ uc->connections--; // no need for decrease_user_connections() here
+ (void) pthread_mutex_unlock(&LOCK_user_conn);
+ DBUG_RETURN(error);
+}
+
+/*
+ Decrease user connection count
+
+ SYNOPSIS
+ decrease_user_connections()
+ uc User connection object
+
+ NOTES
+ If there is a n user connection object for a connection
+ (which only happens if 'max_user_connections' is defined or
+ if someone has created a resource grant for a user), then
+ the connection count is always incremented on connect.
+
+ The user connect object is not freed if some users has
+ 'max connections per hour' defined as we need to be able to hold
+ count over the lifetime of the connection.
+*/
+
+static void decrease_user_connections(USER_CONN *uc)
+{
+ DBUG_ENTER("decrease_user_connections");
+ (void) pthread_mutex_lock(&LOCK_user_conn);
+ DBUG_ASSERT(uc->connections);
+ if (!--uc->connections && !mqh_used)
+ {
+ /* Last connection for user; Delete it */
+ (void) hash_delete(&hash_user_connections,(byte*) uc);
+ }
+ (void) pthread_mutex_unlock(&LOCK_user_conn);
+ DBUG_VOID_RETURN;
+}
+
+#endif /* NO_EMBEDDED_ACCESS_CHECKS */
+
+
+void free_max_user_conn(void)
+{
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ hash_free(&hash_user_connections);
+#endif /* NO_EMBEDDED_ACCESS_CHECKS */
+}
+
+
/*
Mark all commands that somehow changes a table
@@ -258,6 +701,402 @@ bool is_update_query(enum enum_sql_command command)
return (sql_command_flags[command] & CF_CHANGES_DATA) != 0;
}
+/*
+ Reset per-hour user resource limits when it has been more than
+ an hour since they were last checked
+
+ SYNOPSIS:
+ time_out_user_resource_limits()
+ thd Thread handler
+ uc User connection details
+
+ NOTE:
+ This assumes that the LOCK_user_conn mutex has been acquired, so it is
+ safe to test and modify members of the USER_CONN structure.
+*/
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+
+static void time_out_user_resource_limits(THD *thd, USER_CONN *uc)
+{
+ time_t check_time = thd->start_time ? thd->start_time : time(NULL);
+ DBUG_ENTER("time_out_user_resource_limits");
+
+ /* If more than a hour since last check, reset resource checking */
+ if (check_time - uc->intime >= 3600)
+ {
+ uc->questions=1;
+ uc->updates=0;
+ uc->conn_per_hour=0;
+ uc->intime=check_time;
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+/*
+ Check if maximum queries per hour limit has been reached
+ returns 0 if OK.
+*/
+
+static bool check_mqh(THD *thd, uint check_command)
+{
+ bool error= 0;
+ USER_CONN *uc=thd->user_connect;
+ DBUG_ENTER("check_mqh");
+ DBUG_ASSERT(uc != 0);
+
+ (void) pthread_mutex_lock(&LOCK_user_conn);
+
+ time_out_user_resource_limits(thd, uc);
+
+ /* Check that we have not done too many questions / hour */
+ if (uc->user_resources.questions &&
+ uc->questions++ >= uc->user_resources.questions)
+ {
+ net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user, "max_questions",
+ (long) uc->user_resources.questions);
+ error=1;
+ goto end;
+ }
+ if (check_command < (uint) SQLCOM_END)
+ {
+ /* Check that we have not done too many updates / hour */
+ if (uc->user_resources.updates &&
+ (sql_command_flags[check_command] & CF_CHANGES_DATA) &&
+ uc->updates++ >= uc->user_resources.updates)
+ {
+ net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user, "max_updates",
+ (long) uc->user_resources.updates);
+ error=1;
+ goto end;
+ }
+ }
+end:
+ (void) pthread_mutex_unlock(&LOCK_user_conn);
+ DBUG_RETURN(error);
+}
+
+#endif /* NO_EMBEDDED_ACCESS_CHECKS */
+
+
+static void reset_mqh(LEX_USER *lu, bool get_them= 0)
+{
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ (void) pthread_mutex_lock(&LOCK_user_conn);
+ if (lu) // for GRANT
+ {
+ USER_CONN *uc;
+ uint temp_len=lu->user.length+lu->host.length+2;
+ char temp_user[USER_HOST_BUFF_SIZE];
+
+ memcpy(temp_user,lu->user.str,lu->user.length);
+ memcpy(temp_user+lu->user.length+1,lu->host.str,lu->host.length);
+ temp_user[lu->user.length]='\0'; temp_user[temp_len-1]=0;
+ if ((uc = (struct user_conn *) hash_search(&hash_user_connections,
+ (byte*) temp_user, temp_len)))
+ {
+ uc->questions=0;
+ get_mqh(temp_user,&temp_user[lu->user.length+1],uc);
+ uc->updates=0;
+ uc->conn_per_hour=0;
+ }
+ }
+ else
+ {
+ /* for FLUSH PRIVILEGES and FLUSH USER_RESOURCES */
+ for (uint idx=0;idx < hash_user_connections.records; idx++)
+ {
+ USER_CONN *uc=(struct user_conn *) hash_element(&hash_user_connections,
+ idx);
+ if (get_them)
+ get_mqh(uc->user,uc->host,uc);
+ uc->questions=0;
+ uc->updates=0;
+ uc->conn_per_hour=0;
+ }
+ }
+ (void) pthread_mutex_unlock(&LOCK_user_conn);
+#endif /* NO_EMBEDDED_ACCESS_CHECKS */
+}
+
+void thd_init_client_charset(THD *thd, uint cs_number)
+{
+ /*
+ Use server character set and collation if
+ - opt_character_set_client_handshake is not set
+ - client has not specified a character set
+ - client character set is the same as the servers
+ - client character set doesn't exists in server
+ */
+ if (!opt_character_set_client_handshake ||
+ !(thd->variables.character_set_client= get_charset(cs_number, MYF(0))) ||
+ !my_strcasecmp(&my_charset_latin1,
+ global_system_variables.character_set_client->name,
+ thd->variables.character_set_client->name))
+ {
+ thd->variables.character_set_client=
+ global_system_variables.character_set_client;
+ thd->variables.collation_connection=
+ global_system_variables.collation_connection;
+ thd->variables.character_set_results=
+ global_system_variables.character_set_results;
+ }
+ else
+ {
+ thd->variables.character_set_results=
+ thd->variables.collation_connection=
+ thd->variables.character_set_client;
+ }
+}
+
+
+/*
+ Perform handshake, authorize client and update thd ACL variables.
+ SYNOPSIS
+ check_connection()
+ thd thread handle
+
+ RETURN
+ 0 success, OK is sent to user, thd is updated.
+ -1 error, which is sent to user
+ > 0 error code (not sent to user)
+*/
+
+#ifndef EMBEDDED_LIBRARY
+static int check_connection(THD *thd)
+{
+ uint connect_errors= 0;
+ NET *net= &thd->net;
+ ulong pkt_len= 0;
+ char *end;
+
+ DBUG_PRINT("info",
+ ("New connection received on %s", vio_description(net->vio)));
+#ifdef SIGNAL_WITH_VIO_CLOSE
+ thd->set_active_vio(net->vio);
+#endif
+
+ if (!thd->main_security_ctx.host) // If TCP/IP connection
+ {
+ char ip[30];
+
+ if (vio_peer_addr(net->vio, ip, &thd->peer_port))
+ return (ER_BAD_HOST_ERROR);
+ if (!(thd->main_security_ctx.ip= my_strdup(ip,MYF(0))))
+ return (ER_OUT_OF_RESOURCES);
+ thd->main_security_ctx.host_or_ip= thd->main_security_ctx.ip;
+ vio_in_addr(net->vio,&thd->remote.sin_addr);
+ if (!(specialflag & SPECIAL_NO_RESOLVE))
+ {
+ vio_in_addr(net->vio,&thd->remote.sin_addr);
+ thd->main_security_ctx.host=
+ ip_to_hostname(&thd->remote.sin_addr, &connect_errors);
+ /* Cut very long hostnames to avoid possible overflows */
+ if (thd->main_security_ctx.host)
+ {
+ if (thd->main_security_ctx.host != my_localhost)
+ thd->main_security_ctx.host[min(strlen(thd->main_security_ctx.host),
+ HOSTNAME_LENGTH)]= 0;
+ thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host;
+ }
+ if (connect_errors > max_connect_errors)
+ return(ER_HOST_IS_BLOCKED);
+ }
+ DBUG_PRINT("info",("Host: %s ip: %s",
+ (thd->main_security_ctx.host ?
+ thd->main_security_ctx.host : "unknown host"),
+ (thd->main_security_ctx.ip ?
+ thd->main_security_ctx.ip : "unknown ip")));
+ if (acl_check_host(thd->main_security_ctx.host, thd->main_security_ctx.ip))
+ return(ER_HOST_NOT_PRIVILEGED);
+ }
+ else /* Hostname given means that the connection was on a socket */
+ {
+ DBUG_PRINT("info",("Host: %s", thd->main_security_ctx.host));
+ thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host;
+ thd->main_security_ctx.ip= 0;
+ /* Reset sin_addr */
+ bzero((char*) &thd->remote, sizeof(thd->remote));
+ }
+ vio_keepalive(net->vio, TRUE);
+ {
+ /* buff[] needs to big enough to hold the server_version variable */
+ char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH + 64];
+ ulong client_flags = (CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB |
+ CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION);
+
+ if (opt_using_transactions)
+ client_flags|=CLIENT_TRANSACTIONS;
+#ifdef HAVE_COMPRESS
+ client_flags |= CLIENT_COMPRESS;
+#endif /* HAVE_COMPRESS */
+#ifdef HAVE_OPENSSL
+ if (ssl_acceptor_fd)
+ client_flags |= CLIENT_SSL; /* Wow, SSL is available! */
+#endif /* HAVE_OPENSSL */
+
+ end= strnmov(buff, server_version, SERVER_VERSION_LENGTH) + 1;
+ int4store((uchar*) end, thd->thread_id);
+ end+= 4;
+ /*
+ So as check_connection is the only entry point to authorization
+ procedure, scramble is set here. This gives us new scramble for
+ each handshake.
+ */
+ create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand);
+ /*
+ Old clients does not understand long scrambles, but can ignore packet
+ tail: that's why first part of the scramble is placed here, and second
+ part at the end of packet.
+ */
+ end= strmake(end, thd->scramble, SCRAMBLE_LENGTH_323) + 1;
+
+ int2store(end, client_flags);
+ /* write server characteristics: up to 16 bytes allowed */
+ end[2]=(char) default_charset_info->number;
+ int2store(end+3, thd->server_status);
+ bzero(end+5, 13);
+ end+= 18;
+ /* write scramble tail */
+ end= strmake(end, thd->scramble + SCRAMBLE_LENGTH_323,
+ SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323) + 1;
+
+ /* At this point we write connection message and read reply */
+ if (net_write_command(net, (uchar) protocol_version, "", 0, buff,
+ (uint) (end-buff)) ||
+ (pkt_len= my_net_read(net)) == packet_error ||
+ pkt_len < MIN_HANDSHAKE_SIZE)
+ {
+ inc_host_errors(&thd->remote.sin_addr);
+ return(ER_HANDSHAKE_ERROR);
+ }
+ }
+#ifdef _CUSTOMCONFIG_
+#include "_cust_sql_parse.h"
+#endif
+ if (connect_errors)
+ reset_host_errors(&thd->remote.sin_addr);
+ if (thd->packet.alloc(thd->variables.net_buffer_length))
+ return(ER_OUT_OF_RESOURCES);
+
+ thd->client_capabilities=uint2korr(net->read_pos);
+ if (thd->client_capabilities & CLIENT_PROTOCOL_41)
+ {
+ thd->client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16;
+ thd->max_client_packet_length= uint4korr(net->read_pos+4);
+ DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8]));
+ thd_init_client_charset(thd, (uint) net->read_pos[8]);
+ thd->update_charset();
+ end= (char*) net->read_pos+32;
+ }
+ else
+ {
+ thd->max_client_packet_length= uint3korr(net->read_pos+2);
+ end= (char*) net->read_pos+5;
+ }
+
+ if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
+ thd->variables.sql_mode|= MODE_IGNORE_SPACE;
+#ifdef HAVE_OPENSSL
+ DBUG_PRINT("info", ("client capabilities: %lu", thd->client_capabilities));
+ if (thd->client_capabilities & CLIENT_SSL)
+ {
+ /* Do the SSL layering. */
+ if (!ssl_acceptor_fd)
+ {
+ inc_host_errors(&thd->remote.sin_addr);
+ return(ER_HANDSHAKE_ERROR);
+ }
+ DBUG_PRINT("info", ("IO layer change in progress..."));
+ if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout))
+ {
+ DBUG_PRINT("error", ("Failed to accept new SSL connection"));
+ inc_host_errors(&thd->remote.sin_addr);
+ return(ER_HANDSHAKE_ERROR);
+ }
+ DBUG_PRINT("info", ("Reading user information over SSL layer"));
+ if ((pkt_len= my_net_read(net)) == packet_error ||
+ pkt_len < NORMAL_HANDSHAKE_SIZE)
+ {
+ DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
+ pkt_len));
+ inc_host_errors(&thd->remote.sin_addr);
+ return(ER_HANDSHAKE_ERROR);
+ }
+ }
+#endif
+
+ if (end >= (char*) net->read_pos+ pkt_len +2)
+ {
+ inc_host_errors(&thd->remote.sin_addr);
+ return(ER_HANDSHAKE_ERROR);
+ }
+
+ if (thd->client_capabilities & CLIENT_INTERACTIVE)
+ thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
+ if ((thd->client_capabilities & CLIENT_TRANSACTIONS) &&
+ opt_using_transactions)
+ net->return_status= &thd->server_status;
+
+ char *user= end;
+ char *passwd= strend(user)+1;
+ uint user_len= passwd - user - 1;
+ char *db= passwd;
+ char db_buff[NAME_LEN + 1]; // buffer to store db in utf8
+ char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8
+ uint dummy_errors;
+
+ /*
+ Old clients send null-terminated string as password; new clients send
+ the size (1 byte) + string (not null-terminated). Hence in case of empty
+ password both send '\0'.
+
+ This strlen() can't be easily deleted without changing protocol.
+ */
+ uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
+ *passwd++ : strlen(passwd);
+ db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
+ db + passwd_len + 1 : 0;
+ /* strlen() can't be easily deleted without changing protocol */
+ uint db_len= db ? strlen(db) : 0;
+
+ if (passwd + passwd_len + db_len > (char *)net->read_pos + pkt_len)
+ {
+ inc_host_errors(&thd->remote.sin_addr);
+ return ER_HANDSHAKE_ERROR;
+ }
+
+ /* Since 4.1 all database names are stored in utf8 */
+ if (db)
+ {
+ db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1,
+ system_charset_info,
+ db, db_len,
+ thd->charset(), &dummy_errors)]= 0;
+ db= db_buff;
+ }
+
+ user_buff[user_len= copy_and_convert(user_buff, sizeof(user_buff)-1,
+ system_charset_info, user, user_len,
+ thd->charset(), &dummy_errors)]= '\0';
+ user= user_buff;
+
+ /* If username starts and ends in "'", chop them off */
+ if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'')
+ {
+ user[user_len-1]= 0;
+ user++;
+ user_len-= 2;
+ }
+
+ if (thd->main_security_ctx.user)
+ x_free(thd->main_security_ctx.user);
+ if (!(thd->main_security_ctx.user= my_strdup(user, MYF(0))))
+ return (ER_OUT_OF_RESOURCES);
+ return check_user(thd, COM_CONNECT, passwd, passwd_len, db, TRUE);
+}
+
void execute_init_command(THD *thd, sys_var_str *init_command_var,
rw_lock_t *var_mutex)
@@ -290,6 +1129,149 @@ void execute_init_command(THD *thd, sys_var_str *init_command_var,
}
+pthread_handler_t handle_one_connection(void *arg)
+{
+ THD *thd=(THD*) arg;
+ uint launch_time =
+ (uint) ((thd->thr_create_time = time(NULL)) - thd->connect_time);
+ if (launch_time >= slow_launch_time)
+ statistic_increment(slow_launch_threads,&LOCK_status );
+
+ pthread_detach_this_thread();
+
+#if !defined( __WIN__) // Win32 calls this in pthread_create
+ /* The following calls needs to be done before we call DBUG_ macros */
+ if (!(test_flags & TEST_NO_THREADS) & my_thread_init())
+ {
+ close_connection(thd, ER_OUT_OF_RESOURCES, 1);
+ statistic_increment(aborted_connects,&LOCK_status);
+ end_thread(thd,0);
+ return 0;
+ }
+#endif
+
+ /*
+ handle_one_connection() is the only way a thread would start
+ and would always be on top of the stack, therefore, the thread
+ stack always starts at the address of the first local variable
+ of handle_one_connection, which is thd. We need to know the
+ start of the stack so that we could check for stack overruns.
+ */
+ DBUG_PRINT("info", ("handle_one_connection called by thread %lu\n",
+ thd->thread_id));
+ /* now that we've called my_thread_init(), it is safe to call DBUG_* */
+
+#if defined(__WIN__)
+ init_signals();
+#elif !defined(__NETWARE__)
+ sigset_t set;
+ VOID(sigemptyset(&set)); // Get mask in use
+ VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
+#endif
+ thd->thread_stack= (char*) &thd;
+ if (thd->store_globals())
+ {
+ close_connection(thd, ER_OUT_OF_RESOURCES, 1);
+ statistic_increment(aborted_connects,&LOCK_status);
+ end_thread(thd,0);
+ return 0;
+ }
+
+ do
+ {
+ int error;
+ NET *net= &thd->net;
+ Security_context *sctx= thd->security_ctx;
+ net->no_send_error= 0;
+
+ /* Use "connect_timeout" value during connection phase */
+ net_set_read_timeout(net, connect_timeout);
+ net_set_write_timeout(net, connect_timeout);
+
+ if ((error=check_connection(thd)))
+ { // Wrong permissions
+ if (error > 0)
+ net_printf_error(thd, error, sctx->host_or_ip);
+#ifdef __NT__
+ if (vio_type(net->vio) == VIO_TYPE_NAMEDPIPE)
+ my_sleep(1000); /* must wait after eof() */
+#endif
+ statistic_increment(aborted_connects,&LOCK_status);
+ goto end_thread;
+ }
+#ifdef __NETWARE__
+ netware_reg_user(sctx->ip, sctx->user, "MySQL");
+#endif
+ if (thd->variables.max_join_size == HA_POS_ERROR)
+ thd->options |= OPTION_BIG_SELECTS;
+ if (thd->client_capabilities & CLIENT_COMPRESS)
+ net->compress=1; // Use compression
+
+ thd->version= refresh_version;
+ thd->proc_info= 0;
+ thd->command= COM_SLEEP;
+ thd->init_for_queries();
+
+ if (sys_init_connect.value_length && !(sctx->master_access & SUPER_ACL))
+ {
+ execute_init_command(thd, &sys_init_connect, &LOCK_sys_init_connect);
+ if (thd->query_error)
+ {
+ thd->killed= THD::KILL_CONNECTION;
+ sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION),
+ thd->thread_id,(thd->db ? thd->db : "unconnected"),
+ sctx->user ? sctx->user : "unauthenticated",
+ sctx->host_or_ip, "init_connect command failed");
+ sql_print_warning("%s", net->last_error);
+ }
+ }
+
+ /* Connect completed, set read/write timeouts back to default */
+ net_set_read_timeout(net, thd->variables.net_read_timeout);
+ net_set_write_timeout(net, thd->variables.net_write_timeout);
+
+ while (!net->error && net->vio != 0 &&
+ !(thd->killed == THD::KILL_CONNECTION))
+ {
+ net->no_send_error= 0;
+ if (do_command(thd))
+ break;
+ }
+ if (thd->user_connect)
+ decrease_user_connections(thd->user_connect);
+ if (net->error && net->vio != 0 && net->report_error)
+ {
+ if (!thd->killed && thd->variables.log_warnings > 1)
+ sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION),
+ thd->thread_id,(thd->db ? thd->db : "unconnected"),
+ sctx->user ? sctx->user : "unauthenticated",
+ sctx->host_or_ip,
+ (net->last_errno ? ER(net->last_errno) :
+ ER(ER_UNKNOWN_ERROR)));
+ net_send_error(thd, net->last_errno, NullS);
+ statistic_increment(aborted_threads,&LOCK_status);
+ }
+ else if (thd->killed)
+ {
+ statistic_increment(aborted_threads,&LOCK_status);
+ }
+
+end_thread:
+ close_connection(thd, 0, 1);
+ end_thread(thd,1);
+ /*
+ If end_thread returns, we are either running with --one-thread
+ or this thread has been schedule to handle the next query
+ */
+ thd= current_thd;
+ thd->thread_stack= (char*) &thd;
+ } while (!(test_flags & TEST_NO_THREADS));
+ /* The following is only executed if we are not using --one-thread */
+ return(0); /* purecov: deadcode */
+}
+
+#endif /* EMBEDDED_LIBRARY */
+
/*
Execute commands from bootstrap_file.
Used when creating the initial grant tables
@@ -316,6 +1298,11 @@ pthread_handler_t handle_bootstrap(void *arg)
#ifndef EMBEDDED_LIBRARY
pthread_detach_this_thread();
thd->thread_stack= (char*) &thd;
+#if !defined(__WIN__) && !defined(__NETWARE__)
+ sigset_t set;
+ VOID(sigemptyset(&set)); // Get mask in use
+ VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
+#endif
#endif /* EMBEDDED_LIBRARY */
if (thd->variables.max_join_size == HA_POS_ERROR)
@@ -373,6 +1360,7 @@ pthread_handler_t handle_bootstrap(void *arg)
mode we have only one thread.
*/
thd->query_id=next_query_id();
+ thd->set_time();
mysql_parse(thd,thd->query,length);
close_thread_tables(thd); // Free tables
if (thd->is_fatal_error)
@@ -1094,6 +2082,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
*/
enum mysql_enum_shutdown_level level=
(enum mysql_enum_shutdown_level) (uchar) packet[0];
+ DBUG_PRINT("quit",("Got shutdown command for level %u", level));
if (level == SHUTDOWN_DEFAULT)
level= SHUTDOWN_WAIT_ALL_BUFFERS; // soon default will be configurable
else if (level != SHUTDOWN_WAIT_ALL_BUFFERS)
@@ -1512,6 +2501,91 @@ static void reset_one_shot_variables(THD *thd)
}
+static
+bool sp_process_definer(THD *thd)
+{
+ DBUG_ENTER("sp_process_definer");
+
+ LEX *lex= thd->lex;
+
+ /*
+ If the definer is not specified, this means that CREATE-statement missed
+ DEFINER-clause. DEFINER-clause can be missed in two cases:
+
+ - The user submitted a statement w/o the clause. This is a normal
+ case, we should assign CURRENT_USER as definer.
+
+ - Our slave received an updated from the master, that does not
+ replicate definer for stored rountines. We should also assign
+ CURRENT_USER as definer here, but also we should mark this routine
+ as NON-SUID. This is essential for the sake of backward
+ compatibility.
+
+ The problem is the slave thread is running under "special" user (@),
+ that actually does not exist. In the older versions we do not fail
+ execution of a stored routine if its definer does not exist and
+ continue the execution under the authorization of the invoker
+ (BUG#13198). And now if we try to switch to slave-current-user (@),
+ we will fail.
+
+ Actually, this leads to the inconsistent state of master and
+ slave (different definers, different SUID behaviour), but it seems,
+ this is the best we can do.
+ */
+
+ if (!lex->definer)
+ {
+ Query_arena original_arena;
+ Query_arena *ps_arena= thd->activate_stmt_arena_if_needed(&original_arena);
+
+ lex->definer= create_default_definer(thd);
+
+ if (ps_arena)
+ thd->restore_active_arena(ps_arena, &original_arena);
+
+ /* Error has been already reported. */
+ if (lex->definer == NULL)
+ DBUG_RETURN(TRUE);
+
+ if (thd->slave_thread)
+ lex->sphead->m_chistics->suid= SP_IS_NOT_SUID;
+ }
+ else
+ {
+ /*
+ If the specified definer differs from the current user, we
+ should check that the current user has SUPER privilege (in order
+ to create a stored routine under another user one must have
+ SUPER privilege).
+ */
+ if ((strcmp(lex->definer->user.str, thd->security_ctx->priv_user) ||
+ my_strcasecmp(system_charset_info, lex->definer->host.str,
+ thd->security_ctx->priv_host)) &&
+ check_global_access(thd, SUPER_ACL))
+ {
+ my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
+ DBUG_RETURN(TRUE);
+ }
+ }
+
+ /* Check that the specified definer exists. Emit a warning if not. */
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (!is_acl_user(lex->definer->host.str, lex->definer->user.str))
+ {
+ push_warning_printf(thd,
+ MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_NO_SUCH_USER,
+ ER(ER_NO_SUCH_USER),
+ lex->definer->user.str,
+ lex->definer->host.str);
+ }
+#endif /* NO_EMBEDDED_ACCESS_CHECKS */
+
+ DBUG_RETURN(FALSE);
+}
+
+
/*
Execute command saved in thd and lex->sql_command
@@ -3015,6 +4089,11 @@ end_with_restore_list:
"function calls as part of this statement");
break;
}
+
+ res= sp_process_definer(thd);
+ if (res)
+ break;
+
switch (lex->sql_command) {
case SQLCOM_CREATE_EVENT:
res= Events::get_instance()->
@@ -3069,8 +4148,7 @@ end_with_restore_list:
if (!(res= Events::get_instance()->drop_event(thd,
lex->spname->m_db,
lex->spname->m_name,
- lex->drop_if_exists,
- FALSE)))
+ lex->drop_if_exists)))
send_ok(thd);
}
break;
@@ -3238,7 +4316,7 @@ end_with_restore_list:
{
if (!(user= get_current_user(thd, tmp_user)))
goto error;
- reset_mqh(user, 0);
+ reset_mqh(user);
}
}
}
@@ -3268,9 +4346,7 @@ end_with_restore_list:
We WANT to write and we CAN write.
! we write after unlocking the table.
*/
- /*
- Presumably, RESET and binlog writing doesn't require synchronization
- */
+ /* Presumably, RESET and binlog writing doesn't require synchronization */
if (!lex->no_write_to_binlog && write_to_binlog)
{
if (mysql_bin_log.is_open())
@@ -3512,83 +4588,8 @@ end_with_restore_list:
}
#endif
- /*
- If the definer is not specified, this means that CREATE-statement missed
- DEFINER-clause. DEFINER-clause can be missed in two cases:
-
- - The user submitted a statement w/o the clause. This is a normal
- case, we should assign CURRENT_USER as definer.
-
- - Our slave received an updated from the master, that does not
- replicate definer for stored rountines. We should also assign
- CURRENT_USER as definer here, but also we should mark this routine
- as NON-SUID. This is essential for the sake of backward
- compatibility.
-
- The problem is the slave thread is running under "special" user (@),
- that actually does not exist. In the older versions we do not fail
- execution of a stored routine if its definer does not exist and
- continue the execution under the authorization of the invoker
- (BUG#13198). And now if we try to switch to slave-current-user (@),
- we will fail.
-
- Actually, this leads to the inconsistent state of master and
- slave (different definers, different SUID behaviour), but it seems,
- this is the best we can do.
- */
-
- if (!lex->definer)
- {
- bool local_res= FALSE;
- Query_arena original_arena;
- Query_arena *ps_arena = thd->activate_stmt_arena_if_needed(&original_arena);
-
- if (!(lex->definer= create_default_definer(thd)))
- local_res= TRUE;
-
- if (ps_arena)
- thd->restore_active_arena(ps_arena, &original_arena);
-
- /* Error has been already reported. */
- if (local_res)
- goto create_sp_error;
-
- if (thd->slave_thread)
- lex->sphead->m_chistics->suid= SP_IS_NOT_SUID;
- }
-
- /*
- If the specified definer differs from the current user, we should check
- that the current user has SUPER privilege (in order to create a stored
- routine under another user one must have SUPER privilege).
- */
-
- else if (strcmp(lex->definer->user.str, thd->security_ctx->priv_user) ||
- my_strcasecmp(system_charset_info,
- lex->definer->host.str,
- thd->security_ctx->priv_host))
- {
- if (check_global_access(thd, SUPER_ACL))
- {
- my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
- goto create_sp_error;
- }
- }
-
- /* Check that the specified definer exists. Emit a warning if not. */
-
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (!is_acl_user(lex->definer->host.str,
- lex->definer->user.str))
- {
- push_warning_printf(thd,
- MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_NO_SUCH_USER,
- ER(ER_NO_SUCH_USER),
- lex->definer->user.str,
- lex->definer->host.str);
- }
-#endif /* NO_EMBEDDED_ACCESS_CHECKS */
+ if (sp_process_definer(thd))
+ goto create_sp_error;
res= (sp_result= lex->sphead->create(thd));
switch (sp_result) {
@@ -3850,7 +4851,7 @@ create_sp_error:
ER(ER_PROC_AUTO_REVOKE_FAIL));
}
#endif
- /* Conditionally writes to binlog */
+ /* Conditionally writes to binlog */
if (lex->sql_command == SQLCOM_DROP_PROCEDURE)
sp_result= sp_drop_procedure(thd, lex->spname);
else
@@ -5590,7 +6591,6 @@ bool st_select_lex::init_nested_join(THD *thd)
join_list->push_front(ptr);
ptr->embedding= embedding;
ptr->join_list= join_list;
- ptr->alias= (char*) "(nested_join)";
embedding= ptr;
join_list= &nested_join->join_list;
join_list->empty();
@@ -5675,7 +6675,6 @@ TABLE_LIST *st_select_lex::nest_last_join(THD *thd)
ptr->embedding= embedding;
ptr->join_list= join_list;
- ptr->alias= (char*) "(nest_last_join)";
embedded_list= &nested_join->join_list;
embedded_list->empty();
@@ -6170,7 +7169,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
}
#endif
if (options & REFRESH_USER_RESOURCES)
- reset_mqh((LEX_USER *) NULL, 0); /* purecov: inspected */
+ reset_mqh((LEX_USER *) NULL);
*write_to_binlog= tmp_write_to_binlog;
return result;
}
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 297bd9f2737..9948e4ab98c 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -76,19 +76,6 @@ const LEX_STRING null_lex_str={0,0};
#define __attribute__(X)
#endif
-/* Helper for parsing "IS [NOT] truth_value" */
-inline Item *is_truth_value(THD *thd, Item *A, bool v1, bool v2)
-{
- Item *v1_t= new (thd->mem_root) Item_int((char *) (v1 ? "TRUE" : "FALSE"),
- v1, 1);
- Item *v1_f= new (thd->mem_root) Item_int((char *) (v1 ? "FALSE" : "TRUE"),
- !v1, 1);
- Item *v2_t= new (thd->mem_root) Item_int((char *) (v2 ? "TRUE" : "FALSE"),
- v2, 1);
- Item *ifnull= new (thd->mem_root) Item_func_ifnull(A, v2_t);
-
- return new (thd->mem_root) Item_func_if(ifnull, v1_t, v1_f);
-}
#ifndef DBUG_OFF
#define YYDEBUG 1
@@ -310,6 +297,81 @@ void case_stmt_action_end_case(LEX *lex, bool simple)
lex->sphead->do_cont_backpatch();
}
+/**
+ Helper to resolve the SQL:2003 Syntax exception 1) in <in predicate>.
+ See SQL:2003, Part 2, section 8.4 <in predicate>, Note 184, page 383.
+ This function returns the proper item for the SQL expression
+ <code>left [NOT] IN ( expr )</code>
+ @param thd the current thread
+ @param left the in predicand
+ @param equal true for IN predicates, false for NOT IN predicates
+ @param expr first and only expression of the in value list
+ @return an expression representing the IN predicate.
+*/
+Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal,
+ Item *expr)
+{
+ /*
+ Relevant references for this issue:
+ - SQL:2003, Part 2, section 8.4 <in predicate>, page 383,
+ - SQL:2003, Part 2, section 7.2 <row value expression>, page 296,
+ - SQL:2003, Part 2, section 6.3 <value expression primary>, page 174,
+ - SQL:2003, Part 2, section 7.15 <subquery>, page 370,
+ - SQL:2003 Feature F561, "Full value expressions".
+
+ The exception in SQL:2003 Note 184 means:
+ Item_singlerow_subselect, which corresponds to a <scalar subquery>,
+ should be re-interpreted as an Item_in_subselect, which corresponds
+ to a <table subquery> when used inside an <in predicate>.
+
+ Our reading of Note 184 is reccursive, so that all:
+ - IN (( <subquery> ))
+ - IN ((( <subquery> )))
+ - IN '('^N <subquery> ')'^N
+ - etc
+ should be interpreted as a <table subquery>, no matter how deep in the
+ expression the <subquery> is.
+ */
+
+ Item *result;
+
+ DBUG_ENTER("handle_sql2003_note184_exception");
+
+ if (expr->type() == Item::SUBSELECT_ITEM)
+ {
+ Item_subselect *expr2 = (Item_subselect*) expr;
+
+ if (expr2->substype() == Item_subselect::SINGLEROW_SUBS)
+ {
+ Item_singlerow_subselect *expr3 = (Item_singlerow_subselect*) expr2;
+ st_select_lex *subselect;
+
+ /*
+ Implement the mandated change, by altering the semantic tree:
+ left IN Item_singlerow_subselect(subselect)
+ is modified to
+ left IN (subselect)
+ which is represented as
+ Item_in_subselect(left, subselect)
+ */
+ subselect= expr3->invalidate_and_restore_select_lex();
+ result= new (thd->mem_root) Item_in_subselect(left, subselect);
+
+ if (! equal)
+ result = negate_expression(thd, result);
+
+ DBUG_RETURN(result);
+ }
+ }
+
+ if (equal)
+ result= new (thd->mem_root) Item_func_eq(left, expr);
+ else
+ result= new (thd->mem_root) Item_func_ne(left, expr);
+
+ DBUG_RETURN(result);
+}
+
%}
%union {
int num;
@@ -1043,7 +1105,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <interval_time_st> interval_time_st
-%type <db_type> storage_engines
+%type <db_type> storage_engines known_storage_engines
%type <row_type> row_types
@@ -4248,19 +4310,31 @@ default_collation:
Lex->create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET;
};
+known_storage_engines:
+ ident_or_text
+ {
+ $$ = ha_resolve_by_name(YYTHD, &$1);
+ if ($$ == NULL)
+ {
+ my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), $1.str);
+ YYABORT;
+ }
+ }
+ ;
+
storage_engines:
ident_or_text
{
$$ = ha_resolve_by_name(YYTHD, &$1);
if ($$ == NULL)
- if (YYTHD->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION)
- {
- my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), $1.str);
- YYABORT;
- }
- else
{
- push_warning_printf(YYTHD, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ if (YYTHD->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION)
+ {
+ my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), $1.str);
+ YYABORT;
+ }
+
+ push_warning_printf(YYTHD, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_UNKNOWN_STORAGE_ENGINE,
ER(ER_UNKNOWN_STORAGE_ENGINE), $1.str);
}
@@ -5015,7 +5089,7 @@ alter:
}
view_list_opt AS view_select view_check_option
{}
- | ALTER EVENT_SYM sp_name
+ | ALTER definer EVENT_SYM sp_name
/*
BE CAREFUL when you add a new rule to update the block where
YYTHD->client_capabilities is set back to original value
@@ -5031,7 +5105,7 @@ alter:
if (!(Lex->event_parse_data= Event_parse_data::new_instance(YYTHD)))
YYABORT;
- Lex->event_parse_data->identifier= $3;
+ Lex->event_parse_data->identifier= $4;
/*
We have to turn off CLIENT_MULTI_QUERIES while parsing a
@@ -5051,13 +5125,14 @@ alter:
{
/*
$1 - ALTER
- $2 - EVENT_SYM
- $3 - sp_name
- $4 - the block above
+ $2 - definer
+ $3 - EVENT_SYM
+ $4 - sp_name
+ $5 - the block above
*/
- YYTHD->client_capabilities |= $<ulong_num>4;
+ YYTHD->client_capabilities |= $<ulong_num>5;
- if (!($5 || $6 || $7 || $8 || $9))
+ if (!($6 || $7 || $8 || $9 || $10))
{
yyerror(ER(ER_SYNTAX_ERROR));
YYABORT;
@@ -6140,13 +6215,18 @@ bool_factor:
| bool_test ;
bool_test:
- bool_pri IS TRUE_SYM { $$= is_truth_value(YYTHD, $1,1,0); }
- | bool_pri IS not TRUE_SYM { $$= is_truth_value(YYTHD, $1,0,0); }
- | bool_pri IS FALSE_SYM { $$= is_truth_value(YYTHD, $1,0,1); }
- | bool_pri IS not FALSE_SYM { $$= is_truth_value(YYTHD, $1,1,1); }
- | bool_pri IS UNKNOWN_SYM { $$= new Item_func_isnull($1); }
- | bool_pri IS not UNKNOWN_SYM { $$= new Item_func_isnotnull($1); }
- | bool_pri ;
+ bool_pri IS TRUE_SYM
+ { $$= new (YYTHD->mem_root) Item_func_istrue($1); }
+ | bool_pri IS not TRUE_SYM
+ { $$= new (YYTHD->mem_root) Item_func_isnottrue($1); }
+ | bool_pri IS FALSE_SYM
+ { $$= new (YYTHD->mem_root) Item_func_isfalse($1); }
+ | bool_pri IS not FALSE_SYM
+ { $$= new (YYTHD->mem_root) Item_func_isnotfalse($1); }
+ | bool_pri IS UNKNOWN_SYM { $$= new Item_func_isnull($1); }
+ | bool_pri IS not UNKNOWN_SYM { $$= new Item_func_isnotnull($1); }
+ | bool_pri
+ ;
bool_pri:
bool_pri IS NULL_SYM { $$= new Item_func_isnull($1); }
@@ -6159,31 +6239,37 @@ bool_pri:
| predicate ;
predicate:
- bit_expr IN_SYM '(' subselect ')'
- { $$= new Item_in_subselect($1, $4); }
- | bit_expr not IN_SYM '(' subselect ')'
- { $$= negate_expression(YYTHD, new Item_in_subselect($1, $5)); }
+ bit_expr IN_SYM '(' subselect ')'
+ {
+ $$= new (YYTHD->mem_root) Item_in_subselect($1, $4);
+ }
+ | bit_expr not IN_SYM '(' subselect ')'
+ {
+ THD *thd= YYTHD;
+ Item *item= new (thd->mem_root) Item_in_subselect($1, $5);
+ $$= negate_expression(thd, item);
+ }
| bit_expr IN_SYM '(' expr ')'
{
- $$= new Item_func_eq($1, $4);
+ $$= handle_sql2003_note184_exception(YYTHD, $1, true, $4);
}
- | bit_expr IN_SYM '(' expr ',' expr_list ')'
- {
- $6->push_front($4);
- $6->push_front($1);
- $$= new Item_func_in(*$6);
+ | bit_expr IN_SYM '(' expr ',' expr_list ')'
+ {
+ $6->push_front($4);
+ $6->push_front($1);
+ $$= new (YYTHD->mem_root) Item_func_in(*$6);
}
| bit_expr not IN_SYM '(' expr ')'
{
- $$= new Item_func_ne($1, $5);
+ $$= handle_sql2003_note184_exception(YYTHD, $1, false, $5);
}
- | bit_expr not IN_SYM '(' expr ',' expr_list ')'
+ | bit_expr not IN_SYM '(' expr ',' expr_list ')'
{
- $7->push_front($5);
- $7->push_front($1);
- Item_func_in *item = new Item_func_in(*$7);
- item->negate();
- $$= item;
+ $7->push_front($5);
+ $7->push_front($1);
+ Item_func_in *item = new (YYTHD->mem_root) Item_func_in(*$7);
+ item->negate();
+ $$= item;
}
| bit_expr BETWEEN_SYM bit_expr AND_SYM predicate
{ $$= new Item_func_between($1,$3,$5); }
@@ -8457,12 +8543,10 @@ show_param:
if (prepare_schema_table(YYTHD, lex, 0, SCH_PLUGINS))
YYABORT;
}
- | ENGINE_SYM storage_engines
+ | ENGINE_SYM known_storage_engines show_engine_param
{ Lex->create_info.db_type= $2; }
- show_engine_param
- | ENGINE_SYM ALL
+ | ENGINE_SYM ALL show_engine_param
{ Lex->create_info.db_type= NULL; }
- show_engine_param
| opt_full COLUMNS from_or_in table_ident opt_db wild_and_where
{
LEX *lex= Lex;
diff --git a/sql/table.cc b/sql/table.cc
index ed3cac85214..0fe1f37e9b1 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -120,7 +120,6 @@ TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key,
share->normalized_path.length= path_length;
share->version= refresh_version;
- share->flush_version= flush_version;
/*
This constant is used to mark that no table map version has been
diff --git a/sql/table.h b/sql/table.h
index fc2f25f3aa8..96a101315a2 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -168,7 +168,7 @@ typedef struct st_table_share
ha_rows min_rows, max_rows; /* create information */
ulong avg_row_length; /* create information */
ulong raid_chunksize;
- ulong version, flush_version, mysql_version;
+ ulong version, mysql_version;
ulong timestamp_offset; /* Set to offset+1 of record */
ulong reclength; /* Recordlength */