diff options
153 files changed, 6609 insertions, 1858 deletions
diff --git a/client/mysql_upgrade.c b/client/mysql_upgrade.c index 6d8337abb4d..e916bebb43c 100644 --- a/client/mysql_upgrade.c +++ b/client/mysql_upgrade.c @@ -171,7 +171,7 @@ void set_extra_default(int id, const struct my_option *opt) } d= (extra_default_t *)my_malloc(sizeof(extra_default_t), - MYF(MY_FAE|MY_ZEROFILL)); + MYF(MY_FAE | MY_ZEROFILL)); d->id= id; d->name= opt->name; d->n_len= strlen(opt->name); @@ -345,15 +345,17 @@ static int create_defaults_file(const char *path, const char *forced_path) } dynstr_set(&buf, NULL); } - if (dynstr_append_mem(&buf, "\n", 1) - || dynstr_append_mem(&buf, d->name, d->n_len) - || (d->v_len && (dynstr_append_mem(&buf, "=", 1) - || dynstr_append_mem(&buf, d->value, d->v_len)))) + if (dynstr_append_mem(&buf, "\n", 1) || + dynstr_append_mem(&buf, d->name, d->n_len) || + (d->v_len && (dynstr_append_mem(&buf, "=", 1) || + dynstr_append_mem(&buf, d->value, d->v_len)))) { ret= 1; goto error; } my_delete((gptr)d, MYF(0)); + my_free((gptr) d, MYF(0)); + list_pop(extra_defaults); /* pop off the head */ } if (my_write(defaults_file, buf.str, buf.length, MYF(MY_FNABP | MY_WME))) @@ -451,10 +453,10 @@ int main(int argc, char **argv) char *forced_extra_defaults; char *local_defaults_group_suffix; const char *script_line; - char *upgrade_defaults_path= 0; + char *upgrade_defaults_path= NULL; char *defaults_to_use= NULL; int upgrade_defaults_created= 0; - + int no_defaults; char path[FN_REFLEN]; DYNAMIC_STRING cmdline; @@ -464,6 +466,10 @@ int main(int argc, char **argv) #endif /* Check if we are forced to use specific defaults */ + no_defaults= 0; + if (argc >= 2 && !strcmp(argv[1],"--no-defaults")) + no_defaults= 1; + get_defaults_options(argc, argv, &forced_defaults_file, &forced_extra_defaults, &local_defaults_group_suffix); @@ -578,7 +584,9 @@ int main(int argc, char **argv) if (defaults_to_use) { dynstr_append(&cmdline, " "); - dynstr_append_os_quoted(&cmdline, "--defaults-extra-file=", + dynstr_append_os_quoted(&cmdline, + (no_defaults ? "--defaults-file=" : + "--defaults-extra-file="), defaults_to_use, NullS); } @@ -652,7 +660,9 @@ fix_priv_tables: if (defaults_to_use) { dynstr_append(&cmdline, " "); - dynstr_append_os_quoted(&cmdline, "--defaults-extra-file=", + dynstr_append_os_quoted(&cmdline, + (no_defaults ? "--defaults-file=" : + "--defaults-extra-file="), defaults_to_use, NullS); } dynstr_append(&cmdline, " "); @@ -684,6 +694,7 @@ error: if (upgrade_defaults_created) my_delete(upgrade_defaults_path, MYF(0)); + my_free(upgrade_defaults_path, MYF(MY_ALLOW_ZERO_PTR)); my_end(info_flag ? MY_CHECK_ERROR : 0); return ret; } diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index f2d395bf966..940fac6da38 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -486,18 +486,15 @@ write_event_header_and_base64(Log_event *ev, FILE *result_file, DBUG_ENTER("write_event_header_and_base64"); /* Write header and base64 output to cache */ IO_CACHE result_cache; - if (init_io_cache(&result_cache, -1, 0, WRITE_CACHE, 0L, FALSE, - MYF(MY_WME | MY_NABP))) - { + if (open_cached_file(&result_cache, NULL, NULL, 0, MYF(MY_WME | MY_NABP))) return 1; - } ev->print_header(&result_cache, print_event_info, FALSE); ev->print_base64(&result_cache, print_event_info, FALSE); /* Read data from cache and write to result file */ my_b_copy_to_file(&result_cache, result_file); - end_io_cache(&result_cache); + close_cached_file(&result_cache); DBUG_RETURN(0); } @@ -1016,6 +1013,9 @@ static int dump_log_entries(const char* logname) { int rc; PRINT_EVENT_INFO print_event_info; + + if (!print_event_info.init_ok()) + return 1; /* Set safe delimiter, to dump things like CREATE PROCEDURE safely diff --git a/include/my_global.h b/include/my_global.h index e25752b8ed8..c41ac8f915f 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -1479,4 +1479,11 @@ do { doubleget_union _tmp; \ #define dlerror() "" #endif +/* + Include standard definitions of operator new and delete. + */ +#ifdef __cplusplus +#include <new> +#endif + #endif /* my_global_h */ 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/extra/binlog_tests/binlog.test b/mysql-test/extra/binlog_tests/binlog.test index 48d9b859c26..834edcff474 100644 --- a/mysql-test/extra/binlog_tests/binlog.test +++ b/mysql-test/extra/binlog_tests/binlog.test @@ -67,6 +67,19 @@ create table if not exists t2 select * from t1; create temporary table tt1 (a int); create table if not exists t3 like tt1; +# BUG#25091 (A DELETE statement to mysql database is not logged with +# ROW mode format): Checking that some basic operations on tables in +# the mysql database is replicated even when the current database is +# 'mysql'. + +--disable_warnings +USE mysql; +INSERT INTO user SET host='localhost', user='@#@', password=password('Just a test'); +UPDATE user SET password=password('Another password') WHERE host='localhost' AND user='@#@'; +DELETE FROM user WHERE host='localhost' AND user='@#@'; +--enable_warnings + +use test; --replace_column 2 # 5 # --replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; diff --git a/mysql-test/extra/binlog_tests/ctype_cp932.test b/mysql-test/extra/binlog_tests/ctype_cp932.test index 688d06c4dde..c6196b928b0 100644 --- a/mysql-test/extra/binlog_tests/ctype_cp932.test +++ b/mysql-test/extra/binlog_tests/ctype_cp932.test @@ -413,3 +413,14 @@ select * from t1; insert into t1 values ('abc'); select * from t1; drop table t1; + +# +# Bug#25815 Data truncated for column TEXT +# +set names utf8; +create table t1 (a text) default character set cp932; +insert into t1 values (_utf8 0xE38182); +show warnings; +select * from t1; +select hex(a) from t1; +drop table t1; diff --git a/mysql-test/extra/rpl_tests/rpl_insert_delayed.test b/mysql-test/extra/rpl_tests/rpl_insert_delayed.test new file mode 100644 index 00000000000..2cba7ab64c8 --- /dev/null +++ b/mysql-test/extra/rpl_tests/rpl_insert_delayed.test @@ -0,0 +1,86 @@ +# The two bugs below (BUG#25507 and BUG#26116) existed only in +# statement-based binlogging; we test that now they are fixed; +# we also test that mixed and row-based binlogging work too, +# for completeness. + +connection master; +--disable_warnings +CREATE SCHEMA IF NOT EXISTS mysqlslap; +USE mysqlslap; +--enable_warnings + +select @@global.binlog_format; + +# +# BUG#25507 "multi-row insert delayed + auto increment causes +# duplicate key entries on slave"; +# happened only in statement-based binlogging. +# + +CREATE TABLE t1 (id INT primary key auto_increment, name VARCHAR(64)); +let $query = "INSERT DELAYED INTO t1 VALUES (null, 'Dr. No'), (null, 'From Russia With Love'), (null, 'Goldfinger'), (null, 'Thunderball'), (null, 'You Only Live Twice')"; +--exec $MYSQL_SLAP --silent --concurrency=5 --iterations=200 --query=$query --delimiter=";" + +FLUSH TABLE t1; # another way to be sure INSERT DELAYED has inserted +SELECT COUNT(*) FROM t1; +# when bug existed slave failed below ("duplicate key" error at random INSERT) +sync_slave_with_master; +use mysqlslap; +SELECT COUNT(*) FROM t1; + +# +# BUG#26116 "If multi-row INSERT DELAYED has errors, +# statement-based binlogging breaks"; +# happened only in statement-based binlogging. +# + +connection master; +truncate table t1; +# first scenario: duplicate on first row +insert delayed into t1 values(10, "my name"); +if ($binlog_format_statement) +{ + # statement below will be converted to non-delayed INSERT and so + # will stop at first error, guaranteeing replication. + --error ER_DUP_ENTRY_WITH_KEY_NAME + insert delayed into t1 values(10, "is Bond"), (20, "James Bond"); +} +if (!$binlog_format_statement) +{ + insert delayed into t1 values(10, "is Bond"), (20, "James Bond"); +} +flush table t1; # to wait for INSERT DELAYED to be done +select * from t1; +sync_slave_with_master; +# when bug existed in statement-based binlogging, t1 on slave had +# different content from on master +select * from t1; + +# second scenario: duplicate on second row +connection master; +delete from t1 where id!=10; +if ($binlog_format_statement) +{ + # statement below will be converted to non-delayed INSERT and so + # will be binlogged with its ER_DUP_ENTRY error code, guaranteeing + # replication (slave will hit the same error code and so be fine). + --error ER_DUP_ENTRY_WITH_KEY_NAME + insert delayed into t1 values(20, "is Bond"), (10, "James Bond"); +} +if (!$binlog_format_statement) +{ + insert delayed into t1 values(20, "is Bond"), (10, "James Bond"); +} +flush table t1; # to wait for INSERT DELAYED to be done +select * from t1; +sync_slave_with_master; +# when bug existed in statement-based binlogging, query was binlogged +# with error_code=0 so slave stopped +select * from t1; + +# clean up +connection master; +USE test; +DROP SCHEMA mysqlslap; +sync_slave_with_master; +connection master; diff --git a/mysql-test/extra/rpl_tests/rpl_insert_id.test b/mysql-test/extra/rpl_tests/rpl_insert_id.test index 0b07216cf11..bd2abbac6de 100644 --- a/mysql-test/extra/rpl_tests/rpl_insert_id.test +++ b/mysql-test/extra/rpl_tests/rpl_insert_id.test @@ -209,7 +209,7 @@ connection master; drop function bug15728; drop function bug15728_insert; -drop table t1; +drop table t1,t2; drop procedure foo; # test of BUG#20188 REPLACE or ON DUPLICATE KEY UPDATE in @@ -276,6 +276,59 @@ connection master; drop table t1; sync_slave_with_master; +# +# BUG#24432 "INSERT... ON DUPLICATE KEY UPDATE skips auto_increment values" +# + +connection master; +# testcase with INSERT VALUES +CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b INT, +UNIQUE(b)); +INSERT INTO t1(b) VALUES(1),(1),(2) ON DUPLICATE KEY UPDATE t1.b=10; +SELECT * FROM t1; +sync_slave_with_master; +SELECT * FROM t1; +connection master; +drop table t1; + +# tescase with INSERT SELECT +CREATE TABLE t1 ( + id bigint(20) unsigned NOT NULL auto_increment, + field_1 int(10) unsigned NOT NULL, + field_2 varchar(255) NOT NULL, + field_3 varchar(255) NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY field_1 (field_1, field_2) +); +CREATE TABLE t2 ( + field_a int(10) unsigned NOT NULL, + field_b varchar(255) NOT NULL, + field_c varchar(255) NOT NULL +); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (1, 'a', '1a'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (2, 'b', '2b'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (3, 'c', '3c'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (4, 'd', '4d'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (5, 'e', '5e'); +# Updating table t1 based on values from table t2 +INSERT INTO t1 (field_1, field_2, field_3) +SELECT t2.field_a, t2.field_b, t2.field_c +FROM t2 +ON DUPLICATE KEY UPDATE +t1.field_3 = t2.field_c; +# Inserting new record into t2 +INSERT INTO t2 (field_a, field_b, field_c) VALUES (6, 'f', '6f'); +# Updating t1 again +INSERT INTO t1 (field_1, field_2, field_3) +SELECT t2.field_a, t2.field_b, t2.field_c +FROM t2 +ON DUPLICATE KEY UPDATE +t1.field_3 = t2.field_c; +SELECT * FROM t1; +sync_slave_with_master; +SELECT * FROM t1; +connection master; +drop table t1, t2; # # BUG#20339: stored procedure using LAST_INSERT_ID() does not 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/binlog_row_binlog.result b/mysql-test/r/binlog_row_binlog.result index aee270bd7f6..769f23ea86c 100644 --- a/mysql-test/r/binlog_row_binlog.result +++ b/mysql-test/r/binlog_row_binlog.result @@ -249,6 +249,11 @@ create table t1 (a int); create table if not exists t2 select * from t1; create temporary table tt1 (a int); create table if not exists t3 like tt1; +USE mysql; +INSERT INTO user SET host='localhost', user='@#@', password=password('Just a test'); +UPDATE user SET password=password('Another password') WHERE host='localhost' AND user='@#@'; +DELETE FROM user WHERE host='localhost' AND user='@#@'; +use test; show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Query 1 # use `test`; create table t1 (id tinyint auto_increment primary key) @@ -262,6 +267,12 @@ master-bin.000001 # Query 1 # use `test`; CREATE TABLE IF NOT EXISTS `t2` ( master-bin.000001 # Query 1 # use `test`; CREATE TABLE IF NOT EXISTS `t3` ( `a` int(11) DEFAULT NULL ) +master-bin.000001 # Table_map 1 # table_id: # (mysql.user) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysql.user) +master-bin.000001 # Update_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysql.user) +master-bin.000001 # Delete_rows 1 # table_id: # flags: STMT_END_F drop table t1,t2,t3,tt1; create table t1 (a int not null auto_increment, primary key (a)) engine=myisam; set @@session.auto_increment_increment=1, @@session.auto_increment_offset=1; @@ -281,6 +292,12 @@ master-bin.000001 # Query 1 # use `test`; CREATE TABLE IF NOT EXISTS `t2` ( master-bin.000001 # Query 1 # use `test`; CREATE TABLE IF NOT EXISTS `t3` ( `a` int(11) DEFAULT NULL ) +master-bin.000001 # Table_map 1 # table_id: # (mysql.user) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysql.user) +master-bin.000001 # Update_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map 1 # table_id: # (mysql.user) +master-bin.000001 # Delete_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Query 1 # use `test`; DROP TABLE `t1`,`t2`,`t3` /* generated by server */ master-bin.000001 # Query 1 # use `test`; create table t1 (a int not null auto_increment, primary key (a)) engine=myisam master-bin.000001 # Table_map 1 # table_id: # (test.t1) diff --git a/mysql-test/r/binlog_row_ctype_cp932.result b/mysql-test/r/binlog_row_ctype_cp932.result index ed57b87c1ba..10451686e2c 100644 --- a/mysql-test/r/binlog_row_ctype_cp932.result +++ b/mysql-test/r/binlog_row_ctype_cp932.result @@ -11353,3 +11353,15 @@ a a a drop table t1; +set names utf8; +create table t1 (a text) default character set cp932; +insert into t1 values (_utf8 0xE38182); +show warnings; +Level Code Message +select * from t1; +a +ã‚ +select hex(a) from t1; +hex(a) +82A0 +drop table t1; diff --git a/mysql-test/r/binlog_stm_binlog.result b/mysql-test/r/binlog_stm_binlog.result index a9b3f69ecee..fb0453b5b68 100644 --- a/mysql-test/r/binlog_stm_binlog.result +++ b/mysql-test/r/binlog_stm_binlog.result @@ -159,6 +159,11 @@ create table t1 (a int); create table if not exists t2 select * from t1; create temporary table tt1 (a int); create table if not exists t3 like tt1; +USE mysql; +INSERT INTO user SET host='localhost', user='@#@', password=password('Just a test'); +UPDATE user SET password=password('Another password') WHERE host='localhost' AND user='@#@'; +DELETE FROM user WHERE host='localhost' AND user='@#@'; +use test; show binlog events from 102; Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Query 1 # use `test`; create table t1 (id tinyint auto_increment primary key) @@ -169,6 +174,9 @@ master-bin.000001 # Query 1 # use `test`; create table t1 (a int) master-bin.000001 # Query 1 # use `test`; create table if not exists t2 select * from t1 master-bin.000001 # Query 1 # use `test`; create temporary table tt1 (a int) master-bin.000001 # Query 1 # use `test`; create table if not exists t3 like tt1 +master-bin.000001 # Query 1 # use `mysql`; INSERT INTO user SET host='localhost', user='@#@', password=password('Just a test') +master-bin.000001 # Query 1 # use `mysql`; UPDATE user SET password=password('Another password') WHERE host='localhost' AND user='@#@' +master-bin.000001 # Query 1 # use `mysql`; DELETE FROM user WHERE host='localhost' AND user='@#@' drop table t1,t2,t3,tt1; create table t1 (a int not null auto_increment, primary key (a)) engine=myisam; set @@session.auto_increment_increment=1, @@session.auto_increment_offset=1; @@ -185,6 +193,9 @@ master-bin.000001 # Query 1 # use `test`; create table t1 (a int) master-bin.000001 # Query 1 # use `test`; create table if not exists t2 select * from t1 master-bin.000001 # Query 1 # use `test`; create temporary table tt1 (a int) master-bin.000001 # Query 1 # use `test`; create table if not exists t3 like tt1 +master-bin.000001 # Query 1 # use `mysql`; INSERT INTO user SET host='localhost', user='@#@', password=password('Just a test') +master-bin.000001 # Query 1 # use `mysql`; UPDATE user SET password=password('Another password') WHERE host='localhost' AND user='@#@' +master-bin.000001 # Query 1 # use `mysql`; DELETE FROM user WHERE host='localhost' AND user='@#@' master-bin.000001 # Query 1 # use `test`; drop table t1,t2,t3,tt1 master-bin.000001 # Query 1 # use `test`; create table t1 (a int not null auto_increment, primary key (a)) engine=myisam master-bin.000001 # Table_map 1 # table_id: # (test.t1) diff --git a/mysql-test/r/binlog_stm_ctype_cp932.result b/mysql-test/r/binlog_stm_ctype_cp932.result index ed57b87c1ba..10451686e2c 100755 --- a/mysql-test/r/binlog_stm_ctype_cp932.result +++ b/mysql-test/r/binlog_stm_ctype_cp932.result @@ -11353,3 +11353,15 @@ a a a drop table t1; +set names utf8; +create table t1 (a text) default character set cp932; +insert into t1 values (_utf8 0xE38182); +show warnings; +Level Code Message +select * from t1; +a +ã‚ +select hex(a) from t1; +hex(a) +82A0 +drop table t1; diff --git a/mysql-test/r/ctype_cp932_binlog_stm.result b/mysql-test/r/ctype_cp932_binlog_stm.result index ef1067e7c5d..ef024e2fa20 100644 --- a/mysql-test/r/ctype_cp932_binlog_stm.result +++ b/mysql-test/r/ctype_cp932_binlog_stm.result @@ -30,17 +30,17 @@ HEX(s1) HEX(s2) d 466F6F2773206120426172 ED40ED41ED42 47.93 DROP PROCEDURE bug18293| DROP TABLE t4| -SHOW BINLOG EVENTS FROM 397| +SHOW BINLOG EVENTS FROM 406| Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 397 Query 1 560 use `test`; CREATE TABLE t4 (s1 CHAR(50) CHARACTER SET latin1, +master-bin.000001 406 Query 1 572 use `test`; CREATE TABLE t4 (s1 CHAR(50) CHARACTER SET latin1, s2 CHAR(50) CHARACTER SET cp932, d DECIMAL(10,2)) -master-bin.000001 560 Query 1 805 use `test`; CREATE DEFINER=`root`@`localhost` PROCEDURE bug18293 (IN ins1 CHAR(50), +master-bin.000001 572 Query 1 820 use `test`; CREATE DEFINER=`root`@`localhost` PROCEDURE bug18293 (IN ins1 CHAR(50), IN ins2 CHAR(50) CHARACTER SET cp932, IN ind DECIMAL(10,2)) BEGIN INSERT INTO t4 VALUES (ins1, ins2, ind); END -master-bin.000001 805 Query 1 1021 use `test`; INSERT INTO t4 VALUES ( NAME_CONST('ins1',_latin1 0x466F6F2773206120426172), NAME_CONST('ins2',_cp932 0xED40ED41ED42), NAME_CONST('ind',47.93)) -master-bin.000001 1021 Query 1 1107 use `test`; DROP PROCEDURE bug18293 -master-bin.000001 1107 Query 1 1183 use `test`; DROP TABLE t4 +master-bin.000001 820 Query 1 1039 use `test`; INSERT INTO t4 VALUES ( NAME_CONST('ins1',_latin1 0x466F6F2773206120426172), NAME_CONST('ins2',_cp932 0xED40ED41ED42), NAME_CONST('ind',47.93)) +master-bin.000001 1039 Query 1 1128 use `test`; DROP PROCEDURE bug18293 +master-bin.000001 1128 Query 1 1207 use `test`; DROP TABLE t4 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/func_misc.result b/mysql-test/r/func_misc.result index 84974f845c5..b4c6f0f6699 100644 --- a/mysql-test/r/func_misc.result +++ b/mysql-test/r/func_misc.result @@ -141,4 +141,48 @@ t1 CREATE TABLE `t1` ( `a` bigint(21) unsigned DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; +drop table if exists table_26093; +drop function if exists func_26093_a; +drop function if exists func_26093_b; +create table table_26093(a int); +insert into table_26093 values +(1), (2), (3), (4), (5), +(6), (7), (8), (9), (10); +create function func_26093_a(x int) returns int +begin +set @invoked := @invoked + 1; +return x; +end// +create function func_26093_b(x int, y int) returns int +begin +set @invoked := @invoked + 1; +return x; +end// +select avg(a) from table_26093; +avg(a) +5.5000 +select benchmark(100, (select avg(a) from table_26093)); +benchmark(100, (select avg(a) from table_26093)) +0 +set @invoked := 0; +select benchmark(100, (select avg(func_26093_a(a)) from table_26093)); +benchmark(100, (select avg(func_26093_a(a)) from table_26093)) +0 +select @invoked; +@invoked +10 +set @invoked := 0; +select benchmark(100, (select avg(func_26093_b(a, rand())) from table_26093)); +benchmark(100, (select avg(func_26093_b(a, rand())) from table_26093)) +0 +select @invoked; +@invoked +1000 +select benchmark(100, (select (a) from table_26093)); +ERROR 21000: Subquery returns more than 1 row +select benchmark(100, (select 1, 1)); +ERROR 21000: Operand should contain 1 column(s) +drop table table_26093; +drop function func_26093_a; +drop function func_26093_b; End of 5.0 tests 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/information_schema_db.result b/mysql-test/r/information_schema_db.result index db14b7b6600..2a6a3e6e0fb 100644 --- a/mysql-test/r/information_schema_db.result +++ b/mysql-test/r/information_schema_db.result @@ -98,13 +98,13 @@ where table_schema='test'; table_name table_type table_comment t1 BASE TABLE v1 VIEW VIEW -v2 VIEW View 'test.v2' references invalid table(s) or column(s) or function(s) or define +v2 VIEW VIEW drop table t1; select table_name, table_type, table_comment from information_schema.tables where table_schema='test'; table_name table_type table_comment -v1 VIEW View 'test.v1' references invalid table(s) or column(s) or function(s) or define -v2 VIEW View 'test.v2' references invalid table(s) or column(s) or function(s) or define +v1 VIEW VIEW +v2 VIEW VIEW drop function f1; drop function f2; drop view v1, v2; 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/innodb-replace.result b/mysql-test/r/innodb-replace.result index b7edcc49e56..77e0aeb38fd 100644 --- a/mysql-test/r/innodb-replace.result +++ b/mysql-test/r/innodb-replace.result @@ -2,11 +2,11 @@ drop table if exists t1; create table t1 (c1 char(5) unique not null, c2 int, stamp timestamp) engine=innodb; select * from t1; c1 c2 stamp -replace delayed into t1 (c1, c2) values ( "text1","11"),( "text2","12"); +replace delayed into t1 (c1, c2) values ( "text1","11"); ERROR HY000: Table storage engine for 't1' doesn't have this option select * from t1; c1 c2 stamp -replace delayed into t1 (c1, c2) values ( "text1","12"),( "text2","13"),( "text3","14", "a" ),( "text4","15", "b" ); +replace delayed into t1 (c1, c2) values ( "text1","12"); ERROR HY000: Table storage engine for 't1' doesn't have this option select * from t1; c1 c2 stamp diff --git a/mysql-test/r/mysqlbinlog.result b/mysql-test/r/mysqlbinlog.result index d671b347786..73e60f833c7 100644 --- a/mysql-test/r/mysqlbinlog.result +++ b/mysql-test/r/mysqlbinlog.result @@ -274,3 +274,61 @@ call p1(); 1 drop procedure p1; flush logs; +create table t1 (a varchar(64) character set utf8); +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set character_set_database=koi8r; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set character_set_database=latin1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set character_set_database=koi8r; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set character_set_database=latin1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +load data infile '../std_data_ln/loaddata6.dat' into table t1 character set koi8r; +select hex(a) from t1; +hex(a) +C3BF +D0AA +C3BF +C3BF +D0AA +C3BF +D0AA +drop table t1; +flush logs; +/*!40019 SET @@session.max_insert_delayed_threads=0*/; +/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/; +DELIMITER /*!*/; +use test/*!*/; +SET TIMESTAMP=1000000000/*!*/; +SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=1, @@session.unique_checks=1/*!*/; +SET @@session.sql_mode=0/*!*/; +/*!\C latin1 *//*!*/; +SET @@session.character_set_client=8,@@session.collation_connection=8,@@session.collation_server=8/*!*/; +create table t1 (a varchar(64) character set utf8)/*!*/; +SET TIMESTAMP=1000000000/*!*/; +load data LOCAL INFILE 'MYSQL_TMP_DIR/SQL_LOAD_MB-6-0' INTO table t1/*!*/; +SET TIMESTAMP=1000000000/*!*/; +SET @@session.collation_database=7/*!*/; +load data LOCAL INFILE 'MYSQL_TMP_DIR/SQL_LOAD_MB-7-0' INTO table t1/*!*/; +SET TIMESTAMP=1000000000/*!*/; +SET @@session.collation_database=DEFAULT/*!*/; +load data LOCAL INFILE 'MYSQL_TMP_DIR/SQL_LOAD_MB-8-0' INTO table t1/*!*/; +SET TIMESTAMP=1000000000/*!*/; +load data LOCAL INFILE 'MYSQL_TMP_DIR/SQL_LOAD_MB-9-0' INTO table t1/*!*/; +SET TIMESTAMP=1000000000/*!*/; +SET @@session.collation_database=7/*!*/; +load data LOCAL INFILE 'MYSQL_TMP_DIR/SQL_LOAD_MB-a-0' INTO table t1/*!*/; +SET TIMESTAMP=1000000000/*!*/; +SET @@session.collation_database=DEFAULT/*!*/; +load data LOCAL INFILE 'MYSQL_TMP_DIR/SQL_LOAD_MB-b-0' INTO table t1/*!*/; +SET TIMESTAMP=1000000000/*!*/; +load data LOCAL INFILE 'MYSQL_TMP_DIR/SQL_LOAD_MB-c-0' INTO table t1 character set koi8r/*!*/; +SET TIMESTAMP=1000000000/*!*/; +drop table t1/*!*/; +DELIMITER ; +# End of log file +ROLLBACK /* added by mysqlbinlog */; +/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; +flush logs; diff --git a/mysql-test/r/mysqlbinlog_base64.result b/mysql-test/r/mysqlbinlog_base64.result index 33047a8774d..b62023e0ccf 100644 --- a/mysql-test/r/mysqlbinlog_base64.result +++ b/mysql-test/r/mysqlbinlog_base64.result @@ -86,5 +86,25 @@ Aberdeen Abernathy aberrant aberration +flush logs; +drop table t2; +create table t2 (word varchar(20)); +load data infile '../std_data_ln/words.dat' into table t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +select count(*) from t2; +count(*) +35840 +flush logs; +select count(*) from t2; +count(*) +35840 drop table t1; drop table t2; 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/rpl_insert_id.result b/mysql-test/r/rpl_insert_id.result index 63be35b8720..b0c1b6cfd73 100644 --- a/mysql-test/r/rpl_insert_id.result +++ b/mysql-test/r/rpl_insert_id.result @@ -196,7 +196,7 @@ id last_id 3 5 drop function bug15728; drop function bug15728_insert; -drop table t1; +drop table t1,t2; drop procedure foo; create table t1 (n int primary key auto_increment not null, b int, unique(b)); @@ -263,6 +263,64 @@ n b 2 100 3 350 drop table t1; +CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b INT, +UNIQUE(b)); +INSERT INTO t1(b) VALUES(1),(1),(2) ON DUPLICATE KEY UPDATE t1.b=10; +SELECT * FROM t1; +a b +1 10 +2 2 +SELECT * FROM t1; +a b +1 10 +2 2 +drop table t1; +CREATE TABLE t1 ( +id bigint(20) unsigned NOT NULL auto_increment, +field_1 int(10) unsigned NOT NULL, +field_2 varchar(255) NOT NULL, +field_3 varchar(255) NOT NULL, +PRIMARY KEY (id), +UNIQUE KEY field_1 (field_1, field_2) +); +CREATE TABLE t2 ( +field_a int(10) unsigned NOT NULL, +field_b varchar(255) NOT NULL, +field_c varchar(255) NOT NULL +); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (1, 'a', '1a'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (2, 'b', '2b'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (3, 'c', '3c'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (4, 'd', '4d'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (5, 'e', '5e'); +INSERT INTO t1 (field_1, field_2, field_3) +SELECT t2.field_a, t2.field_b, t2.field_c +FROM t2 +ON DUPLICATE KEY UPDATE +t1.field_3 = t2.field_c; +INSERT INTO t2 (field_a, field_b, field_c) VALUES (6, 'f', '6f'); +INSERT INTO t1 (field_1, field_2, field_3) +SELECT t2.field_a, t2.field_b, t2.field_c +FROM t2 +ON DUPLICATE KEY UPDATE +t1.field_3 = t2.field_c; +SELECT * FROM t1; +id field_1 field_2 field_3 +1 1 a 1a +2 2 b 2b +3 3 c 3c +4 4 d 4d +5 5 e 5e +6 6 f 6f +SELECT * FROM t1; +id field_1 field_2 field_3 +1 1 a 1a +2 2 b 2b +3 3 c 3c +4 4 d 4d +5 5 e 5e +6 6 f 6f +drop table t1, t2; DROP PROCEDURE IF EXISTS p1; DROP TABLE IF EXISTS t1, t2; SELECT LAST_INSERT_ID(0); diff --git a/mysql-test/r/rpl_known_bugs_detection.result b/mysql-test/r/rpl_known_bugs_detection.result new file mode 100644 index 00000000000..eabc6185780 --- /dev/null +++ b/mysql-test/r/rpl_known_bugs_detection.result @@ -0,0 +1,133 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b INT, +UNIQUE(b)); +INSERT INTO t1(b) VALUES(1),(1),(2) ON DUPLICATE KEY UPDATE t1.b=10; +SELECT * FROM t1; +a b +1 10 +2 2 +show slave status;; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port # +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1105 +Last_Error Error 'master may suffer from http://bugs.mysql.com/bug.php?id=24432 so slave stops; check error log on slave for more info' on query. Default database: 'test'. Query: 'INSERT INTO t1(b) VALUES(1),(1),(2) ON DUPLICATE KEY UPDATE t1.b=10' +Skip_Counter 0 +Exec_Master_Log_Pos 242 +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SELECT * FROM t1; +a b +stop slave; +reset slave; +reset master; +drop table t1; +start slave; +CREATE TABLE t1 ( +id bigint(20) unsigned NOT NULL auto_increment, +field_1 int(10) unsigned NOT NULL, +field_2 varchar(255) NOT NULL, +field_3 varchar(255) NOT NULL, +PRIMARY KEY (id), +UNIQUE KEY field_1 (field_1, field_2) +); +CREATE TABLE t2 ( +field_a int(10) unsigned NOT NULL, +field_b varchar(255) NOT NULL, +field_c varchar(255) NOT NULL +); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (1, 'a', '1a'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (2, 'b', '2b'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (3, 'c', '3c'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (4, 'd', '4d'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (5, 'e', '5e'); +INSERT INTO t1 (field_1, field_2, field_3) +SELECT t2.field_a, t2.field_b, t2.field_c +FROM t2 +ON DUPLICATE KEY UPDATE +t1.field_3 = t2.field_c; +INSERT INTO t2 (field_a, field_b, field_c) VALUES (6, 'f', '6f'); +INSERT INTO t1 (field_1, field_2, field_3) +SELECT t2.field_a, t2.field_b, t2.field_c +FROM t2 +ON DUPLICATE KEY UPDATE +t1.field_3 = t2.field_c; +SELECT * FROM t1; +id field_1 field_2 field_3 +1 1 a 1a +2 2 b 2b +3 3 c 3c +4 4 d 4d +5 5 e 5e +6 6 f 6f +show slave status;; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port # +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1105 +Last_Error Error 'master may suffer from http://bugs.mysql.com/bug.php?id=24432 so slave stops; check error log on slave for more info' on query. Default database: 'test'. Query: 'INSERT INTO t1 (field_1, field_2, field_3) +SELECT t2.field_a, t2.field_b, t2.field_c +FROM t2 +ON DUPLICATE KEY UPDATE +t1.field_3 = t2.field_c' +Skip_Counter 0 +Exec_Master_Log_Pos 1274 +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SELECT * FROM t1; +id field_1 field_2 field_3 +drop table t1, t2; +drop table t1, t2; diff --git a/mysql-test/r/rpl_loaddata_charset.result b/mysql-test/r/rpl_loaddata_charset.result new file mode 100644 index 00000000000..929d06e74cf --- /dev/null +++ b/mysql-test/r/rpl_loaddata_charset.result @@ -0,0 +1,37 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +create table t1 (a varchar(10) character set utf8); +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set @@character_set_database=koi8r; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set @@character_set_database=DEFAULT; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set @@character_set_database=koi8r; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set @@character_set_database=DEFAULT; +load data infile '../std_data_ln/loaddata6.dat' into table t1 character set koi8r; +select hex(a) from t1; +hex(a) +C3BF +D0AA +C3BF +C3BF +C3BF +D0AA +D0AA +select hex(a) from t1; +hex(a) +C3BF +D0AA +C3BF +C3BF +C3BF +D0AA +D0AA +drop table t1; diff --git a/mysql-test/r/rpl_loaddata2.result b/mysql-test/r/rpl_loaddata_simple.result index 196093211b6..196093211b6 100644 --- a/mysql-test/r/rpl_loaddata2.result +++ b/mysql-test/r/rpl_loaddata_simple.result diff --git a/mysql-test/r/rpl_replicate_do.result b/mysql-test/r/rpl_replicate_do.result index 43e7c6779bf..51a281fdb12 100644 --- a/mysql-test/r/rpl_replicate_do.result +++ b/mysql-test/r/rpl_replicate_do.result @@ -41,3 +41,37 @@ select * from t1; ts 2005-08-12 00:00:00 drop table t1; +*** master *** +create table t1 (a int, b int); +create trigger trg1 before insert on t1 for each row set new.b=2; +create table t2 (a int, b int); +create trigger trg2 before insert on t2 for each row set new.b=2; +show tables; +Tables_in_test +t1 +t2 +show triggers; +Trigger Event Table Statement Timing Created sql_mode Definer +trg1 INSERT t1 set new.b=2 BEFORE NULL root@localhost +trg2 INSERT t2 set new.b=2 BEFORE NULL root@localhost +*** slave *** +show tables; +Tables_in_test +t1 +show triggers; +Trigger Event Table Statement Timing Created sql_mode Definer +trg1 INSERT t1 set new.b=2 BEFORE NULL root@localhost +*** master *** +drop trigger trg1; +drop trigger trg2; +show triggers; +Trigger Event Table Statement Timing Created sql_mode Definer +*** slave *** +show tables; +Tables_in_test +t1 +show triggers; +Trigger Event Table Statement Timing Created sql_mode Definer +*** master *** +drop table t1; +drop table t2; diff --git a/mysql-test/r/rpl_row_insert_delayed.result b/mysql-test/r/rpl_row_insert_delayed.result new file mode 100644 index 00000000000..2044672f49d --- /dev/null +++ b/mysql-test/r/rpl_row_insert_delayed.result @@ -0,0 +1,48 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +set @old_global_binlog_format = @@global.binlog_format; +set @@global.binlog_format = row; +CREATE SCHEMA IF NOT EXISTS mysqlslap; +USE mysqlslap; +select @@global.binlog_format; +@@global.binlog_format +ROW +CREATE TABLE t1 (id INT primary key auto_increment, name VARCHAR(64)); +FLUSH TABLE t1; +SELECT COUNT(*) FROM t1; +COUNT(*) +5000 +use mysqlslap; +SELECT COUNT(*) FROM t1; +COUNT(*) +5000 +truncate table t1; +insert delayed into t1 values(10, "my name"); +insert delayed into t1 values(10, "is Bond"), (20, "James Bond"); +flush table t1; +select * from t1; +id name +10 my name +20 James Bond +select * from t1; +id name +10 my name +20 James Bond +delete from t1 where id!=10; +insert delayed into t1 values(20, "is Bond"), (10, "James Bond"); +flush table t1; +select * from t1; +id name +10 my name +20 is Bond +select * from t1; +id name +10 my name +20 is Bond +USE test; +DROP SCHEMA mysqlslap; +set @@global.binlog_format = @old_global_binlog_format; diff --git a/mysql-test/r/rpl_stm_insert_delayed.result b/mysql-test/r/rpl_stm_insert_delayed.result new file mode 100644 index 00000000000..1c003856eb9 --- /dev/null +++ b/mysql-test/r/rpl_stm_insert_delayed.result @@ -0,0 +1,88 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +set @old_global_binlog_format = @@global.binlog_format; +set @@global.binlog_format = statement; +CREATE SCHEMA IF NOT EXISTS mysqlslap; +USE mysqlslap; +select @@global.binlog_format; +@@global.binlog_format +STATEMENT +CREATE TABLE t1 (id INT primary key auto_increment, name VARCHAR(64)); +FLUSH TABLE t1; +SELECT COUNT(*) FROM t1; +COUNT(*) +5000 +use mysqlslap; +SELECT COUNT(*) FROM t1; +COUNT(*) +5000 +truncate table t1; +insert delayed into t1 values(10, "my name"); +insert delayed into t1 values(10, "is Bond"), (20, "James Bond"); +ERROR 23000: Duplicate entry '10' for key 'PRIMARY' +flush table t1; +select * from t1; +id name +10 my name +select * from t1; +id name +10 my name +delete from t1 where id!=10; +insert delayed into t1 values(20, "is Bond"), (10, "James Bond"); +ERROR 23000: Duplicate entry '10' for key 'PRIMARY' +flush table t1; +select * from t1; +id name +10 my name +20 is Bond +select * from t1; +id name +10 my name +20 is Bond +USE test; +DROP SCHEMA mysqlslap; +set @@global.binlog_format = mixed; +CREATE SCHEMA IF NOT EXISTS mysqlslap; +USE mysqlslap; +select @@global.binlog_format; +@@global.binlog_format +MIXED +CREATE TABLE t1 (id INT primary key auto_increment, name VARCHAR(64)); +FLUSH TABLE t1; +SELECT COUNT(*) FROM t1; +COUNT(*) +5000 +use mysqlslap; +SELECT COUNT(*) FROM t1; +COUNT(*) +5000 +truncate table t1; +insert delayed into t1 values(10, "my name"); +insert delayed into t1 values(10, "is Bond"), (20, "James Bond"); +flush table t1; +select * from t1; +id name +10 my name +20 James Bond +select * from t1; +id name +10 my name +20 James Bond +delete from t1 where id!=10; +insert delayed into t1 values(20, "is Bond"), (10, "James Bond"); +flush table t1; +select * from t1; +id name +10 my name +20 is Bond +select * from t1; +id name +10 my name +20 is Bond +USE test; +DROP SCHEMA mysqlslap; +set @@global.binlog_format = @old_global_binlog_format; diff --git a/mysql-test/r/rpl_switch_stm_row_mixed.result b/mysql-test/r/rpl_switch_stm_row_mixed.result index 258adc83b04..48b228550a7 100644 --- a/mysql-test/r/rpl_switch_stm_row_mixed.result +++ b/mysql-test/r/rpl_switch_stm_row_mixed.result @@ -7,8 +7,31 @@ start slave; drop database if exists mysqltest1; create database mysqltest1; use mysqltest1; +set session binlog_format=mixed; +show session variables like "binlog_format%"; +Variable_name Value +binlog_format MIXED +set session binlog_format=statement; +show session variables like "binlog_format%"; +Variable_name Value +binlog_format STATEMENT set session binlog_format=row; -set global binlog_format=row; +show session variables like "binlog_format%"; +Variable_name Value +binlog_format ROW +set global binlog_format=DEFAULT; +show global variables like "binlog_format%"; +Variable_name Value +binlog_format MIXED +set global binlog_format=MIXED; +show global variables like "binlog_format%"; +Variable_name Value +binlog_format MIXED +set global binlog_format=STATEMENT; +show global variables like "binlog_format%"; +Variable_name Value +binlog_format STATEMENT +set global binlog_format=ROW; show global variables like "binlog_format%"; Variable_name Value binlog_format ROW @@ -67,12 +90,11 @@ execute stmt1 using @string; deallocate prepare stmt1; insert into t1 values("for_10_"); insert into t1 select "yesterday_11_"; -set binlog_format=default; +set binlog_format=statement; select @@global.binlog_format, @@session.binlog_format; @@global.binlog_format @@session.binlog_format STATEMENT STATEMENT -set global binlog_format=default; -ERROR 42000: Variable 'binlog_format' doesn't have a default value +set global binlog_format=statement; select @@global.binlog_format, @@session.binlog_format; @@global.binlog_format @@session.binlog_format STATEMENT STATEMENT @@ -87,11 +109,11 @@ execute stmt1 using @string; deallocate prepare stmt1; insert into t1 values("for_15_"); insert into t1 select "yesterday_16_"; -set binlog_format=mixed; +set global binlog_format=mixed; select @@global.binlog_format, @@session.binlog_format; @@global.binlog_format @@session.binlog_format -STATEMENT MIXED -set global binlog_format=mixed; +MIXED STATEMENT +set binlog_format=default; select @@global.binlog_format, @@session.binlog_format; @@global.binlog_format @@session.binlog_format MIXED MIXED diff --git a/mysql-test/r/rpl_user_variables.result b/mysql-test/r/rpl_user_variables.result index ed0d2782394..3b6002a5d2d 100644 --- a/mysql-test/r/rpl_user_variables.result +++ b/mysql-test/r/rpl_user_variables.result @@ -80,4 +80,178 @@ abc\def This is a test insert into t1 select * FROM (select @var1 union select @var2) AS t2; drop table t1; +End of 4.1 tests. +DROP TABLE IF EXISTS t20; +DROP TABLE IF EXISTS t21; +DROP PROCEDURE IF EXISTS test.insert; +CREATE TABLE t20 (a VARCHAR(20)); +CREATE TABLE t21 (a VARCHAR(20)); +CREATE PROCEDURE test.insert() +BEGIN +IF (@VAR) +THEN +INSERT INTO test.t20 VALUES ('SP_TRUE'); +ELSE +INSERT INTO test.t20 VALUES ('SP_FALSE'); +END IF; +END| +CREATE TRIGGER test.insert_bi BEFORE INSERT +ON test.t20 FOR EACH ROW +BEGIN +IF (@VAR) +THEN +INSERT INTO test.t21 VALUES ('TRIG_TRUE'); +ELSE +INSERT INTO test.t21 VALUES ('TRIG_FALSE'); +END IF; +END| +SET @VAR=0; +CALL test.insert(); +SET @VAR=1; +CALL test.insert(); +On master: Check the tables for correct data +SELECT * FROM t20; +a +SP_FALSE +SP_TRUE +SELECT * FROM t21; +a +TRIG_FALSE +TRIG_TRUE +On slave: Check the tables for correct data and it matches master +SELECT * FROM t20; +a +SP_FALSE +SP_TRUE +SELECT * FROM t21; +a +TRIG_FALSE +TRIG_TRUE +DROP TABLE t20; +DROP TABLE t21; +DROP PROCEDURE test.insert; +DROP TABLE IF EXISTS t1; +DROP FUNCTION IF EXISTS test.square; +CREATE TABLE t1 (i INT); +CREATE FUNCTION test.square() RETURNS INTEGER DETERMINISTIC RETURN +(@var * @var); +SET @var = 1; +INSERT INTO t1 VALUES (square()); +SET @var = 2; +INSERT INTO t1 VALUES (square()); +SET @var = 3; +INSERT INTO t1 VALUES (square()); +SET @var = 4; +INSERT INTO t1 VALUES (square()); +SET @var = 5; +INSERT INTO t1 VALUES (square()); +On master: Retrieve the values from the table +SELECT * FROM t1; +i +1 +4 +9 +16 +25 +On slave: Retrieve the values from the table and verify they are the same as on master +SELECT * FROM t1; +i +1 +4 +9 +16 +25 +DROP TABLE t1; +DROP FUNCTION test.square; +DROP TABLE IF EXISTS t1; +DROP FUNCTION IF EXISTS f1; +DROP FUNCTION IF EXISTS f2; +CREATE TABLE t1(a int); +CREATE FUNCTION f1() returns int deterministic BEGIN +return @a; +END | +CREATE FUNCTION f2() returns int deterministic BEGIN +IF (@b > 0) then +SET @c = (@a + @b); +else +SET @c = (@a - 1); +END if; +return @c; +END | +SET @a=500; +INSERT INTO t1 values(f1()); +SET @b = 125; +SET @c = 1; +INSERT INTO t1 values(f2()); +On master: Retrieve the values from the table +SELECT * from t1; +a +500 +625 +On slave: Check the tables for correct data and it matches master +SELECT * from t1; +a +500 +625 +DROP TABLE t1; +DROP FUNCTION f1; +DROP FUNCTION f2; +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t2; +CREATE TABLE t1 (i int); +CREATE TABLE t2 (k int); +CREATE trigger t1_bi before INSERT on t1 for each row BEGIN +INSERT INTO t2 values (@a); +SET @a:=42; +INSERT INTO t2 values (@a); +END | +SET @a:=100; +INSERT INTO t1 values (5); +On master: Check to see that data was inserted correctly in both tables +SELECT * from t1; +i +5 +SELECT * from t2; +k +100 +42 +On slave: Check the tables for correct data and it matches master +SELECT * from t1; +i +5 +SELECT * from t2; +k +100 +42 +End of 5.0 tests. +DROP TABLE t1; +DROP TABLE t2; +DROP TABLE IF EXISTS t1; +DROP FUNCTION IF EXISTS f1; +DROP FUNCTION IF EXISTS f2; +CREATE TABLE t1 (i INT); +CREATE FUNCTION f1() RETURNS INT RETURN @a; +CREATE +FUNCTION f2() RETURNS INT BEGIN +INSERT INTO t1 VALUES (10 + @a); +RETURN 0; +END| +SET @a:=123; +SELECT f1(), f2(); +f1() f2() +123 0 +On master: Check to see that data was inserted correctly +INSERT INTO t1 VALUES(f1()); +SELECT * FROM t1; +i +133 +123 +On slave: Check the table for correct data and it matches master +SELECT * FROM t1; +i +133 +123 +DROP FUNCTION f1; +DROP FUNCTION f2; +DROP TABLE t1; stop slave; diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result index 516355839b5..acab2f17910 100644 --- a/mysql-test/r/show_check.result +++ b/mysql-test/r/show_check.result @@ -730,4 +730,26 @@ 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 +set names utf8; +drop table if exists `été`; +create table `été` (field1 int); +show full tables; +Tables_in_test Table_type +été BASE TABLE +drop table `été`; +set names latin1; End of 5.1 tests diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result index 0ed92aa9e7a..8c933927250 100644 --- a/mysql-test/r/sp-error.result +++ b/mysql-test/r/sp-error.result @@ -1128,9 +1128,9 @@ drop view if exists v1, v2, v3, v4; create function bug11555_1() returns int return (select max(i) from t1); create function bug11555_2() returns int return bug11555_1(); create view v1 as select bug11555_1(); -ERROR 42S02: Table 'test.t1' doesn't exist +drop view v1; create view v2 as select bug11555_2(); -ERROR 42S02: Table 'test.t1' doesn't exist +drop view v2; create table t1 (i int); create view v1 as select bug11555_1(); create view v2 as select bug11555_2(); @@ -1143,8 +1143,7 @@ ERROR HY000: View 'test.v2' references invalid table(s) or column(s) or function select * from v3; ERROR HY000: View 'test.v3' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them create view v4 as select * from v1; -ERROR HY000: View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them -drop view v1, v2, v3; +drop view v1, v2, v3, v4; drop function bug11555_1; drop function bug11555_2; create table t1 (i int); @@ -1153,12 +1152,12 @@ create trigger t1_ai after insert on t1 for each row insert into t2 values (new. create view v1 as select * from t1; drop table t2; insert into v1 values (1); -ERROR HY000: View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them +ERROR HY000: Table 't2' was not locked with LOCK TABLES drop trigger t1_ai; create function bug11555_1() returns int return (select max(i) from t2); create trigger t1_ai after insert on t1 for each row set @a:=bug11555_1(); insert into v1 values (2); -ERROR HY000: View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them +ERROR HY000: Table 't2' was not locked with LOCK TABLES drop function bug11555_1; drop table t1; drop view v1; @@ -1269,6 +1268,138 @@ call bug24491(); ERROR 42S22: Unknown column 'y.value' in 'field list' drop procedure bug24491; drop tables t1; +DROP FUNCTION IF EXISTS bug18914_f1; +DROP FUNCTION IF EXISTS bug18914_f2; +DROP PROCEDURE IF EXISTS bug18914_p1; +DROP PROCEDURE IF EXISTS bug18914_p2; +DROP TABLE IF EXISTS t1, t2; +CREATE TABLE t1 (i INT); +CREATE PROCEDURE bug18914_p1() CREATE TABLE t2 (i INT); +CREATE PROCEDURE bug18914_p2() DROP TABLE IF EXISTS no_such_table; +CREATE FUNCTION bug18914_f1() RETURNS INT +BEGIN +CALL bug18914_p1(); +RETURN 1; +END | +CREATE FUNCTION bug18914_f2() RETURNS INT +BEGIN +CALL bug18914_p2(); +RETURN 1; +END | +CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW +CALL bug18914_p1(); +INSERT INTO t1 VALUES (1); +ERROR HY000: Explicit or implicit commit is not allowed in stored function or trigger. +SELECT bug18914_f1(); +ERROR HY000: Explicit or implicit commit is not allowed in stored function or trigger. +SELECT bug18914_f2(); +ERROR HY000: Explicit or implicit commit is not allowed in stored function or trigger. +SELECT * FROM t2; +ERROR 42S02: Table 'test.t2' doesn't exist +DROP FUNCTION bug18914_f1; +DROP FUNCTION bug18914_f2; +DROP PROCEDURE bug18914_p1; +DROP PROCEDURE bug18914_p2; +DROP TABLE t1; +drop table if exists bogus_table_20713; +drop function if exists func_20713_a; +drop function if exists func_20713_b; +create table bogus_table_20713( id int(10) not null primary key); +insert into bogus_table_20713 values (1), (2), (3); +create function func_20713_a() returns int(11) +begin +declare id int; +declare continue handler for sqlexception set id=null; +set @in_func := 1; +set id = (select id from bogus_table_20713 where id = 3); +set @in_func := 2; +return id; +end// +create function func_20713_b() returns int(11) +begin +declare id int; +declare continue handler for sqlstate value '42S02' set id=null; +set @in_func := 1; +set id = (select id from bogus_table_20713 where id = 3); +set @in_func := 2; +return id; +end// +set @in_func := 0; +select func_20713_a(); +func_20713_a() +NULL +select @in_func; +@in_func +2 +set @in_func := 0; +select func_20713_b(); +func_20713_b() +NULL +select @in_func; +@in_func +2 +drop table bogus_table_20713; +set @in_func := 0; +select func_20713_a(); +func_20713_a() +NULL +select @in_func; +@in_func +2 +set @in_func := 0; +select func_20713_b(); +func_20713_b() +NULL +select @in_func; +@in_func +2 +drop function if exists func_20713_a; +drop function if exists func_20713_b; +drop table if exists table_25345_a; +drop table if exists table_25345_b; +drop procedure if exists proc_25345; +drop function if exists func_25345; +drop function if exists func_25345_b; +create table table_25345_a (a int); +create table table_25345_b (b int); +create procedure proc_25345() +begin +declare c1 cursor for select a from table_25345_a; +declare c2 cursor for select b from table_25345_b; +select 1 as result; +end || +create function func_25345() returns int(11) +begin +call proc_25345(); +return 1; +end || +create function func_25345_b() returns int(11) +begin +declare c1 cursor for select a from table_25345_a; +declare c2 cursor for select b from table_25345_b; +return 1; +end || +call proc_25345(); +result +1 +select func_25345(); +ERROR 0A000: Not allowed to return a result set from a function +select func_25345_b(); +func_25345_b() +1 +drop table table_25345_a; +call proc_25345(); +result +1 +select func_25345(); +ERROR 0A000: Not allowed to return a result set from a function +select func_25345_b(); +func_25345_b() +1 +drop table table_25345_b; +drop procedure proc_25345; +drop function func_25345; +drop function func_25345_b; End of 5.0 tests drop function if exists bug16164; create function bug16164() returns int 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/sp.result b/mysql-test/r/sp.result index 51ab8d5e139..9b96781b58a 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -1155,9 +1155,13 @@ create function f12_2() returns int return (select count(*) from t3)| drop temporary table t3| select f12_1()| -ERROR 42S02: Table 'test.t3' doesn't exist +f12_1() +3 +Warnings: +Note 1051 Unknown table 't3' select f12_1() from t1 limit 1| -ERROR 42S02: Table 'test.t3' doesn't exist +f12_1() +3 drop function f0| drop function f1| drop function f2| @@ -5832,4 +5836,38 @@ END| CALL bug24117()| DROP PROCEDURE bug24117| DROP TABLE t3| +drop function if exists func_8407_a| +drop function if exists func_8407_b| +create function func_8407_a() returns int +begin +declare x int; +declare continue handler for sqlexception +begin +end; +select 1 from no_such_view limit 1 into x; +return x; +end| +create function func_8407_b() returns int +begin +declare x int default 0; +declare continue handler for sqlstate '42S02' + begin +set x:= x+1000; +end; +case (select 1 from no_such_view limit 1) +when 1 then set x:= x+1; +when 2 then set x:= x+2; +else set x:= x+100; +end case; +set x:=x + 500; +return x; +end| +select func_8407_a()| +func_8407_a() +NULL +select func_8407_b()| +func_8407_b() +1500 +drop function func_8407_a| +drop function func_8407_b| drop table t1,t2; diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 43abef692e9..30e6d073d9a 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -3095,7 +3095,7 @@ SELECT a FROM t1 GROUP BY a HAVING IFNULL((SELECT b FROM t2 WHERE b > 4), (SELECT c FROM t2 WHERE c=a AND b > 1 ORDER BY b)) > 3; ERROR 21000: Subquery returns more than 1 row -SELECT a FROM t1 +SELECT a FROM t1 ORDER BY IFNULL((SELECT b FROM t2 WHERE b > 2), (SELECT c FROM t2 WHERE c=a AND b > 2 ORDER BY b)); a @@ -3103,11 +3103,11 @@ a 4 1 3 -SELECT a FROM t1 +SELECT a FROM t1 ORDER BY IFNULL((SELECT b FROM t2 WHERE b > 1), (SELECT c FROM t2 WHERE c=a AND b > 1 ORDER BY b)); ERROR 21000: Subquery returns more than 1 row -SELECT a FROM t1 +SELECT a FROM t1 ORDER BY IFNULL((SELECT b FROM t2 WHERE b > 4), (SELECT c FROM t2 WHERE c=a AND b > 2 ORDER BY b)); a @@ -3115,7 +3115,7 @@ a 1 3 4 -SELECT a FROM t1 +SELECT a FROM t1 ORDER BY IFNULL((SELECT b FROM t2 WHERE b > 4), (SELECT c FROM t2 WHERE c=a AND b > 1 ORDER BY b)); ERROR 21000: Subquery returns more than 1 row @@ -3683,16 +3683,16 @@ CREATE TABLE t1 (id char(4) PRIMARY KEY, c int); CREATE TABLE t2 (c int); INSERT INTO t1 VALUES ('aa', 1); INSERT INTO t2 VALUES (1); -SELECT * FROM t1 +SELECT * FROM t1 WHERE EXISTS (SELECT c FROM t2 WHERE c=1 -UNION +UNION SELECT c from t2 WHERE c=t1.c); id c aa 1 INSERT INTO t1 VALUES ('bb', 2), ('cc', 3), ('dd',1); -SELECT * FROM t1 +SELECT * FROM t1 WHERE EXISTS (SELECT c FROM t2 WHERE c=1 -UNION +UNION SELECT c from t2 WHERE c=t1.c); id c aa 1 @@ -3702,9 +3702,9 @@ dd 1 INSERT INTO t2 VALUES (2); CREATE TABLE t3 (c int); INSERT INTO t3 VALUES (1); -SELECT * FROM t1 +SELECT * FROM t1 WHERE EXISTS (SELECT t2.c FROM t2 JOIN t3 ON t2.c=t3.c WHERE t2.c=1 -UNION +UNION SELECT c from t2 WHERE c=t1.c); id c aa 1 @@ -3760,3 +3760,116 @@ a MAX(b) test 2 3 h 3 4 i DROP TABLE t1; +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 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 +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 +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 +DROP TABLE t1; +DROP TABLE t2; +DROP TABLE t1xt2; diff --git a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result index 9e5e9c8244c..e5d1b5a3f1f 100644 --- a/mysql-test/r/trigger.result +++ b/mysql-test/r/trigger.result @@ -1335,4 +1335,41 @@ SELECT fubar_id FROM t2; fubar_id 1 DROP TABLE t1,t2; +DROP TABLE IF EXISTS bug21825_A; +DROP TABLE IF EXISTS bug21825_B; +CREATE TABLE bug21825_A (id int(10)); +CREATE TABLE bug21825_B (id int(10)); +CREATE TRIGGER trgA AFTER INSERT ON bug21825_A +FOR EACH ROW +BEGIN +INSERT INTO bug21825_B (id) values (1); +END// +INSERT INTO bug21825_A (id) VALUES (10); +INSERT INTO bug21825_A (id) VALUES (20); +DROP TABLE bug21825_B; +DELETE FROM bug21825_A WHERE id = 20; +DROP TABLE bug21825_A; +DROP TABLE IF EXISTS bug22580_t1; +DROP PROCEDURE IF EXISTS bug22580_proc_1; +DROP PROCEDURE IF EXISTS bug22580_proc_2; +CREATE TABLE bug22580_t1 (a INT, b INT); +CREATE PROCEDURE bug22580_proc_2() +BEGIN +DROP TABLE IF EXISTS bug22580_tmp; +CREATE TEMPORARY TABLE bug22580_tmp (a INT); +DROP TABLE bug22580_tmp; +END|| +CREATE PROCEDURE bug22580_proc_1() +BEGIN +CALL bug22580_proc_2(); +END|| +CREATE TRIGGER t1bu BEFORE UPDATE ON bug22580_t1 +FOR EACH ROW +BEGIN +CALL bug22580_proc_1(); +END|| +INSERT INTO bug22580_t1 VALUES (1,1); +DROP TABLE bug22580_t1; +DROP PROCEDURE bug22580_proc_1; +DROP PROCEDURE bug22580_proc_2; End of 5.0 tests diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 8f83e1acb09..30043e066db 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; @@ -1933,11 +1935,11 @@ create function f1 () returns int return (select max(col1) from t1); DROP TABLE t1; CHECK TABLE v1, v2, v3, v4, v5, v6; Table Op Msg_type Msg_text -test.v1 check error View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them +test.v1 check status OK test.v2 check status OK -test.v3 check error View 'test.v3' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them +test.v3 check status OK test.v4 check status OK -test.v5 check error View 'test.v5' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them +test.v5 check status OK test.v6 check status OK drop function f1; drop function f2; @@ -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/r/xml.result b/mysql-test/r/xml.result index 08f8293ff52..236c50774bd 100644 --- a/mysql-test/r/xml.result +++ b/mysql-test/r/xml.result @@ -809,3 +809,78 @@ ExtractValue(@xml, "/entry[(50<pt)]/id") select ExtractValue(@xml, "/entry[(50<=pt)]/id"); ExtractValue(@xml, "/entry[(50<=pt)]/id") pt50 +select ExtractValue('<a><b><Text>test</Text></b></a>','/a/b/Text'); +ExtractValue('<a><b><Text>test</Text></b></a>','/a/b/Text') +test +select ExtractValue('<a><b><comment>test</comment></b></a>','/a/b/comment'); +ExtractValue('<a><b><comment>test</comment></b></a>','/a/b/comment') +test +select ExtractValue('<a><b><node>test</node></b></a>','/a/b/node'); +ExtractValue('<a><b><node>test</node></b></a>','/a/b/node') +test +select ExtractValue('<a><b><processing-instruction>test</processing-instruction></b></a>','/a/b/processing-instruction'); +ExtractValue('<a><b><processing-instruction>test</processing-instruction></b></a>','/a/b/processing-instruction') +test +select ExtractValue('<a><and>test</and></a>', '/a/and'); +ExtractValue('<a><and>test</and></a>', '/a/and') +test +select ExtractValue('<a><or>test</or></a>', '/a/or'); +ExtractValue('<a><or>test</or></a>', '/a/or') +test +select ExtractValue('<a><mod>test</mod></a>', '/a/mod'); +ExtractValue('<a><mod>test</mod></a>', '/a/mod') +test +select ExtractValue('<a><div>test</div></a>', '/a/div'); +ExtractValue('<a><div>test</div></a>', '/a/div') +test +select ExtractValue('<a><and:and>test</and:and></a>', '/a/and:and'); +ExtractValue('<a><and:and>test</and:and></a>', '/a/and:and') +test +select ExtractValue('<a><or:or>test</or:or></a>', '/a/or:or'); +ExtractValue('<a><or:or>test</or:or></a>', '/a/or:or') +test +select ExtractValue('<a><mod:mod>test</mod:mod></a>', '/a/mod:mod'); +ExtractValue('<a><mod:mod>test</mod:mod></a>', '/a/mod:mod') +test +select ExtractValue('<a><div:div>test</div:div></a>', '/a/div:div'); +ExtractValue('<a><div:div>test</div:div></a>', '/a/div:div') +test +select ExtractValue('<a><ancestor>test</ancestor></a>', '/a/ancestor'); +ExtractValue('<a><ancestor>test</ancestor></a>', '/a/ancestor') +test +select ExtractValue('<a><ancestor-or-self>test</ancestor-or-self></a>', '/a/ancestor-or-self'); +ExtractValue('<a><ancestor-or-self>test</ancestor-or-self></a>', '/a/ancestor-or-self') +test +select ExtractValue('<a><attribute>test</attribute></a>', '/a/attribute'); +ExtractValue('<a><attribute>test</attribute></a>', '/a/attribute') +test +select ExtractValue('<a><child>test</child></a>', '/a/child'); +ExtractValue('<a><child>test</child></a>', '/a/child') +test +select ExtractValue('<a><descendant>test</descendant></a>', '/a/descendant'); +ExtractValue('<a><descendant>test</descendant></a>', '/a/descendant') +test +select ExtractValue('<a><descendant-or-self>test</descendant-or-self></a>', '/a/descendant-or-self'); +ExtractValue('<a><descendant-or-self>test</descendant-or-self></a>', '/a/descendant-or-self') +test +select ExtractValue('<a><following>test</following></a>', '/a/following'); +ExtractValue('<a><following>test</following></a>', '/a/following') +test +select ExtractValue('<a><following-sibling>test</following-sibling></a>', '/a/following-sibling'); +ExtractValue('<a><following-sibling>test</following-sibling></a>', '/a/following-sibling') +test +select ExtractValue('<a><namespace>test</namespace></a>', '/a/namespace'); +ExtractValue('<a><namespace>test</namespace></a>', '/a/namespace') +test +select ExtractValue('<a><parent>test</parent></a>', '/a/parent'); +ExtractValue('<a><parent>test</parent></a>', '/a/parent') +test +select ExtractValue('<a><preceding>test</preceding></a>', '/a/preceding'); +ExtractValue('<a><preceding>test</preceding></a>', '/a/preceding') +test +select ExtractValue('<a><preceding-sibling>test</preceding-sibling></a>', '/a/preceding-sibling'); +ExtractValue('<a><preceding-sibling>test</preceding-sibling></a>', '/a/preceding-sibling') +test +select ExtractValue('<a><self>test</self></a>', '/a/self'); +ExtractValue('<a><self>test</self></a>', '/a/self') +test 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/std_data/loaddata6.dat b/mysql-test/std_data/loaddata6.dat new file mode 100644 index 00000000000..29e181ebb88 --- /dev/null +++ b/mysql-test/std_data/loaddata6.dat @@ -0,0 +1 @@ +ÿ diff --git a/mysql-test/t/ctype_cp932_binlog_stm.test b/mysql-test/t/ctype_cp932_binlog_stm.test index 6b591fbe5f5..9111c4ad369 100644 --- a/mysql-test/t/ctype_cp932_binlog_stm.test +++ b/mysql-test/t/ctype_cp932_binlog_stm.test @@ -22,7 +22,7 @@ CALL bug18293("Foo's a Bar", _cp932 0xED40ED41ED42, 47.93)| SELECT HEX(s1),HEX(s2),d FROM t4| DROP PROCEDURE bug18293| DROP TABLE t4| -SHOW BINLOG EVENTS FROM 397| +SHOW BINLOG EVENTS FROM 406| delimiter ;| # End of 5.0 tests 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/func_misc.test b/mysql-test/t/func_misc.test index 5cac6c45f65..8ff62f68e45 100644 --- a/mysql-test/t/func_misc.test +++ b/mysql-test/t/func_misc.test @@ -132,4 +132,61 @@ set global query_cache_size=default; create table t1 select INET_ATON('255.255.0.1') as `a`; show create table t1; drop table t1; + +# +# Bug#26093 (SELECT BENCHMARK() for SELECT statements does not produce +# valid results) +# + +--disable_warnings +drop table if exists table_26093; +drop function if exists func_26093_a; +drop function if exists func_26093_b; +--enable_warnings + +create table table_26093(a int); +insert into table_26093 values +(1), (2), (3), (4), (5), +(6), (7), (8), (9), (10); + +delimiter //; + +create function func_26093_a(x int) returns int +begin + set @invoked := @invoked + 1; + return x; +end// + +create function func_26093_b(x int, y int) returns int +begin + set @invoked := @invoked + 1; + return x; +end// + +delimiter ;// + +select avg(a) from table_26093; + +select benchmark(100, (select avg(a) from table_26093)); + +set @invoked := 0; +select benchmark(100, (select avg(func_26093_a(a)) from table_26093)); +# Returns only 10, since intermediate results are cached. +select @invoked; + +set @invoked := 0; +select benchmark(100, (select avg(func_26093_b(a, rand())) from table_26093)); +# Returns 1000, due to rand() preventing caching. +select @invoked; + +--error ER_SUBQUERY_NO_1_ROW +select benchmark(100, (select (a) from table_26093)); + +--error ER_OPERAND_COLUMNS +select benchmark(100, (select 1, 1)); + +drop table table_26093; +drop function func_26093_a; +drop function func_26093_b; + --echo End of 5.0 tests 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/innodb-replace.test b/mysql-test/t/innodb-replace.test index 51b70f34b65..d44ede65ce8 100644 --- a/mysql-test/t/innodb-replace.test +++ b/mysql-test/t/innodb-replace.test @@ -12,10 +12,10 @@ drop table if exists t1; create table t1 (c1 char(5) unique not null, c2 int, stamp timestamp) engine=innodb; select * from t1; --error 1031 -replace delayed into t1 (c1, c2) values ( "text1","11"),( "text2","12"); +replace delayed into t1 (c1, c2) values ( "text1","11"); select * from t1; --error 1031 -replace delayed into t1 (c1, c2) values ( "text1","12"),( "text2","13"),( "text3","14", "a" ),( "text4","15", "b" ); +replace delayed into t1 (c1, c2) values ( "text1","12"); select * from t1; drop table t1; 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/mysqlbinlog.test b/mysql-test/t/mysqlbinlog.test index 8c5d5f772fc..852cc6d2e36 100644 --- a/mysql-test/t/mysqlbinlog.test +++ b/mysql-test/t/mysqlbinlog.test @@ -193,6 +193,30 @@ drop procedure p1; --exec $MYSQL_BINLOG --help 2>&1 > /dev/null --enable_query_log +# +# Bug#15126 character_set_database is not replicated +# (LOAD DATA INFILE need it) +# + +flush logs; +create table t1 (a varchar(64) character set utf8); +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set character_set_database=koi8r; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set character_set_database=latin1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set character_set_database=koi8r; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set character_set_database=latin1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +load data infile '../std_data_ln/loaddata6.dat' into table t1 character set koi8r; +select hex(a) from t1; +drop table t1; +flush logs; +--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +--exec $MYSQL_BINLOG --short-form $MYSQLTEST_VARDIR/log/master-bin.000012 + # End of 5.0 tests # @@ -200,7 +224,7 @@ drop procedure p1; # flush logs; --error 1 ---exec $MYSQL_BINLOG $MYSQLTEST_VARDIR/log/master-bin.000012 >/dev/null 2>/dev/null ---exec $MYSQL_BINLOG --force-if-open $MYSQLTEST_VARDIR/log/master-bin.000012 >/dev/null 2>/dev/null +--exec $MYSQL_BINLOG $MYSQLTEST_VARDIR/log/master-bin.000014 >/dev/null 2>/dev/null +--exec $MYSQL_BINLOG --force-if-open $MYSQLTEST_VARDIR/log/master-bin.000014 >/dev/null 2>/dev/null # End of 5.1 tests diff --git a/mysql-test/t/mysqlbinlog_base64.test b/mysql-test/t/mysqlbinlog_base64.test index 1ca018218b2..1b5dc67c150 100644 --- a/mysql-test/t/mysqlbinlog_base64.test +++ b/mysql-test/t/mysqlbinlog_base64.test @@ -32,6 +32,34 @@ select * from t1; select * from t2; # +# Verify that events larger than the default IO_CACHE buffer +# are handled correctly (BUG#25628). +# +flush logs; +drop table t2; +create table t2 (word varchar(20)); +load data infile '../std_data_ln/words.dat' into table t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +insert into t2 select * from t2; +select count(*) from t2; + +flush logs; +--exec $MYSQL_BINLOG --hexdump $MYSQLTEST_VARDIR/log/master-bin.000003 > $MYSQLTEST_VARDIR/tmp/mysqlbinlog_base64.sql +--exec $MYSQL test < $MYSQLTEST_VARDIR/tmp/mysqlbinlog_base64.sql + +# +# Verify that all binlog events have been executed +# +select count(*) from t2; + +# # Test cleanup # --exec rm $MYSQLTEST_VARDIR/tmp/mysqlbinlog_base64.sql 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/rpl_known_bugs_detection-master.opt b/mysql-test/t/rpl_known_bugs_detection-master.opt new file mode 100644 index 00000000000..d4ba386a1a0 --- /dev/null +++ b/mysql-test/t/rpl_known_bugs_detection-master.opt @@ -0,0 +1 @@ +--loose-debug=d,pretend_version_50034_in_binlog diff --git a/mysql-test/t/rpl_known_bugs_detection.test b/mysql-test/t/rpl_known_bugs_detection.test new file mode 100644 index 00000000000..ce3debf3c5b --- /dev/null +++ b/mysql-test/t/rpl_known_bugs_detection.test @@ -0,0 +1,93 @@ +# Test to see if slave can detect certain known bugs present +# on the master, and appropriately decides to stop +# (assuming the bug is fixed in the slave, slave cannot of course +# imitate the bug, so it has to stop). + +source include/have_debug.inc; +source include/master-slave.inc; + +# Currently only statement-based-specific bugs are here +-- source include/have_binlog_format_mixed_or_statement.inc + +# +# This is to test that slave properly detects if +# master may suffer from: +# BUG#24432 "INSERT... ON DUPLICATE KEY UPDATE skips auto_increment values" +# (i.e. on master, INSERT ON DUPLICATE KEY UPDATE is used and manipulates +# an auto_increment column, and is binlogged statement-based). +# + +# testcase with INSERT VALUES +CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b INT, +UNIQUE(b)); +sync_slave_with_master; +connection master; +INSERT INTO t1(b) VALUES(1),(1),(2) ON DUPLICATE KEY UPDATE t1.b=10; +SELECT * FROM t1; +connection slave; +wait_for_slave_to_stop; +# show the error message +--replace_column 1 # 4 # 7 # 8 # 9 # 23 # 33 # +--query_vertical show slave status; +# show that it was not replicated +SELECT * FROM t1; + +# restart replication for the next testcase +stop slave; +reset slave; +connection master; +reset master; +drop table t1; +connection slave; +start slave; + +# testcase with INSERT SELECT +connection master; +CREATE TABLE t1 ( + id bigint(20) unsigned NOT NULL auto_increment, + field_1 int(10) unsigned NOT NULL, + field_2 varchar(255) NOT NULL, + field_3 varchar(255) NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY field_1 (field_1, field_2) +); +CREATE TABLE t2 ( + field_a int(10) unsigned NOT NULL, + field_b varchar(255) NOT NULL, + field_c varchar(255) NOT NULL +); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (1, 'a', '1a'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (2, 'b', '2b'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (3, 'c', '3c'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (4, 'd', '4d'); +INSERT INTO t2 (field_a, field_b, field_c) VALUES (5, 'e', '5e'); +sync_slave_with_master; +connection master; +# Updating table t1 based on values from table t2 +INSERT INTO t1 (field_1, field_2, field_3) +SELECT t2.field_a, t2.field_b, t2.field_c +FROM t2 +ON DUPLICATE KEY UPDATE +t1.field_3 = t2.field_c; +# Inserting new record into t2 +INSERT INTO t2 (field_a, field_b, field_c) VALUES (6, 'f', '6f'); +# Updating t1 again +INSERT INTO t1 (field_1, field_2, field_3) +SELECT t2.field_a, t2.field_b, t2.field_c +FROM t2 +ON DUPLICATE KEY UPDATE +t1.field_3 = t2.field_c; +SELECT * FROM t1; +connection slave; +wait_for_slave_to_stop; +# show the error message +--replace_column 1 # 4 # 7 # 8 # 9 # 23 # 33 # +--query_vertical show slave status; +# show that it was not replicated +SELECT * FROM t1; +connection master; +drop table t1, t2; +connection slave; +drop table t1, t2; + +# End of 5.0 tests diff --git a/mysql-test/t/rpl_loaddata_charset.test b/mysql-test/t/rpl_loaddata_charset.test new file mode 100644 index 00000000000..7f2389cb9f6 --- /dev/null +++ b/mysql-test/t/rpl_loaddata_charset.test @@ -0,0 +1,33 @@ +# +# Check LOAD DATA + character sets + replication +# +source include/master-slave.inc; + +# +# Bug#15126 character_set_database is not replicated +# (LOAD DATA INFILE need it) +# +connection master; +create table t1 (a varchar(10) character set utf8); +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set @@character_set_database=koi8r; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set @@character_set_database=DEFAULT; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set @@character_set_database=koi8r; +load data infile '../std_data_ln/loaddata6.dat' into table t1; +set @@character_set_database=DEFAULT; +load data infile '../std_data_ln/loaddata6.dat' into table t1 character set koi8r; + +select hex(a) from t1; + +save_master_pos; +connection slave; +sync_with_master; + +select hex(a) from t1; +connection master; +drop table t1; +sync_slave_with_master; diff --git a/mysql-test/t/rpl_loaddata2.test b/mysql-test/t/rpl_loaddata_simple.test index 9e908cac677..9e908cac677 100644 --- a/mysql-test/t/rpl_loaddata2.test +++ b/mysql-test/t/rpl_loaddata_simple.test diff --git a/mysql-test/t/rpl_replicate_do.test b/mysql-test/t/rpl_replicate_do.test index e7141c3f905..600840a2828 100644 --- a/mysql-test/t/rpl_replicate_do.test +++ b/mysql-test/t/rpl_replicate_do.test @@ -58,3 +58,35 @@ drop table t1; sync_slave_with_master; # End of 4.1 tests + +# +# Bug#24478 DROP TRIGGER is not caught by replicate-*-table filters +# +--echo *** master *** +connection master; +create table t1 (a int, b int); +create trigger trg1 before insert on t1 for each row set new.b=2; +create table t2 (a int, b int); +create trigger trg2 before insert on t2 for each row set new.b=2; +show tables; +show triggers; +sync_slave_with_master; +--echo *** slave *** +connection slave; +show tables; +show triggers; +--echo *** master *** +connection master; +drop trigger trg1; +drop trigger trg2; +show triggers; +sync_slave_with_master; +--echo *** slave *** +connection slave; +show tables; +show triggers; +--echo *** master *** +connection master; +drop table t1; +drop table t2; +sync_slave_with_master; diff --git a/mysql-test/t/rpl_row_insert_delayed.test b/mysql-test/t/rpl_row_insert_delayed.test new file mode 100644 index 00000000000..9aeb57c4fa2 --- /dev/null +++ b/mysql-test/t/rpl_row_insert_delayed.test @@ -0,0 +1,14 @@ +--source include/have_binlog_format_row.inc +--source include/master-slave.inc +--source include/not_embedded.inc +--source include/not_windows.inc + +connection master; +set @old_global_binlog_format = @@global.binlog_format; + +let $binlog_format_statement=0; +set @@global.binlog_format = row; +--source extra/rpl_tests/rpl_insert_delayed.test + +connection master; +set @@global.binlog_format = @old_global_binlog_format; diff --git a/mysql-test/t/rpl_stm_insert_delayed.test b/mysql-test/t/rpl_stm_insert_delayed.test new file mode 100644 index 00000000000..d55e3a4da2c --- /dev/null +++ b/mysql-test/t/rpl_stm_insert_delayed.test @@ -0,0 +1,20 @@ +# we run first in statement-based then in mixed binlogging + +--source include/have_binlog_format_mixed_or_statement.inc +--source include/master-slave.inc +--source include/not_embedded.inc +--source include/not_windows.inc + +connection master; +set @old_global_binlog_format = @@global.binlog_format; + +let $binlog_format_statement=1; +set @@global.binlog_format = statement; +--source extra/rpl_tests/rpl_insert_delayed.test + +let $binlog_format_statement=0; +set @@global.binlog_format = mixed; +--source extra/rpl_tests/rpl_insert_delayed.test + +connection master; +set @@global.binlog_format = @old_global_binlog_format; diff --git a/mysql-test/t/rpl_switch_stm_row_mixed.test b/mysql-test/t/rpl_switch_stm_row_mixed.test index ccd505941c8..bffa5905f1f 100644 --- a/mysql-test/t/rpl_switch_stm_row_mixed.test +++ b/mysql-test/t/rpl_switch_stm_row_mixed.test @@ -1,3 +1,14 @@ +# +# rpl_switch_stm_row_mixed tests covers +# +# - switching explicitly between STATEMENT, ROW, and MIXED binlog format +# showing when it is possible and when not. +# - switching from MIXED to RBR implicitly listing all use cases, +# e.g a query invokes UUID(), thereafter to serve as the definition +# of MIXED binlog format +# - correctness of execution + + -- source include/not_ndb_default.inc -- source include/master-slave.inc @@ -8,9 +19,22 @@ create database mysqltest1; --enable_warnings use mysqltest1; + +# play with switching +set session binlog_format=mixed; +show session variables like "binlog_format%"; +set session binlog_format=statement; +show session variables like "binlog_format%"; set session binlog_format=row; -set global binlog_format=row; +show session variables like "binlog_format%"; +set global binlog_format=DEFAULT; +show global variables like "binlog_format%"; +set global binlog_format=MIXED; +show global variables like "binlog_format%"; +set global binlog_format=STATEMENT; +show global variables like "binlog_format%"; +set global binlog_format=ROW; show global variables like "binlog_format%"; show session variables like "binlog_format%"; select @@global.binlog_format, @@session.binlog_format; @@ -63,12 +87,10 @@ deallocate prepare stmt1; insert into t1 values("for_10_"); insert into t1 select "yesterday_11_"; -# test SET DEFAULT (=statement at this point of test) -set binlog_format=default; +# test statement (is not default after wl#3368) +set binlog_format=statement; select @@global.binlog_format, @@session.binlog_format; -# due to cluster it's hard to set back to default ---error ER_NO_DEFAULT -set global binlog_format=default; +set global binlog_format=statement; select @@global.binlog_format, @@session.binlog_format; prepare stmt1 from 'insert into t1 select ?'; @@ -87,10 +109,10 @@ insert into t1 select "yesterday_16_"; # and now the mixed mode -set binlog_format=mixed; -select @@global.binlog_format, @@session.binlog_format; set global binlog_format=mixed; select @@global.binlog_format, @@session.binlog_format; +set binlog_format=default; +select @@global.binlog_format, @@session.binlog_format; prepare stmt1 from 'insert into t1 select concat(UUID(),?)'; set @string="emergency_17_"; diff --git a/mysql-test/t/rpl_user_variables.test b/mysql-test/t/rpl_user_variables.test index 08717fce114..226591bd13f 100644 --- a/mysql-test/t/rpl_user_variables.test +++ b/mysql-test/t/rpl_user_variables.test @@ -53,5 +53,302 @@ SELECT * FROM t1 ORDER BY n; connection master; insert into t1 select * FROM (select @var1 union select @var2) AS t2; drop table t1; +--echo End of 4.1 tests. + +# BUG#20141 +# The following tests ensure that if user-defined variables are used in SF/Triggers +# that they are replicated correctly. These tests should be run in both SBR and RBR +# modes. + +# This test uses a procedure that inserts data values based on the value of a +# user-defined variable. It also has a trigger that inserts data based on the +# same variable. Successful test runs show that the @var is replicated +# properly and that the procedure and trigger insert the correct data on the +# slave. +# +# The test of stored procedure was included for completeness. Replication of stored +# procedures was not directly affected by BUG#20141. +# +# This test was constructed for BUG#20141 + +--disable_warnings +DROP TABLE IF EXISTS t20; +DROP TABLE IF EXISTS t21; +DROP PROCEDURE IF EXISTS test.insert; +--enable_warnings + +CREATE TABLE t20 (a VARCHAR(20)); +CREATE TABLE t21 (a VARCHAR(20)); +DELIMITER |; + +# Create a procedure that uses the @var for flow control + +CREATE PROCEDURE test.insert() +BEGIN + IF (@VAR) + THEN + INSERT INTO test.t20 VALUES ('SP_TRUE'); + ELSE + INSERT INTO test.t20 VALUES ('SP_FALSE'); + END IF; +END| + +# Create a trigger that uses the @var for flow control + +CREATE TRIGGER test.insert_bi BEFORE INSERT + ON test.t20 FOR EACH ROW + BEGIN + IF (@VAR) + THEN + INSERT INTO test.t21 VALUES ('TRIG_TRUE'); + ELSE + INSERT INTO test.t21 VALUES ('TRIG_FALSE'); + END IF; + END| +DELIMITER ;| + +sync_slave_with_master; +connection master; + +# Set @var and call the procedure, repeat with different values + +SET @VAR=0; +CALL test.insert(); +SET @VAR=1; +CALL test.insert(); + +--echo On master: Check the tables for correct data + +SELECT * FROM t20; +SELECT * FROM t21; + +sync_slave_with_master; + +--echo On slave: Check the tables for correct data and it matches master + +SELECT * FROM t20; +SELECT * FROM t21; +connection master; + +# Cleanup + +DROP TABLE t20; +DROP TABLE t21; +DROP PROCEDURE test.insert; + +# This test uses a stored function that uses user-defined variables to return data +# This test was constructed for BUG#20141 + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP FUNCTION IF EXISTS test.square; +--enable_warnings + +CREATE TABLE t1 (i INT); + +# Create function that returns a value from @var. In this case, the square function + +CREATE FUNCTION test.square() RETURNS INTEGER DETERMINISTIC RETURN +(@var * @var); + +# Set the @var to different values and insert them into a table + +SET @var = 1; +INSERT INTO t1 VALUES (square()); +SET @var = 2; +INSERT INTO t1 VALUES (square()); +SET @var = 3; +INSERT INTO t1 VALUES (square()); +SET @var = 4; +INSERT INTO t1 VALUES (square()); +SET @var = 5; +INSERT INTO t1 VALUES (square()); + +--echo On master: Retrieve the values from the table + +SELECT * FROM t1; + +sync_slave_with_master; + +--echo On slave: Retrieve the values from the table and verify they are the same as on master + +SELECT * FROM t1; + +connection master; + +# Cleanup + +DROP TABLE t1; +DROP FUNCTION test.square; + +# This test uses stored functions that uses user-defined variables to return data +# based on the use of @vars inside a function body. +# This test was constructed for BUG#14914 + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP FUNCTION IF EXISTS f1; +DROP FUNCTION IF EXISTS f2; +--enable_warnings + +CREATE TABLE t1(a int); +DELIMITER |; + +# Create a function that simply returns the value of an @var. +# Create a function that uses an @var for flow control, creates and uses another +# @var and sets its value to a value based on another @var. + +CREATE FUNCTION f1() returns int deterministic BEGIN + return @a; +END | + +CREATE FUNCTION f2() returns int deterministic BEGIN + IF (@b > 0) then + SET @c = (@a + @b); + else + SET @c = (@a - 1); + END if; + return @c; +END | +DELIMITER ;| + +sync_slave_with_master; +connection master; + +# Set an @var to a value and insert data into a table using the first function. +# Set two more @vars to some values and insert data into a table using the second function. + +SET @a=500; +INSERT INTO t1 values(f1()); +SET @b = 125; +SET @c = 1; +INSERT INTO t1 values(f2()); + +--echo On master: Retrieve the values from the table + +sync_slave_with_master; +connection master; + +SELECT * from t1; + +connection slave; + +--echo On slave: Check the tables for correct data and it matches master + +SELECT * from t1; + +connection master; + +# Cleanup + +DROP TABLE t1; +DROP FUNCTION f1; +DROP FUNCTION f2; + +# This test uses a function that changes a user-defined variable in its body. This test +# will ensure the @vars are replicated when needed and not interrupt the normal execution +# of the function on the slave. This also applies to procedures and triggers. + +# This test was constructed for BUG#25167 + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t2; +--enable_warnings +CREATE TABLE t1 (i int); +CREATE TABLE t2 (k int); +DELIMITER |; + +# Create a trigger that inserts data into another table, changes the @var then inserts +# another row with the modified value. + +CREATE trigger t1_bi before INSERT on t1 for each row BEGIN + INSERT INTO t2 values (@a); + SET @a:=42; + INSERT INTO t2 values (@a); +END | +DELIMITER ;| + +sync_slave_with_master; +connection master; + +# Set the @var to a value then insert data into first table. + +SET @a:=100; +INSERT INTO t1 values (5); + +--echo On master: Check to see that data was inserted correctly in both tables + +SELECT * from t1; +SELECT * from t2; + +sync_slave_with_master; + +--echo On slave: Check the tables for correct data and it matches master + +SELECT * from t1; +SELECT * from t2; + +connection master; + +--echo End of 5.0 tests. + +# Cleanup + +DROP TABLE t1; +DROP TABLE t2; + +# This test uses a stored function that uses user-defined variables to return data +# The test ensures the value of the user-defined variable is replicated correctly +# and in the correct order of assignment. + +# This test was constructed for BUG#20141 + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP FUNCTION IF EXISTS f1; +DROP FUNCTION IF EXISTS f2; +--enable_warnings + +CREATE TABLE t1 (i INT); + +# Create two functions. One simply returns the user-defined variable. The other +# returns a value based on the user-defined variable. + +CREATE FUNCTION f1() RETURNS INT RETURN @a; DELIMITER |; CREATE +FUNCTION f2() RETURNS INT BEGIN + INSERT INTO t1 VALUES (10 + @a); + RETURN 0; +END| +DELIMITER ;| + +sync_slave_with_master; +connection master; + +# Set the variable and execute the functions. + +SET @a:=123; +SELECT f1(), f2(); + +--echo On master: Check to see that data was inserted correctly + +INSERT INTO t1 VALUES(f1()); +SELECT * FROM t1; + +sync_slave_with_master; + +--echo On slave: Check the table for correct data and it matches master + +SELECT * FROM t1; + +connection master; + +# Cleanup + +DROP FUNCTION f1; +DROP FUNCTION f2; +DROP TABLE t1; + sync_slave_with_master; stop slave; + diff --git a/mysql-test/t/show_check.test b/mysql-test/t/show_check.test index 938baec51d1..91832ee8af4 100644 --- a/mysql-test/t/show_check.test +++ b/mysql-test/t/show_check.test @@ -563,4 +563,33 @@ 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; +# +# Bug#25081 SHOW FULL TABLES on table with latin chars in name fails +# +set names utf8; +--disable_warnings +drop table if exists `été`; +--enable_warnings +create table `été` (field1 int); +show full tables; +drop table `été`; +set names latin1; + --echo End of 5.1 tests diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test index e67d6370153..6dc94869f04 100644 --- a/mysql-test/t/sp-error.test +++ b/mysql-test/t/sp-error.test @@ -1611,10 +1611,12 @@ create function bug11555_1() returns int return (select max(i) from t1); create function bug11555_2() returns int return bug11555_1(); # It is OK to report name of implicitly used table which is missing # when we create view. ---error ER_NO_SUCH_TABLE +# For stored functions however, because of exceptions handlers, there is +# no easy way to find out if a missing table makes the view invalid. create view v1 as select bug11555_1(); ---error ER_NO_SUCH_TABLE +drop view v1; create view v2 as select bug11555_2(); +drop view v2; # But we should hide name of missing implicitly used table when we use view create table t1 (i int); create view v1 as select bug11555_1(); @@ -1629,9 +1631,8 @@ select * from v2; select * from v3; # Note that creation of view which depends on broken view is yet # another form of view usage. ---error ER_VIEW_INVALID create view v4 as select * from v1; -drop view v1, v2, v3; +drop view v1, v2, v3, v4; # We also should hide details about broken triggers which are # invoked for view. drop function bug11555_1; @@ -1641,12 +1642,14 @@ create table t2 (i int); create trigger t1_ai after insert on t1 for each row insert into t2 values (new.i); create view v1 as select * from t1; drop table t2; ---error ER_VIEW_INVALID +# Limitation, the desired error is ER_VIEW_INVALID +--error ER_TABLE_NOT_LOCKED insert into v1 values (1); drop trigger t1_ai; create function bug11555_1() returns int return (select max(i) from t2); create trigger t1_ai after insert on t1 for each row set @a:=bug11555_1(); ---error ER_VIEW_INVALID +# Limitation, the desired error is ER_VIEW_INVALID +--error ER_TABLE_NOT_LOCKED insert into v1 values (2); drop function bug11555_1; drop table t1; @@ -1843,6 +1846,184 @@ call bug24491(); drop procedure bug24491; drop tables t1; +# +# BUG#18914: Calling certain SPs from triggers fail +# +# Failing to call a procedure that does implicit commit from a trigger +# is a correct behaviour, however the error message was misleading. +# +# DROP TABLE IF EXISTS is also fixed to give correct error instead of +# "Table doesn't exist". +# +--disable_warnings +DROP FUNCTION IF EXISTS bug18914_f1; +DROP FUNCTION IF EXISTS bug18914_f2; +DROP PROCEDURE IF EXISTS bug18914_p1; +DROP PROCEDURE IF EXISTS bug18914_p2; +DROP TABLE IF EXISTS t1, t2; +--enable_warnings + +CREATE TABLE t1 (i INT); + +CREATE PROCEDURE bug18914_p1() CREATE TABLE t2 (i INT); +CREATE PROCEDURE bug18914_p2() DROP TABLE IF EXISTS no_such_table; + +delimiter |; +CREATE FUNCTION bug18914_f1() RETURNS INT +BEGIN + CALL bug18914_p1(); + RETURN 1; +END | + +CREATE FUNCTION bug18914_f2() RETURNS INT +BEGIN + CALL bug18914_p2(); + RETURN 1; +END | +delimiter ;| + +CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW + CALL bug18914_p1(); + +--error ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG +INSERT INTO t1 VALUES (1); + +--error ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG +SELECT bug18914_f1(); + +--error ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG +SELECT bug18914_f2(); + +--error ER_NO_SUCH_TABLE +SELECT * FROM t2; + +DROP FUNCTION bug18914_f1; +DROP FUNCTION bug18914_f2; +DROP PROCEDURE bug18914_p1; +DROP PROCEDURE bug18914_p2; +DROP TABLE t1; + +# +# Bug#20713 (Functions will not not continue for SQLSTATE VALUE '42S02') +# + +--disable_warnings +drop table if exists bogus_table_20713; +drop function if exists func_20713_a; +drop function if exists func_20713_b; +--enable_warnings + +create table bogus_table_20713( id int(10) not null primary key); +insert into bogus_table_20713 values (1), (2), (3); + +delimiter //; + +create function func_20713_a() returns int(11) +begin + declare id int; + + declare continue handler for sqlexception set id=null; + + set @in_func := 1; + set id = (select id from bogus_table_20713 where id = 3); + set @in_func := 2; + + return id; +end// + +create function func_20713_b() returns int(11) +begin + declare id int; + + declare continue handler for sqlstate value '42S02' set id=null; + + set @in_func := 1; + set id = (select id from bogus_table_20713 where id = 3); + set @in_func := 2; + + return id; +end// + +delimiter ;// + +set @in_func := 0; +select func_20713_a(); +select @in_func; + +set @in_func := 0; +select func_20713_b(); +select @in_func; + +drop table bogus_table_20713; + +set @in_func := 0; +select func_20713_a(); +select @in_func; + +set @in_func := 0; +select func_20713_b(); +select @in_func; + +drop function if exists func_20713_a; +drop function if exists func_20713_b; + +# +# Bug#25345 (Cursors from Functions) +# + +--disable_warnings +drop table if exists table_25345_a; +drop table if exists table_25345_b; +drop procedure if exists proc_25345; +drop function if exists func_25345; +drop function if exists func_25345_b; +--enable_warnings + +create table table_25345_a (a int); +create table table_25345_b (b int); + +delimiter ||; + +create procedure proc_25345() +begin + declare c1 cursor for select a from table_25345_a; + declare c2 cursor for select b from table_25345_b; + + select 1 as result; +end || + +create function func_25345() returns int(11) +begin + call proc_25345(); + return 1; +end || + +create function func_25345_b() returns int(11) +begin + declare c1 cursor for select a from table_25345_a; + declare c2 cursor for select b from table_25345_b; + + return 1; +end || + +delimiter ;|| + +call proc_25345(); +--error ER_SP_NO_RETSET +select func_25345(); +select func_25345_b(); + +drop table table_25345_a; + +call proc_25345(); +--error ER_SP_NO_RETSET +select func_25345(); +select func_25345_b(); + +drop table table_25345_b; +drop procedure proc_25345; +drop function func_25345; +drop function func_25345_b; # # End of 5.0 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/sp.test b/mysql-test/t/sp.test index 38753ed3ac0..6c7fde71e78 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -1369,7 +1369,7 @@ end| select f11()| --error ER_CANT_REOPEN_TABLE select f11() from t1| -# We don't handle temporary tables used by nested functions well +# Test that using a single table instance at a time works create function f12_1() returns int begin drop temporary table if exists t3; @@ -1379,11 +1379,9 @@ begin end| create function f12_2() returns int return (select count(*) from t3)| -# We need clean start to get error + drop temporary table t3| ---error ER_NO_SUCH_TABLE select f12_1()| ---error ER_NO_SUCH_TABLE select f12_1() from t1 limit 1| # Cleanup @@ -6803,6 +6801,53 @@ DROP PROCEDURE bug24117| DROP TABLE t3| # +# Bug#8407(Stored functions/triggers ignore exception handler) +# + +--disable_warnings +drop function if exists func_8407_a| +drop function if exists func_8407_b| +--enable_warnings + +create function func_8407_a() returns int +begin + declare x int; + + declare continue handler for sqlexception + begin + end; + + select 1 from no_such_view limit 1 into x; + + return x; +end| + +create function func_8407_b() returns int +begin + declare x int default 0; + + declare continue handler for sqlstate '42S02' + begin + set x:= x+1000; + end; + + case (select 1 from no_such_view limit 1) + when 1 then set x:= x+1; + when 2 then set x:= x+2; + else set x:= x+100; + end case; + set x:=x + 500; + + return x; +end| + +select func_8407_a()| +select func_8407_b()| + +drop function func_8407_a| +drop function func_8407_b| + +# # NOTE: The delimiter is `|`, and not `;`. It is changed to `;` # at the end of the file! # diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 0ce3d1d78c6..8ca99130921 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -2002,7 +2002,7 @@ EXPLAIN SELECT a FROM t1 WHERE (SELECT 1 FROM DUAL WHERE 1=0) IS NULL; DROP TABLE t1; # -# Bug 24653: sorting by expressions containing subselects +# Bug 24653: sorting by expressions containing subselects # that return more than one row # @@ -2014,12 +2014,12 @@ INSERT INTO t2 VALUES (2,1), (1,3), (2,1), (4,4), (2,2), (1,4); SELECT a FROM t1 ORDER BY (SELECT c FROM t2 WHERE b > 2 ); ---error 1242 -SELECT a FROM t1 ORDER BY (SELECT c FROM t2 WHERE b > 1); -SELECT a FROM t1 ORDER BY (SELECT c FROM t2 WHERE b > 2), a; ---error 1242 +--error 1242 +SELECT a FROM t1 ORDER BY (SELECT c FROM t2 WHERE b > 1); +SELECT a FROM t1 ORDER BY (SELECT c FROM t2 WHERE b > 2), a; +--error 1242 SELECT a FROM t1 ORDER BY (SELECT c FROM t2 WHERE b > 1), a; - + SELECT b, MAX(c) FROM t2 GROUP BY b, (SELECT c FROM t2 WHERE b > 2); --error 1242 SELECT b, MAX(c) FROM t2 GROUP BY b, (SELECT c FROM t2 WHERE b > 1); @@ -2036,28 +2036,28 @@ SELECT a FROM t1 GROUP BY a SELECT a FROM t1 GROUP BY a HAVING IFNULL((SELECT b FROM t2 WHERE b > 4), (SELECT c FROM t2 WHERE c=a AND b > 2 ORDER BY b)) > 3; ---error 1242 +--error 1242 SELECT a FROM t1 GROUP BY a HAVING IFNULL((SELECT b FROM t2 WHERE b > 4), (SELECT c FROM t2 WHERE c=a AND b > 1 ORDER BY b)) > 3; -SELECT a FROM t1 +SELECT a FROM t1 ORDER BY IFNULL((SELECT b FROM t2 WHERE b > 2), (SELECT c FROM t2 WHERE c=a AND b > 2 ORDER BY b)); --error 1242 -SELECT a FROM t1 +SELECT a FROM t1 ORDER BY IFNULL((SELECT b FROM t2 WHERE b > 1), (SELECT c FROM t2 WHERE c=a AND b > 1 ORDER BY b)); -SELECT a FROM t1 +SELECT a FROM t1 ORDER BY IFNULL((SELECT b FROM t2 WHERE b > 4), (SELECT c FROM t2 WHERE c=a AND b > 2 ORDER BY b)); --error 1242 -SELECT a FROM t1 +SELECT a FROM t1 ORDER BY IFNULL((SELECT b FROM t2 WHERE b > 4), (SELECT c FROM t2 WHERE c=a AND b > 1 ORDER BY b)); -DROP TABLE t1,t2; +DROP TABLE t1,t2; # End of 4.1 tests @@ -2571,7 +2571,7 @@ DROP TABLE t1,t2; # # Bug #25219: EXIST subquery with UNION over a mix of # correlated and uncorrelated selects -# +# CREATE TABLE t1 (id char(4) PRIMARY KEY, c int); CREATE TABLE t2 (c int); @@ -2579,26 +2579,27 @@ CREATE TABLE t2 (c int); INSERT INTO t1 VALUES ('aa', 1); INSERT INTO t2 VALUES (1); -SELECT * FROM t1 +SELECT * FROM t1 WHERE EXISTS (SELECT c FROM t2 WHERE c=1 - UNION + UNION SELECT c from t2 WHERE c=t1.c); INSERT INTO t1 VALUES ('bb', 2), ('cc', 3), ('dd',1); -SELECT * FROM t1 +SELECT * FROM t1 WHERE EXISTS (SELECT c FROM t2 WHERE c=1 - UNION + UNION SELECT c from t2 WHERE c=t1.c); INSERT INTO t2 VALUES (2); CREATE TABLE t3 (c int); INSERT INTO t3 VALUES (1); -SELECT * FROM t1 +SELECT * FROM t1 WHERE EXISTS (SELECT t2.c FROM t2 JOIN t3 ON t2.c=t3.c WHERE t2.c=1 - UNION + UNION SELECT c from t2 WHERE c=t1.c); + DROP TABLE t1,t2,t3; # @@ -2608,7 +2609,6 @@ CREATE TABLE t1 (s1 char(1)); 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. @@ -2636,3 +2636,103 @@ 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/trigger.test b/mysql-test/t/trigger.test index b6bf8fcb40e..14608a3b193 100644 --- a/mysql-test/t/trigger.test +++ b/mysql-test/t/trigger.test @@ -1625,4 +1625,78 @@ SELECT fubar_id FROM t2; DROP TABLE t1,t2; +# +# Bug#21285 (Incorrect message error deleting records in a table with a +# trigger for inserting) +# + +--disable_warnings +DROP TABLE IF EXISTS bug21825_A; +DROP TABLE IF EXISTS bug21825_B; +--enable_warnings + +CREATE TABLE bug21825_A (id int(10)); +CREATE TABLE bug21825_B (id int(10)); + +delimiter //; + +CREATE TRIGGER trgA AFTER INSERT ON bug21825_A +FOR EACH ROW +BEGIN + INSERT INTO bug21825_B (id) values (1); +END// +delimiter ;// + +INSERT INTO bug21825_A (id) VALUES (10); +INSERT INTO bug21825_A (id) VALUES (20); + +DROP TABLE bug21825_B; + +# Must pass, the missing table in the insert trigger should not matter. +DELETE FROM bug21825_A WHERE id = 20; + +DROP TABLE bug21825_A; + +# +# Bug#22580 (DROP TABLE in nested stored procedure causes strange dependancy +# error) +# + +--disable_warnings +DROP TABLE IF EXISTS bug22580_t1; +DROP PROCEDURE IF EXISTS bug22580_proc_1; +DROP PROCEDURE IF EXISTS bug22580_proc_2; +--enable_warnings + +CREATE TABLE bug22580_t1 (a INT, b INT); + +DELIMITER ||; + +CREATE PROCEDURE bug22580_proc_2() +BEGIN + DROP TABLE IF EXISTS bug22580_tmp; + CREATE TEMPORARY TABLE bug22580_tmp (a INT); + DROP TABLE bug22580_tmp; +END|| + +CREATE PROCEDURE bug22580_proc_1() +BEGIN + CALL bug22580_proc_2(); +END|| + +CREATE TRIGGER t1bu BEFORE UPDATE ON bug22580_t1 +FOR EACH ROW +BEGIN + CALL bug22580_proc_1(); +END|| + +DELIMITER ;|| + +# Must pass, the actions of the update trigger should not matter +INSERT INTO bug22580_t1 VALUES (1,1); + +DROP TABLE bug22580_t1; +DROP PROCEDURE bug22580_proc_1; +DROP PROCEDURE bug22580_proc_2; + --echo End of 5.0 tests 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/mysql-test/t/xml.test b/mysql-test/t/xml.test index ef94c7508c4..8517dce111f 100644 --- a/mysql-test/t/xml.test +++ b/mysql-test/t/xml.test @@ -406,3 +406,41 @@ select ExtractValue(@xml, "/entry[(50>pt)]/id"); select ExtractValue(@xml, "/entry[(50>=pt)]/id"); select ExtractValue(@xml, "/entry[(50<pt)]/id"); select ExtractValue(@xml, "/entry[(50<=pt)]/id"); + +# +# Bug#24747 XPath error with the node name "Text" +# +# +# Test nodetypes in node name context +# +select ExtractValue('<a><b><Text>test</Text></b></a>','/a/b/Text'); +select ExtractValue('<a><b><comment>test</comment></b></a>','/a/b/comment'); +select ExtractValue('<a><b><node>test</node></b></a>','/a/b/node'); +select ExtractValue('<a><b><processing-instruction>test</processing-instruction></b></a>','/a/b/processing-instruction'); +# +# Test keywords in node name contexts +# +select ExtractValue('<a><and>test</and></a>', '/a/and'); +select ExtractValue('<a><or>test</or></a>', '/a/or'); +select ExtractValue('<a><mod>test</mod></a>', '/a/mod'); +select ExtractValue('<a><div>test</div></a>', '/a/div'); +select ExtractValue('<a><and:and>test</and:and></a>', '/a/and:and'); +select ExtractValue('<a><or:or>test</or:or></a>', '/a/or:or'); +select ExtractValue('<a><mod:mod>test</mod:mod></a>', '/a/mod:mod'); +select ExtractValue('<a><div:div>test</div:div></a>', '/a/div:div'); +# +# Test axis names in node name context +# +select ExtractValue('<a><ancestor>test</ancestor></a>', '/a/ancestor'); +select ExtractValue('<a><ancestor-or-self>test</ancestor-or-self></a>', '/a/ancestor-or-self'); +select ExtractValue('<a><attribute>test</attribute></a>', '/a/attribute'); +select ExtractValue('<a><child>test</child></a>', '/a/child'); +select ExtractValue('<a><descendant>test</descendant></a>', '/a/descendant'); +select ExtractValue('<a><descendant-or-self>test</descendant-or-self></a>', '/a/descendant-or-self'); +select ExtractValue('<a><following>test</following></a>', '/a/following'); +select ExtractValue('<a><following-sibling>test</following-sibling></a>', '/a/following-sibling'); +select ExtractValue('<a><namespace>test</namespace></a>', '/a/namespace'); +select ExtractValue('<a><parent>test</parent></a>', '/a/parent'); +select ExtractValue('<a><preceding>test</preceding></a>', '/a/preceding'); +select ExtractValue('<a><preceding-sibling>test</preceding-sibling></a>', '/a/preceding-sibling'); +select ExtractValue('<a><self>test</self></a>', '/a/self'); diff --git a/mysys/mf_iocache2.c b/mysys/mf_iocache2.c index b3ae18e9932..bdb5d057a16 100644 --- a/mysys/mf_iocache2.c +++ b/mysys/mf_iocache2.c @@ -50,7 +50,6 @@ int my_b_copy_to_file(IO_CACHE *cache, FILE *file) { - byte buf[IO_SIZE]; uint bytes_in_cache; DBUG_ENTER("my_b_copy_to_file"); @@ -58,19 +57,17 @@ my_b_copy_to_file(IO_CACHE *cache, FILE *file) if (reinit_io_cache(cache, READ_CACHE, 0L, FALSE, FALSE)) DBUG_RETURN(1); bytes_in_cache= my_b_bytes_in_cache(cache); - while (bytes_in_cache > 0) { - uint const read_bytes= min(bytes_in_cache, sizeof(buf)); - DBUG_PRINT("debug", ("Remaining %u bytes - Reading %u bytes", - bytes_in_cache, read_bytes)); - if (my_b_read(cache, buf, read_bytes)) - DBUG_RETURN(1); - if (my_fwrite(file, buf, read_bytes, MYF(MY_WME | MY_NABP)) == (uint) -1) + do + { + if (my_fwrite(file, cache->read_pos, bytes_in_cache, + MYF(MY_WME | MY_NABP)) == (uint) -1) DBUG_RETURN(1); - bytes_in_cache -= read_bytes; - } + cache->read_pos= cache->read_end; + } while ((bytes_in_cache= my_b_fill(cache))); DBUG_RETURN(0); } + my_off_t my_b_append_tell(IO_CACHE* info) { /* 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..14795e2225a 100644 --- a/server-tools/instance-manager/WindowsService.cpp +++ b/server-tools/instance-manager/WindowsService.cpp @@ -13,20 +13,30 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "my_global.h" #include <windows.h> -#include <assert.h> -#include ".\windowsservice.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 +45,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 +80,7 @@ BOOL WindowsService::Install() BOOL WindowsService::Init() { - assert(serviceName != NULL); + DBUG_ASSERT(serviceName != NULL); if (inited) return TRUE; @@ -207,7 +217,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 +225,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..a1892112dc8 --- /dev/null +++ b/server-tools/instance-manager/angel.cc @@ -0,0 +1,404 @@ +/* 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() +{ + 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..6d6ebbee57d 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((uint) 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 TRUE; } - 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)); + return FALSE; } - -#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; - } -} - -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; -} - - -/* - 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..dad8aeb2e20 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; @@ -1791,17 +1810,12 @@ 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? - */ + 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..296c30506f6 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", (long) 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", (long) 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..64bba756be9 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"); @@ -106,7 +112,7 @@ evex_print_warnings(THD *thd, Event_job_data *et) thd Thread NOTES - Before this is called, one should not do any DBUG_XXX() calls. + Before this is called, one should not do any DBUG_XXX() calls. */ @@ -219,7 +225,7 @@ event_scheduler_thread(void *arg) Event_scheduler *scheduler= ((struct scheduler_param *) arg)->scheduler; bool res; - thd->thread_stack= (char*) &thd; // remember where our stack is + thd->thread_stack= (char *)&thd; // remember where our stack is res= post_init_event_thread(thd); DBUG_ENTER("event_scheduler_thread"); @@ -248,53 +254,106 @@ event_scheduler_thread(void *arg) pthread_handler_t event_worker_thread(void *arg) { - /* needs to be first for thread_stack */ THD *thd; - Event_job_data *event= (Event_job_data *)arg; + Event_queue_element_for_exec *event= (Event_queue_element_for_exec *)arg; + + thd= event->thd; + + Event_worker_thread worker_thread; + worker_thread.run(thd, event); + + 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) +{ + /* needs to be first for thread_stack */ + char my_stack; int ret; + Event_job_data *job_data= NULL; bool res; - thd= event->thd; - thd->thread_stack= (char *) &thd; // remember where our stack is + thd->thread_stack= &my_stack; // remember where our stack is res= post_init_event_thread(thd); - DBUG_ENTER("event_worker_thread"); - if (!res) + DBUG_ENTER("Event_worker_thread::run"); + DBUG_PRINT("info", ("Time is %ld, THD: 0x%lx", + (long) time(NULL), (long) thd)); + + if (res) + 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; } - DBUG_PRINT("info", ("BURAN %s.%s is landing!", event->dbname.str, + + 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", ("Done with Event %s.%s", event->dbname.str, event->name.str)); - delete event; + delete event; deinit_event_thread(thd); - pthread_exit(0); - DBUG_RETURN(0); // Can't return anything here } @@ -440,7 +499,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 +511,21 @@ 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_for_execution_if_time returned " + "event_name=0x%lx", (long) event_name)); + if (event_name) { - if ((res= execute_top(thd, job_data))) + if ((res= execute_top(thd, event_name))) break; } else @@ -498,7 +559,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,22 +570,22 @@ 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; - 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->thd= new_thd; + DBUG_PRINT("info", ("Event %s@%s ready for start", + 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; - DBUG_PRINT("info", ("Launch succeeded. BURAN is in THD: 0x%lx", (long) new_thd)); + DBUG_PRINT("info", ("Event is in THD: 0x%lx", (long) new_thd)); DBUG_RETURN(FALSE); error: - DBUG_PRINT("error", ("Baikonur, we have a problem! res: %d", res)); + DBUG_PRINT("error", ("Event_scheduler::execute_top() res: %d", res)); if (new_thd) { new_thd->proc_info= "Clearing"; @@ -536,7 +597,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..f73dc97e7c2 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,132 @@ 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", (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; + } + + DBUG_PRINT("load_events_from_db", ("Adding 0x%lx to the exec list.", + (long) et)); + 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/field.cc b/sql/field.cc index 5d4dbe9a416..d406459b6c7 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -7203,7 +7203,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) cannot_convert_error_pos, from + length)) return 2; - if (copy_length < length) + if (from_end_pos < from + length) { report_data_too_long(this); return 2; 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..23c3103493e 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); } @@ -3456,7 +3451,7 @@ namespace { { int const check(table->s->tmp_table == NO_TMP_TABLE && binlog_filter->db_ok(table->s->db.str) && - strcmp("mysql", table->s->db.str) != 0); + !table->no_replicate); table->s->cached_row_logging_check= check; } 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_func.cc b/sql/item_func.cc index aa344a343de..55888f6b16a 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3447,6 +3447,7 @@ longlong Item_func_benchmark::val_int() DBUG_ASSERT(fixed == 1); char buff[MAX_FIELD_WIDTH]; String tmp(buff,sizeof(buff), &my_charset_bin); + my_decimal tmp_decimal; THD *thd=current_thd; ulong loop_count; @@ -3471,6 +3472,9 @@ longlong Item_func_benchmark::val_int() case STRING_RESULT: (void) args[1]->val_str(&tmp); break; + case DECIMAL_RESULT: + (void) args[1]->val_decimal(&tmp_decimal); + break; case ROW_RESULT: default: // This case should never be chosen @@ -4220,7 +4224,14 @@ int get_var_with_binlog(THD *thd, enum_sql_command sql_command, user_var_entry *var_entry; var_entry= get_variable(&thd->user_vars, name, 0); - if (!(opt_bin_log && is_update_query(sql_command))) + /* + Any reference to user-defined variable which is done from stored + function or trigger affects their execution and the execution of the + calling statement. We must log all such variables even if they are + not involved in table-updating statements. + */ + if (!(opt_bin_log && + (is_update_query(sql_command) || thd->in_sub_stmt))) { *out_entry= var_entry; return 0; 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/item_xmlfunc.cc b/sql/item_xmlfunc.cc index 9321992e566..26474990644 100644 --- a/sql/item_xmlfunc.cc +++ b/sql/item_xmlfunc.cc @@ -1044,12 +1044,12 @@ static struct my_xpath_keyword_names_st my_keyword_names[] = {MY_XPATH_LEX_OR , "or" , 2, 0 }, {MY_XPATH_LEX_DIV , "div" , 3, 0 }, {MY_XPATH_LEX_MOD , "mod" , 3, 0 }, - - {MY_XPATH_LEX_NODETYPE, "comment" , 7, 0 }, - {MY_XPATH_LEX_NODETYPE, "text" , 4, 0 }, - {MY_XPATH_LEX_NODETYPE, "processing-instruction" , 22,0 }, - {MY_XPATH_LEX_NODETYPE, "node" , 4, 0 }, - + {0,NULL,0,0} +}; + + +static struct my_xpath_keyword_names_st my_axis_names[]= +{ {MY_XPATH_LEX_AXIS,"ancestor" , 8,MY_XPATH_AXIS_ANCESTOR }, {MY_XPATH_LEX_AXIS,"ancestor-or-self" ,16,MY_XPATH_AXIS_ANCESTOR_OR_SELF }, {MY_XPATH_LEX_AXIS,"attribute" , 9,MY_XPATH_AXIS_ATTRIBUTE }, @@ -1063,7 +1063,16 @@ static struct my_xpath_keyword_names_st my_keyword_names[] = {MY_XPATH_LEX_AXIS,"preceding" , 9,MY_XPATH_AXIS_PRECEDING }, {MY_XPATH_LEX_AXIS,"preceding-sibling" ,17,MY_XPATH_AXIS_PRECEDING_SIBLING }, {MY_XPATH_LEX_AXIS,"self" , 4,MY_XPATH_AXIS_SELF }, + {0,NULL,0,0} +}; + +static struct my_xpath_keyword_names_st my_nodetype_names[]= +{ + {MY_XPATH_LEX_NODETYPE, "comment" , 7, 0 }, + {MY_XPATH_LEX_NODETYPE, "text" , 4, 0 }, + {MY_XPATH_LEX_NODETYPE, "processing-instruction" , 22,0 }, + {MY_XPATH_LEX_NODETYPE, "node" , 4, 0 }, {0,NULL,0,0} }; @@ -1078,11 +1087,14 @@ static struct my_xpath_keyword_names_st my_keyword_names[] = - Token type, on lookup success. - MY_XPATH_LEX_IDENT, on lookup failure. */ -static int my_xpath_keyword(MY_XPATH *x, const char *beg, const char *end) +static int +my_xpath_keyword(MY_XPATH *x, + struct my_xpath_keyword_names_st *keyword_names, + const char *beg, const char *end) { struct my_xpath_keyword_names_st *k; size_t length= end-beg; - for (k= my_keyword_names; k->name; k++) + for (k= keyword_names; k->name; k++) { if (length == k->length && !strncasecmp(beg, k->name, length)) { @@ -1368,15 +1380,32 @@ my_xpath_lex_scan(MY_XPATH *xpath, beg+= length) /* no op */; lex->end= beg; - // check if a function call - if (*beg == '(' && (xpath->func= my_xpath_function(lex->beg, beg))) + if (beg < end) { - lex->term= MY_XPATH_LEX_FUNC; - return; + if (*beg == '(') + { + /* + check if a function call, e.g.: count(/a/b) + or a nodetype test, e.g.: /a/b/text() + */ + if ((xpath->func= my_xpath_function(lex->beg, beg))) + lex->term= MY_XPATH_LEX_FUNC; + else + lex->term= my_xpath_keyword(xpath, my_nodetype_names, + lex->beg, beg); + return; + } + // check if an axis specifier, e.g.: /a/b/child::* + else if (*beg == ':' && beg + 1 < end && beg[1] == ':') + { + lex->term= my_xpath_keyword(xpath, my_axis_names, + lex->beg, beg); + return; + } } - // check if a keyword - lex->term= my_xpath_keyword(xpath, lex->beg, beg); + lex->term= my_xpath_keyword(xpath, my_keyword_names, + lex->beg, beg); return; } @@ -2329,6 +2358,36 @@ static int my_xpath_parse_Number(MY_XPATH *xpath) /* + Scan NCName. + + SYNOPSYS + + The keywords AND, OR, MOD, DIV are valid identitiers + when they are in identifier context: + + SELECT + ExtractValue('<and><or><mod><div>VALUE</div></mod></or></and>', + '/and/or/mod/div') + -> VALUE + + RETURN + 1 - success + 0 - failure +*/ + +static int +my_xpath_parse_NCName(MY_XPATH *xpath) +{ + return + my_xpath_parse_term(xpath, MY_XPATH_LEX_IDENT) || + my_xpath_parse_term(xpath, MY_XPATH_LEX_AND) || + my_xpath_parse_term(xpath, MY_XPATH_LEX_OR) || + my_xpath_parse_term(xpath, MY_XPATH_LEX_MOD) || + my_xpath_parse_term(xpath, MY_XPATH_LEX_DIV) ? 1 : 0; +} + + +/* QName grammar can be found in a separate document http://www.w3.org/TR/REC-xml-names/#NT-QName @@ -2336,16 +2395,17 @@ static int my_xpath_parse_Number(MY_XPATH *xpath) [7] Prefix ::= NCName [8] LocalPart ::= NCName */ + static int my_xpath_parse_QName(MY_XPATH *xpath) { const char *beg; - if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_IDENT)) + if (!my_xpath_parse_NCName(xpath)) return 0; beg= xpath->prevtok.beg; if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_COLON)) return 1; /* Non qualified name */ - if (!my_xpath_parse_term(xpath, MY_XPATH_LEX_IDENT)) + if (!my_xpath_parse_NCName(xpath)) return 0; xpath->prevtok.beg= beg; return 1; diff --git a/sql/lock.cc b/sql/lock.cc index edef3b3b67f..a93033bfdd0 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -604,7 +604,7 @@ TABLE_LIST *mysql_lock_have_duplicate(THD *thd, TABLE_LIST *needle, for (; haystack; haystack= haystack->next_global) { - if (haystack->placeholder() || haystack->schema_table) + if (haystack->placeholder()) continue; table2= haystack->table; if (table2->s->tmp_table == TMP_TABLE) diff --git a/sql/log.cc b/sql/log.cc index e25f008e90c..3b7d0c84106 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -31,15 +31,6 @@ #include <mysql/plugin.h> -/* - Define placement versions of operator new and operator delete since - we cannot be sure that the <new> include exists. - */ -inline void *operator new(size_t, void *ptr) { return ptr; } -inline void *operator new[](size_t, void *ptr) { return ptr; } -inline void operator delete(void*, void*) { /* Do nothing */ } -inline void operator delete[](void*, void*) { /* Do nothing */ } - /* max size of the log message */ #define MAX_LOG_BUFFER_SIZE 1024 #define MAX_USER_HOST_SIZE 512 @@ -148,6 +139,7 @@ public: void truncate(my_off_t pos) { DBUG_PRINT("info", ("truncating to position %lu", (ulong) pos)); + DBUG_PRINT("info", ("before_stmt_pos=%lu", (ulong) pos)); delete pending(); set_pending(0); reinit_io_cache(&trans_log, WRITE_CACHE, pos, 0, 0); @@ -311,6 +303,7 @@ bool Log_to_csv_event_handler::open_log_table(uint log_table_type) { table->table->use_all_columns(); table->table->locked_by_logger= TRUE; + table->table->no_replicate= TRUE; } /* restore thread settings */ if (curr) @@ -3352,13 +3345,13 @@ bool MYSQL_BIN_LOG::flush_and_sync() return err; } -void MYSQL_BIN_LOG::start_union_events(THD *thd) +void MYSQL_BIN_LOG::start_union_events(THD *thd, query_id_t query_id_param) { DBUG_ASSERT(!thd->binlog_evt_union.do_union); thd->binlog_evt_union.do_union= TRUE; thd->binlog_evt_union.unioned_events= FALSE; thd->binlog_evt_union.unioned_events_trans= FALSE; - thd->binlog_evt_union.first_query_id= thd->query_id; + thd->binlog_evt_union.first_query_id= query_id_param; } void MYSQL_BIN_LOG::stop_union_events(THD *thd) @@ -3473,9 +3466,9 @@ int THD::binlog_flush_transaction_cache() { DBUG_ENTER("binlog_flush_transaction_cache"); binlog_trx_data *trx_data= (binlog_trx_data*) ha_data[binlog_hton->slot]; - DBUG_PRINT("enter", ("trx_data: 0x%lx", (ulong) trx_data)); + DBUG_PRINT("enter", ("trx_data=0x%lu", (ulong) trx_data)); if (trx_data) - DBUG_PRINT("enter", ("trx_data->before_stmt_pos: %lu", + DBUG_PRINT("enter", ("trx_data->before_stmt_pos=%lu", (ulong) trx_data->before_stmt_pos)); /* @@ -4525,7 +4518,7 @@ int TC_LOG_MMAP::open(const char *opt_name) goto err; if (using_heuristic_recover()) return 1; - if ((fd= my_create(logname, O_RDWR, 0, MYF(MY_WME))) < 0) + if ((fd= my_create(logname, CREATE_MODE, O_RDWR, MYF(MY_WME))) < 0) goto err; inited=1; file_length= opt_tc_log_size; diff --git a/sql/log.h b/sql/log.h index 80aa4b20ee6..970823dcd4a 100644 --- a/sql/log.h +++ b/sql/log.h @@ -341,7 +341,7 @@ public: int write_cache(IO_CACHE *cache, bool lock_log, bool flush_and_sync); - void start_union_events(THD *thd); + void start_union_events(THD *thd, query_id_t query_id_param); void stop_union_events(THD *thd); bool is_query_in_union(THD *thd, query_id_t query_id_param); diff --git a/sql/log_event.cc b/sql/log_event.cc index 951cf72d653..f8d3c43bfba 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1130,13 +1130,18 @@ void Log_event::print_header(IO_CACHE* file, char emit_buf[256]; int const bytes_written= my_snprintf(emit_buf, sizeof(emit_buf), - "# %8.8lx %-48.48s |%s|\n# ", + "# %8.8lx %-48.48s |%s|\n", (unsigned long) (hexdump_from + (i & 0xfffffff0)), hex_string, char_string); DBUG_ASSERT(bytes_written >= 0); DBUG_ASSERT(static_cast<my_size_t>(bytes_written) < sizeof(emit_buf)); my_b_write(file, (byte*) emit_buf, bytes_written); } + /* + need a # to prefix the rest of printouts for example those of + Rows_log_event::print_helper(). + */ + my_b_write(file, reinterpret_cast<const byte*>("# "), 2); } DBUG_VOID_RETURN; } @@ -1276,7 +1281,8 @@ bool Query_log_event::write(IO_CACHE* file) 1+4+ // code of autoinc and the 2 autoinc variables 1+6+ // code of charset and charset 1+1+MAX_TIME_ZONE_NAME_LENGTH+ // code of tz and tz length and tz name - 1+2 // code of lc_time_names and lc_time_names_number + 1+2+ // code of lc_time_names and lc_time_names_number + 1+2 // code of charset_database and charset_database_number ], *start, *start_of_status; ulong event_length; @@ -1395,6 +1401,13 @@ bool Query_log_event::write(IO_CACHE* file) int2store(start, lc_time_names_number); start+= 2; } + if (charset_database_number) + { + DBUG_ASSERT(charset_database_number <= 0xFFFF); + *start++= Q_CHARSET_DATABASE_CODE; + int2store(start, charset_database_number); + start+= 2; + } /* Here there could be code like if (command-line-option-which-says-"log_this_variable" && inited) @@ -1460,7 +1473,8 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, sql_mode(thd_arg->variables.sql_mode), auto_increment_increment(thd_arg->variables.auto_increment_increment), auto_increment_offset(thd_arg->variables.auto_increment_offset), - lc_time_names_number(thd_arg->variables.lc_time_names->number) + lc_time_names_number(thd_arg->variables.lc_time_names->number), + charset_database_number(0) { time_t end_time; time(&end_time); @@ -1468,6 +1482,9 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, catalog_len = (catalog) ? (uint32) strlen(catalog) : 0; /* status_vars_len is set just before writing the event */ db_len = (db) ? (uint32) strlen(db) : 0; + if (thd_arg->variables.collation_database != thd_arg->db_charset) + charset_database_number= thd_arg->variables.collation_database->number; + /* If we don't use flags2 for anything else than options contained in thd->options, it would be more efficient to flags2=thd_arg->options @@ -1538,7 +1555,7 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, db(NullS), catalog_len(0), status_vars_len(0), flags2_inited(0), sql_mode_inited(0), charset_inited(0), auto_increment_increment(1), auto_increment_offset(1), - time_zone_len(0), lc_time_names_number(0) + time_zone_len(0), lc_time_names_number(0), charset_database_number(0) { ulong data_len; uint32 tmp; @@ -1643,6 +1660,10 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, lc_time_names_number= uint2korr(pos); pos+= 2; break; + case Q_CHARSET_DATABASE_CODE: + charset_database_number= uint2korr(pos); + pos+= 2; + break; default: /* That's why you must write status vars in growing order of code */ DBUG_PRINT("info",("Query_log_event has unknown status vars (first has\ @@ -1841,6 +1862,16 @@ void Query_log_event::print_query_header(IO_CACHE* file, lc_time_names_number, print_event_info->delimiter); print_event_info->lc_time_names_number= lc_time_names_number; } + if (charset_database_number != print_event_info->charset_database_number) + { + if (charset_database_number) + my_b_printf(file, "SET @@session.collation_database=%d%s\n", + charset_database_number, print_event_info->delimiter); + else + my_b_printf(file, "SET @@session.collation_database=DEFAULT%s\n", + print_event_info->delimiter); + print_event_info->charset_database_number= charset_database_number; + } } @@ -1996,7 +2027,21 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli, } else thd->variables.lc_time_names= &my_locale_en_US; - + if (charset_database_number) + { + CHARSET_INFO *cs; + if (!(cs= get_charset(charset_database_number, MYF(0)))) + { + char buf[20]; + int10_to_str((int) charset_database_number, buf, -10); + my_error(ER_UNKNOWN_COLLATION, MYF(0), buf); + goto compare_errors; + } + thd->variables.collation_database= cs; + } + else + thd->variables.collation_database= thd->db_charset; + /* Execute the query (note that we bypass dispatch_command()) */ mysql_parse(thd, thd->query, thd->query_length); @@ -2241,6 +2286,8 @@ Start_log_event_v3::Start_log_event_v3(const char* buf, binlog_version= uint2korr(buf+ST_BINLOG_VER_OFFSET); memcpy(server_version, buf+ST_SERVER_VER_OFFSET, ST_SERVER_VER_LEN); + // prevent overrun if log is corrupted on disk + server_version[ST_SERVER_VER_LEN-1]= 0; created= uint4korr(buf+ST_CREATED_OFFSET); /* We use log_pos to mark if this was an artificial event or not */ artificial_event= (log_pos == 0); @@ -2364,6 +2411,8 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver) switch (binlog_ver) { case 4: /* MySQL 5.0 */ memcpy(server_version, ::server_version, ST_SERVER_VER_LEN); + DBUG_EXECUTE_IF("pretend_version_50034_in_binlog", + strmov(server_version, "5.0.34");); common_header_len= LOG_EVENT_HEADER_LEN; number_of_event_types= LOG_EVENT_TYPES; /* we'll catch my_malloc() error in is_valid() */ @@ -2454,6 +2503,7 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver) post_header_len= 0; /* will make is_valid() fail */ break; } + calc_server_version_split(); } @@ -2493,6 +2543,7 @@ Format_description_log_event(const char* buf, post_header_len= (uint8*) my_memdup((byte*)buf+ST_COMMON_HEADER_LEN_OFFSET+1, number_of_event_types* sizeof(*post_header_len), MYF(0)); + calc_server_version_split(); DBUG_VOID_RETURN; } @@ -2593,6 +2644,37 @@ int Format_description_log_event::exec_event(struct st_relay_log_info* rli) } #endif + +/** + Splits the event's 'server_version' string into three numeric pieces stored + into 'server_version_split': + X.Y.Zabc (X,Y,Z numbers, a not a digit) -> {X,Y,Z} + X.Yabc -> {X,Y,0} + Xabc -> {X,0,0} + 'server_version_split' is then used for lookups to find if the server which + created this event has some known bug. +*/ +void Format_description_log_event::calc_server_version_split() +{ + char *p= server_version, *r; + ulong number; + for (uint i= 0; i<=2; i++) + { + number= strtoul(p, &r, 10); + server_version_split[i]= (uchar)number; + DBUG_ASSERT(number < 256); // fit in uchar + p= r; + DBUG_ASSERT(!((i == 0) && (*r != '.'))); // should be true in practice + if (*r == '.') + p++; // skip the dot + } + DBUG_PRINT("info",("Format_description_log_event::server_version_split:" + " '%s' %d %d %d", server_version, + server_version_split[0], + server_version_split[1], server_version_split[2])); +} + + /************************************************************************** Load_log_event methods General note about Load_log_event: the binlogging of LOAD DATA INFILE is @@ -3253,8 +3335,8 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli, ex.skip_lines = skip_lines; List<Item> field_list; - thd->main_lex.select_lex.context.resolve_in_table_list_only(&tables); - set_fields(tables.db, field_list, &thd->main_lex.select_lex.context); + thd->lex->select_lex.context.resolve_in_table_list_only(&tables); + set_fields(tables.db, field_list, &thd->lex->select_lex.context); thd->variables.pseudo_thread_id= thread_id; if (net) { @@ -5465,12 +5547,12 @@ int Rows_log_event::do_add_row_data(byte *const row_data, DBUG_ASSERT(m_rows_cur <= m_rows_end); /* The cast will always work since m_rows_cur <= m_rows_end */ - if (static_cast<my_size_t>(m_rows_end - m_rows_cur) < length) + if (static_cast<my_size_t>(m_rows_end - m_rows_cur) <= length) { my_size_t const block_size= 1024; my_ptrdiff_t const cur_size= m_rows_cur - m_rows_buf; my_ptrdiff_t const new_alloc= - block_size * ((cur_size + length) / block_size + block_size - 1); + block_size * ((cur_size + length + block_size - 1) / block_size); byte* const new_buf= (byte*)my_realloc((gptr)m_rows_buf, (uint) new_alloc, MYF(MY_ALLOW_ZERO_PTR|MY_WME)); @@ -5491,7 +5573,7 @@ int Rows_log_event::do_add_row_data(byte *const row_data, m_rows_end= m_rows_buf + new_alloc; } - DBUG_ASSERT(m_rows_cur + length < m_rows_end); + DBUG_ASSERT(m_rows_cur + length <= m_rows_end); memcpy(m_rows_cur, row_data, length); m_rows_cur+= length; m_row_count++; @@ -5741,10 +5823,10 @@ int Rows_log_event::exec_event(st_relay_log_info *rli) need to add code to assert that is the case. */ thd->binlog_flush_pending_rows_event(false); - close_tables_for_reopen(thd, &rli->tables_to_lock); + TABLE_LIST *tables= rli->tables_to_lock; + close_tables_for_reopen(thd, &tables); - if ((error= open_tables(thd, &rli->tables_to_lock, - &rli->tables_to_lock_count, 0))) + if ((error= open_tables(thd, &tables, &rli->tables_to_lock_count, 0))) { if (thd->query_error || thd->is_fatal_error) { @@ -5763,15 +5845,45 @@ int Rows_log_event::exec_event(st_relay_log_info *rli) DBUG_RETURN(error); } } + + /* + When the open and locking succeeded, we check all tables to + ensure that they still have the correct type. + + We can use a down cast here since we know that every table added + to the tables_to_lock is a RPL_TABLE_LIST. + */ + + { + RPL_TABLE_LIST *ptr= rli->tables_to_lock; + for ( ; ptr ; ptr= static_cast<RPL_TABLE_LIST*>(ptr->next_global)) + { + if (ptr->m_tabledef.compatible_with(rli, ptr->table)) + { + mysql_unlock_tables(thd, thd->lock); + thd->lock= 0; + thd->query_error= 1; + rli->clear_tables_to_lock(); + DBUG_RETURN(ERR_BAD_TABLE_DEF); + } + } + } + /* - When the open and locking succeeded, we add all the tables to - the table map and remove them from tables to lock. + ... and then we add all the tables to the table map and remove + them from tables to lock. We also invalidate the query cache for all the tables, since they will now be changed. + + TODO [/Matz]: Maybe the query cache should not be invalidated + here? It might be that a table is not changed, even though it + was locked for the statement. We do know that each + Rows_log_event contain at least one row, so after processing one + Rows_log_event, we can invalidate the query cache for the + associated table. */ - TABLE_LIST *ptr; - for (ptr= rli->tables_to_lock ; ptr ; ptr= ptr->next_global) + for (TABLE_LIST *ptr= rli->tables_to_lock ; ptr ; ptr= ptr->next_global) { rli->m_table_map.set_table(ptr->table_id, ptr->table); } @@ -6042,7 +6154,7 @@ void Rows_log_event::print_helper(FILE *file, { bool const last_stmt_event= get_flags(STMT_END_F); print_header(head, print_event_info, !last_stmt_event); - my_b_printf(head, "\t%s: table id %lu", name, m_table_id); + my_b_printf(head, "\t%s: table id %lu\n", name, m_table_id); print_base64(body, print_event_info, !last_stmt_event); } @@ -6224,11 +6336,11 @@ int Table_map_log_event::exec_event(st_relay_log_info *rli) thd->query_id= next_query_id(); pthread_mutex_unlock(&LOCK_thread_count); - TABLE_LIST *table_list; + RPL_TABLE_LIST *table_list; char *db_mem, *tname_mem; void *const memory= my_multi_malloc(MYF(MY_WME), - &table_list, sizeof(TABLE_LIST), + &table_list, sizeof(RPL_TABLE_LIST), &db_mem, NAME_LEN + 1, &tname_mem, NAME_LEN + 1, NULL); @@ -6274,11 +6386,27 @@ int Table_map_log_event::exec_event(st_relay_log_info *rli) } /* - Open the table if it is not already open and add the table to table map. - Note that for any table that should not be replicated, a filter is needed. + Open the table if it is not already open and add the table to + table map. Note that for any table that should not be + replicated, a filter is needed. + + The creation of a new TABLE_LIST is used to up-cast the + table_list consisting of RPL_TABLE_LIST items. This will work + since the only case where the argument to open_tables() is + changed, is when thd->lex->query_tables == table_list, i.e., + when the statement requires prelocking. Since this is not + executed when a statement is executed, this case will not occur. + As a precaution, an assertion is added to ensure that the bad + case is not a fact. + + Either way, the memory in the list is *never* released + internally in the open_tables() function, hence we take a copy + of the pointer to make sure that it's not lost. */ uint count; - if ((error= open_tables(thd, &table_list, &count, 0))) + DBUG_ASSERT(thd->lex->query_tables != table_list); + TABLE_LIST *tmp_table_list= table_list; + if ((error= open_tables(thd, &tmp_table_list, &count, 0))) { if (thd->query_error || thd->is_fatal_error) { @@ -6305,14 +6433,17 @@ int Table_map_log_event::exec_event(st_relay_log_info *rli) */ DBUG_ASSERT(m_table->in_use); - table_def const def(m_coltype, m_colcnt); - if (def.compatible_with(rli, m_table)) - { - thd->query_error= 1; - error= ERR_BAD_TABLE_DEF; - goto err; - /* purecov: end */ - } + /* + Use placement new to construct the table_def instance in the + memory allocated for it inside table_list. + + The memory allocated by the table_def structure (i.e., not the + memory allocated *for* the table_def structure) is released + inside st_relay_log_info::clear_tables_to_lock() by calling the + table_def destructor explicitly. + */ + new (&table_list->m_tabledef) table_def(m_coltype, m_colcnt); + table_list->m_tabledef_valid= TRUE; /* We record in the slave's information that the table should be diff --git a/sql/log_event.h b/sql/log_event.h index 5994beb0df3..7cbe8925d9a 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -272,6 +272,7 @@ struct sql_ex_info #define Q_LC_TIME_NAMES_CODE 7 +#define Q_CHARSET_DATABASE_CODE 8 /* Intvar event post-header */ #define I_TYPE_OFFSET 0 @@ -533,10 +534,11 @@ typedef struct st_print_event_info char charset[6]; // 3 variables, each of them storable in 2 bytes char time_zone_str[MAX_TIME_ZONE_NAME_LENGTH]; uint lc_time_names_number; + uint charset_database_number; st_print_event_info() :flags2_inited(0), sql_mode_inited(0), auto_increment_increment(1),auto_increment_offset(1), charset_inited(0), - lc_time_names_number(0) + lc_time_names_number(0), charset_database_number(0) { /* Currently we only use static PRINT_EVENT_INFO objects, so zeroed at @@ -546,16 +548,19 @@ typedef struct st_print_event_info bzero(db, sizeof(db)); bzero(charset, sizeof(charset)); bzero(time_zone_str, sizeof(time_zone_str)); - strcpy(delimiter, ";"); - uint const flags = MYF(MY_WME | MY_NABP); - init_io_cache(&head_cache, -1, 0, WRITE_CACHE, 0L, FALSE, flags); - init_io_cache(&body_cache, -1, 0, WRITE_CACHE, 0L, FALSE, flags); + delimiter[0]= ';'; + delimiter[1]= 0; + myf const flags = MYF(MY_WME | MY_NABP); + open_cached_file(&head_cache, NULL, NULL, 0, flags); + open_cached_file(&body_cache, NULL, NULL, 0, flags); } ~st_print_event_info() { - end_io_cache(&head_cache); - end_io_cache(&body_cache); + close_cached_file(&head_cache); + close_cached_file(&body_cache); } + bool init_ok() /* tells if construction was successful */ + { return my_b_inited(&head_cache) && my_b_inited(&body_cache); } /* Settings on how to print the events */ @@ -846,6 +851,7 @@ public: uint time_zone_len; /* 0 means uninited */ const char *time_zone_str; uint lc_time_names_number; /* 0 means en_US */ + uint charset_database_number; #ifndef MYSQL_CLIENT @@ -1153,6 +1159,7 @@ public: uint8 number_of_event_types; /* The list of post-headers' lengthes */ uint8 *post_header_len; + uchar server_version_split[3]; Format_description_log_event(uint8 binlog_ver, const char* server_ver=0); @@ -1184,6 +1191,7 @@ public: */ return FORMAT_DESCRIPTION_HEADER_LEN; } + void calc_server_version_split(); }; @@ -1727,14 +1735,17 @@ public: TYPE_CODE = TABLE_MAP_EVENT }; + /** + Enumeration of the errors that can be returned. + */ enum enum_error { - ERR_OPEN_FAILURE = -1, /* Failure to open table */ - ERR_OK = 0, /* No error */ - ERR_TABLE_LIMIT_EXCEEDED = 1, /* No more room for tables */ - ERR_OUT_OF_MEM = 2, /* Out of memory */ - ERR_BAD_TABLE_DEF = 3, /* Table definition does not match */ - ERR_RBR_TO_SBR = 4 /* daisy-chanining RBR to SBR not allowed */ + ERR_OPEN_FAILURE = -1, /**< Failure to open table */ + ERR_OK = 0, /**< No error */ + ERR_TABLE_LIMIT_EXCEEDED = 1, /**< No more room for tables */ + ERR_OUT_OF_MEM = 2, /**< Out of memory */ + ERR_BAD_TABLE_DEF = 3, /**< Table definition does not match */ + ERR_RBR_TO_SBR = 4 /**< daisy-chanining RBR to SBR not allowed */ }; enum enum_flag @@ -1814,7 +1825,7 @@ private: Row level log event class. - Common base class for all row-level log events. + Common base class for all row-containing log events. RESPONSIBILITIES @@ -1828,6 +1839,19 @@ private: class Rows_log_event : public Log_event { public: + /** + Enumeration of the errors that can be returned. + */ + enum enum_error + { + ERR_OPEN_FAILURE = -1, /**< Failure to open table */ + ERR_OK = 0, /**< No error */ + ERR_TABLE_LIMIT_EXCEEDED = 1, /**< No more room for tables */ + ERR_OUT_OF_MEM = 2, /**< Out of memory */ + ERR_BAD_TABLE_DEF = 3, /**< Table definition does not match */ + ERR_RBR_TO_SBR = 4 /**< daisy-chanining RBR to SBR not allowed */ + }; + /* These definitions allow you to combine the flags into an appropriate flag set using the normal bitwise operators. The @@ -1835,7 +1859,6 @@ public: accepted by the compiler, which is then used to set the real set of flags. */ - enum enum_flag { /* Last event of a statement */ diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index ae9549f93d2..c812aa800c9 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -96,15 +96,18 @@ void net_set_read_timeout(NET *net, uint timeout); #define PREV_BITS(type,A) ((type) (((type) 1 << (A)) -1)) #define all_bits_set(A,B) ((A) & (B) != (B)) -#define WARN_DEPRECATED(Thd,Ver,Old,New) \ - do { \ - DBUG_ASSERT(strncmp(Ver, MYSQL_SERVER_VERSION, sizeof(Ver)-1) >= 0); \ - push_warning_printf(((THD *)Thd), MYSQL_ERROR::WARN_LEVEL_WARN, \ - ER_WARN_DEPRECATED_SYNTAX, ER(ER_WARN_DEPRECATED_SYNTAX), \ - (Old), (Ver), (New)); \ +#define WARN_DEPRECATED(Thd,Ver,Old,New) \ + do { \ + DBUG_ASSERT(strncmp(Ver, MYSQL_SERVER_VERSION, sizeof(Ver)-1) > 0); \ + if (((gptr)Thd) != NULL) \ + push_warning_printf(((THD *)Thd), MYSQL_ERROR::WARN_LEVEL_WARN, \ + ER_WARN_DEPRECATED_SYNTAX, ER(ER_WARN_DEPRECATED_SYNTAX), \ + (Old), (Ver), (New)); \ + else \ + sql_print_warning("The syntax %s is deprecated and will be removed " \ + "in MySQL %s. Please use %s instead.", (Old), (Ver), (New)); \ } while(0) - extern CHARSET_INFO *system_charset_info, *files_charset_info ; extern CHARSET_INFO *national_charset_info, *table_alias_charset; @@ -1579,7 +1582,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..83b16d9af0a 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -367,6 +367,7 @@ my_bool opt_safe_user_create = 0, opt_no_mix_types = 0; my_bool opt_show_slave_auth_info, opt_sql_bin_update = 0; my_bool opt_log_slave_updates= 0; my_bool opt_innodb; +bool slave_warning_issued = false; /* Legacy global handlerton. These will be removed (please do not add more). @@ -454,9 +455,10 @@ my_bool sp_automatic_privileges= 1; ulong opt_binlog_rows_event_max_size; const char *binlog_format_names[]= {"STATEMENT", "ROW", "MIXED", NullS}; TYPELIB binlog_format_typelib= - { array_elements(binlog_format_names)-1,"", + { array_elements(binlog_format_names) - 1, "", binlog_format_names, NULL }; - +ulong opt_binlog_format_id= (ulong) BINLOG_FORMAT_UNSPEC; +const char *opt_binlog_format= binlog_format_names[opt_binlog_format_id]; #ifdef HAVE_INITGROUPS static bool calling_initgroups= FALSE; /* Used in SIGSEGV handler. */ #endif @@ -476,7 +478,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; @@ -1684,18 +1686,6 @@ static void network_init(void) #endif /*!EMBEDDED_LIBRARY*/ -void MYSQLerror(const char *s) -{ - THD *thd=current_thd; - char *yytext= (char*) thd->lex->tok_start; - /* "parse error" changed into "syntax error" between bison 1.75 and 1.875 */ - if (strcmp(s,"parse error") == 0 || strcmp(s,"syntax error") == 0) - s=ER(ER_SYNTAX_ERROR); - my_printf_error(ER_PARSE_ERROR, ER(ER_PARSE_ERROR), MYF(0), s, - (yytext ? (char*) yytext : ""), - thd->lex->yylineno); -} - #ifndef EMBEDDED_LIBRARY /* @@ -2551,6 +2541,14 @@ static int my_message_sql(uint error, const char *str, myf MyFlags) */ if ((thd= current_thd)) { + /* + TODO: There are two exceptions mechanism (THD and sp_rcontext), + this could be improved by having a common stack of handlers. + */ + if (thd->handle_error(error, + MYSQL_ERROR::WARN_LEVEL_ERROR)) + DBUG_RETURN(0); + if (thd->spcont && thd->spcont->handle_error(error, MYSQL_ERROR::WARN_LEVEL_ERROR, thd)) { @@ -3255,17 +3253,24 @@ with --log-bin instead."); "--log-slave-updates work."); unireg_abort(1); } - - if (!opt_bin_log && (global_system_variables.binlog_format != BINLOG_FORMAT_UNSPEC)) - { - sql_print_error("You need to use --log-bin to make " - "--binlog-format work."); - unireg_abort(1); - } - if (global_system_variables.binlog_format == BINLOG_FORMAT_UNSPEC) - { + if (!opt_bin_log) + if (opt_binlog_format_id != BINLOG_FORMAT_UNSPEC) + { + sql_print_error("You need to use --log-bin to make " + "--binlog-format work."); + unireg_abort(1); + } + else + { + global_system_variables.binlog_format= BINLOG_FORMAT_UNSPEC; + } + else + if (opt_binlog_format_id == BINLOG_FORMAT_UNSPEC) global_system_variables.binlog_format= BINLOG_FORMAT_MIXED; - } + else + { + DBUG_ASSERT(global_system_variables.binlog_format != BINLOG_FORMAT_UNSPEC); + } /* Check that we have not let the format to unspecified at this point */ DBUG_ASSERT((uint)global_system_variables.binlog_format <= @@ -3401,7 +3406,7 @@ server."); (TC_LOG *) &tc_log_mmap) : (TC_LOG *) &tc_log_dummy); - if (tc_log->open(opt_bin_logname)) + if (tc_log->open(opt_bin_log ? opt_bin_logname : opt_tc_log_file)) { sql_print_error("Can't init tc log"); unireg_abort(1); @@ -5028,6 +5033,7 @@ struct my_option my_long_options[] = (gptr*) &my_bind_addr_str, (gptr*) &my_bind_addr_str, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"binlog_format", OPT_BINLOG_FORMAT, + "Does not have any effect without '--log-bin'. " "Tell the master the form of binary logging to use: either 'row' for " "row-based binary logging, or 'statement' for statement-based binary " "logging, or 'mixed'. 'mixed' is statement-based binary logging except " @@ -5035,11 +5041,12 @@ struct my_option my_long_options[] = "involve user-defined functions (i.e. UDFs) or the UUID() function; for " "those, row-based binary logging is automatically used. " #ifdef HAVE_NDB_BINLOG - "If ndbcluster is enabled, the default is 'row'." + "If ndbcluster is enabled and binlog_format is `mixed', the format switches" + " to 'row' and back implicitly per each query accessing a NDB table." #endif - , 0, 0, 0, GET_STR, REQUIRED_ARG, - BINLOG_FORMAT_MIXED - , 0, 0, 0, 0, 0 }, + ,(gptr*) &opt_binlog_format, (gptr*) &opt_binlog_format, + 0, GET_STR, REQUIRED_ARG, BINLOG_FORMAT_MIXED, BINLOG_FORMAT_STMT, + BINLOG_FORMAT_MIXED, 0, 0, 0}, {"binlog-do-db", OPT_BINLOG_DO_DB, "Tells the master it should log updates for the specified database, and exclude all others not explicitly mentioned.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, @@ -7105,7 +7112,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"; @@ -7423,7 +7430,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), binlog_format_names[BINLOG_FORMAT_MIXED]); exit(1); } - global_system_variables.binlog_format= id-1; + global_system_variables.binlog_format= opt_binlog_format_id= id - 1; break; } case (int)OPT_BINLOG_DO_DB: @@ -7602,6 +7609,29 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), case (int) OPT_STANDALONE: /* Dummy option for NT */ break; #endif + /* + The following change issues a deprecation warning if the slave + configuration is specified either in the my.cnf file or on + the command-line. See BUG#21490. + */ + case OPT_MASTER_HOST: + case OPT_MASTER_USER: + case OPT_MASTER_PASSWORD: + case OPT_MASTER_PORT: + case OPT_MASTER_CONNECT_RETRY: + case OPT_MASTER_SSL: + case OPT_MASTER_SSL_KEY: + case OPT_MASTER_SSL_CERT: + case OPT_MASTER_SSL_CAPATH: + case OPT_MASTER_SSL_CIPHER: + case OPT_MASTER_SSL_CA: + if (!slave_warning_issued) //only show the warning once + { + slave_warning_issued = true; + WARN_DEPRECATED(NULL, "5.2", "for replication startup options", + "'CHANGE MASTER'"); + } + break; case OPT_CONSOLE: if (opt_console) opt_error_log= 0; // Force logs to stdout diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 6a7b22bf23d..8a051195dba 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -18,6 +18,7 @@ #include "rpl_rli.h" #include <my_dir.h> // For MY_STAT #include "sql_repl.h" // For check_binlog_magic +#include "rpl_utility.h" static int count_relay_log_space(RELAY_LOG_INFO* rli); @@ -1108,4 +1109,23 @@ void st_relay_log_info::cleanup_context(THD *thd, bool error) unsafe_to_stop_at= 0; DBUG_VOID_RETURN; } + +void st_relay_log_info::clear_tables_to_lock() +{ + while (tables_to_lock) + { + gptr to_free= reinterpret_cast<gptr>(tables_to_lock); + if (tables_to_lock->m_tabledef_valid) + { + tables_to_lock->m_tabledef.table_def::~table_def(); + tables_to_lock->m_tabledef_valid= FALSE; + } + tables_to_lock= + static_cast<RPL_TABLE_LIST*>(tables_to_lock->next_global); + tables_to_lock_count--; + my_free(to_free, MYF(MY_WME)); + } + DBUG_ASSERT(tables_to_lock == NULL && tables_to_lock_count == 0); +} + #endif diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h index cb9894a2125..45c9fb1cf96 100644 --- a/sql/rpl_rli.h +++ b/sql/rpl_rli.h @@ -20,6 +20,8 @@ #include "rpl_tblmap.h" +struct RPL_TABLE_LIST; + /**************************************************************************** @@ -279,7 +281,7 @@ typedef struct st_relay_log_info group_relay_log_pos); } - TABLE_LIST *tables_to_lock; /* RBR: Tables to lock */ + RPL_TABLE_LIST *tables_to_lock; /* RBR: Tables to lock */ uint tables_to_lock_count; /* RBR: Count of tables to lock */ table_mapping m_table_map; /* RBR: Mapping table-id to table */ @@ -295,16 +297,7 @@ typedef struct st_relay_log_info void transaction_end(THD*); void cleanup_context(THD *, bool); - void clear_tables_to_lock() { - while (tables_to_lock) - { - char *to_free= reinterpret_cast<gptr>(tables_to_lock); - tables_to_lock= tables_to_lock->next_global; - tables_to_lock_count--; - my_free(to_free, MYF(MY_WME)); - } - DBUG_ASSERT(tables_to_lock == NULL && tables_to_lock_count == 0); - } + void clear_tables_to_lock(); time_t unsafe_to_stop_at; } RELAY_LOG_INFO; diff --git a/sql/rpl_utility.h b/sql/rpl_utility.h index 34cebf93ddb..b1aa642619c 100644 --- a/sql/rpl_utility.h +++ b/sql/rpl_utility.h @@ -22,98 +22,100 @@ #include "mysql_priv.h" +struct st_relay_log_info; +typedef st_relay_log_info RELAY_LOG_INFO; + uint32 -field_length_from_packed(enum_field_types const field_type, - byte const *const data); +field_length_from_packed(enum_field_types field_type, byte const *data); -/* +/** A table definition from the master. - RESPONSIBILITIES - + The responsibilities of this class is: - Extract and decode table definition data from the table map event - Check if table definition in table map is compatible with table definition on slave - DESCRIPTION - - Currently, the only field type data available is an array of the - type operators that are present in the table map event. + Currently, the only field type data available is an array of the + type operators that are present in the table map event. - TODO - - Add type operands to this structure to allow detection of - difference between, e.g., BIT(5) and BIT(10). + @todo Add type operands to this structure to allow detection of + difference between, e.g., BIT(5) and BIT(10). */ class table_def { public: - /* + /** Convenience declaration of the type of the field type data in a table map event. */ typedef unsigned char field_type; - /* + /** Constructor. - SYNOPSIS - table_def() - types Array of types - size Number of elements in array 'types' + @param types Array of types + @param size Number of elements in array 'types' */ table_def(field_type *types, my_size_t size) - : m_type(types), m_size(size) + : m_type(new unsigned char [size]), m_size(size) { + if (m_type) + memcpy(m_type, types, size); + else + m_size= 0; } - /* - Return the number of fields there is type data for. + ~table_def() { + if (m_type) + delete [] m_type; +#ifndef DBUG_OFF + m_type= 0; + m_size= 0; +#endif + } - SYNOPSIS - size() + /** + Return the number of fields there is type data for. - RETURN VALUE - The number of fields that there is type data for. + @return The number of fields that there is type data for. */ my_size_t size() const { return m_size; } + /* Return a representation of the type data for one field. - SYNOPSIS - type() - i Field index to return data for + @param index Field index to return data for - RETURN VALUE - - Will return a representation of the type data for field - 'i'. Currently, only the type identifier is returned. + @return Will return a representation of the type data for field + <code>index</code>. Currently, only the type identifier is + returned. */ - field_type type(my_ptrdiff_t i) const { return m_type[i]; } + field_type type(my_ptrdiff_t index) const + { + DBUG_ASSERT(0 <= index); + DBUG_ASSERT(static_cast<my_size_t>(index) < m_size); + return m_type[index]; + } - /* + /** Decide if the table definition is compatible with a table. - SYNOPSIS - compatible_with() - rli Pointer to relay log info - table Pointer to table to compare with. - - DESCRIPTION - - Compare the definition with a table to see if it is compatible - with it. A table definition is compatible with a table if: + Compare the definition with a table to see if it is compatible + with it. + A table definition is compatible with a table if: - the columns types of the table definition is a (not necessarily proper) prefix of the column type of the table, or - - the other way around - RETURN VALUE - 1 if the table definition is not compatible with 'table' - 0 if the table definition is compatible with 'table' + @param rli Pointer to relay log info + @param table Pointer to table to compare with. + + @retval 1 if the table definition is not compatible with @c table + @retval 0 if the table definition is compatible with @c table */ int compatible_with(RELAY_LOG_INFO *rli, TABLE *table) const; @@ -122,4 +124,15 @@ private: field_type *m_type; // Array of type descriptors }; +/** + Extend the normal table list with a few new fields needed by the + slave thread, but nowhere else. + */ +struct RPL_TABLE_LIST + : public st_table_list +{ + bool m_tabledef_valid; + table_def m_tabledef; +}; + #endif /* RPL_UTILITY_H */ 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/slave.cc b/sql/slave.cc index 12418915ad7..6f62f74647a 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -53,6 +53,7 @@ ulonglong relay_log_space_limit = 0; */ int disconnect_slave_event_count = 0, abort_slave_event_count = 0; +int events_till_abort = -1; typedef enum { SLAVE_THD_IO, SLAVE_THD_SQL} SLAVE_THD_TYPE; @@ -2205,11 +2206,16 @@ err: THD_CHECK_SENTRY(thd); delete thd; pthread_mutex_unlock(&LOCK_thread_count); - mi->abort_slave = 0; - mi->slave_running = 0; - mi->io_thd = 0; - pthread_mutex_unlock(&mi->run_lock); + mi->abort_slave= 0; + mi->slave_running= 0; + mi->io_thd= 0; + /* + Note: the order of the two following calls (first broadcast, then unlock) + is important. Otherwise a killer_thread can execute between the calls and + delete the mi structure leading to a crash! (see BUG#25306 for details) + */ pthread_cond_broadcast(&mi->stop_cond); // tell the world we are done + pthread_mutex_unlock(&mi->run_lock); my_thread_end(); pthread_exit(0); DBUG_RETURN(0); // Can't return anything here @@ -2455,9 +2461,14 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \ THD_CHECK_SENTRY(thd); delete thd; pthread_mutex_unlock(&LOCK_thread_count); + /* + Note: the order of the broadcast and unlock calls below (first broadcast, then unlock) + is important. Otherwise a killer_thread can execute between the calls and + delete the mi structure leading to a crash! (see BUG#25306 for details) + */ pthread_cond_broadcast(&rli->stop_cond); - // tell the world we are done - pthread_mutex_unlock(&rli->run_lock); + pthread_mutex_unlock(&rli->run_lock); // tell the world we are done + my_thread_end(); pthread_exit(0); DBUG_RETURN(0); // Can't return anything here @@ -3653,6 +3664,70 @@ end: } +/** + Detects, based on master's version (as found in the relay log), if master + has a certain bug. + @param rli RELAY_LOG_INFO which tells the master's version + @param bug_id Number of the bug as found in bugs.mysql.com + @return TRUE if master has the bug, FALSE if it does not. +*/ +bool rpl_master_has_bug(RELAY_LOG_INFO *rli, uint bug_id) +{ + struct st_version_range_for_one_bug { + uint bug_id; + const uchar introduced_in[3]; // first version with bug + const uchar fixed_in[3]; // first version with fix + }; + static struct st_version_range_for_one_bug versions_for_all_bugs[]= + { + {24432, { 5, 0, 24 }, { 5, 0, 38 } }, + {24432, { 5, 1, 12 }, { 5, 1, 17 } } + }; + const uchar *master_ver= + rli->relay_log.description_event_for_exec->server_version_split; + + DBUG_ASSERT(sizeof(rli->relay_log.description_event_for_exec->server_version_split) == 3); + + for (uint i= 0; + i < sizeof(versions_for_all_bugs)/sizeof(*versions_for_all_bugs);i++) + { + const uchar *introduced_in= versions_for_all_bugs[i].introduced_in, + *fixed_in= versions_for_all_bugs[i].fixed_in; + if ((versions_for_all_bugs[i].bug_id == bug_id) && + (memcmp(introduced_in, master_ver, 3) <= 0) && + (memcmp(fixed_in, master_ver, 3) > 0)) + { + // a short message for SHOW SLAVE STATUS (message length constraints) + my_printf_error(ER_UNKNOWN_ERROR, "master may suffer from" + " http://bugs.mysql.com/bug.php?id=%u" + " so slave stops; check error log on slave" + " for more info", MYF(0), bug_id); + // a verbose message for the error log + slave_print_msg(ERROR_LEVEL, rli, ER_UNKNOWN_ERROR, + "According to the master's version ('%s')," + " it is probable that master suffers from this bug:" + " http://bugs.mysql.com/bug.php?id=%u" + " and thus replicating the current binary log event" + " may make the slave's data become different from the" + " master's data." + " To take no risk, slave refuses to replicate" + " this event and stops." + " We recommend that all updates be stopped on the" + " master and slave, that the data of both be" + " manually synchronized," + " that master's binary logs be deleted," + " that master be upgraded to a version at least" + " equal to '%d.%d.%d'. Then replication can be" + " restarted.", + rli->relay_log.description_event_for_exec->server_version, + bug_id, + fixed_in[0], fixed_in[1], fixed_in[2]); + return TRUE; + } + } + return FALSE; +} + #ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION template class I_List_iterator<i_string>; template class I_List_iterator<i_string_pair>; diff --git a/sql/slave.h b/sql/slave.h index bc039f6eb75..f21266bbee4 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -159,6 +159,7 @@ int fetch_master_table(THD* thd, const char* db_name, const char* table_name, bool show_master_info(THD* thd, MASTER_INFO* mi); bool show_binlog_info(THD* thd); +bool rpl_master_has_bug(RELAY_LOG_INFO *rli, uint bug_id); const char *print_slave_db_safe(const char *db); int check_expected_error(THD* thd, RELAY_LOG_INFO* rli, int error_code); diff --git a/sql/sp.cc b/sql/sp.cc index 3a7bea6a4b1..0c0bc8e8869 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -498,6 +498,13 @@ db_create_routine(THD *thd, int type, sp_head *sp) DBUG_PRINT("enter", ("type: %d name: %.*s",type,sp->m_name.length, sp->m_name.str)); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + if (!(table= open_proc_table_for_update(thd))) ret= SP_OPEN_TABLE_FAILED; else @@ -634,6 +641,13 @@ db_drop_routine(THD *thd, int type, sp_name *name) DBUG_PRINT("enter", ("type: %d name: %.*s", type, name->m_name.length, name->m_name.str)); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + if (!(table= open_proc_table_for_update(thd))) DBUG_RETURN(SP_OPEN_TABLE_FAILED); if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK) @@ -666,6 +680,13 @@ db_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics) DBUG_PRINT("enter", ("type: %d name: %.*s", type, name->m_name.length, name->m_name.str)); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + if (!(table= open_proc_table_for_update(thd))) DBUG_RETURN(SP_OPEN_TABLE_FAILED); if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK) diff --git a/sql/sp_head.cc b/sql/sp_head.cc index b77d0cc9a0c..1e0986f6e82 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: @@ -1482,8 +1484,24 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, if (need_binlog_call) { + query_id_t q; reset_dynamic(&thd->user_var_events); - mysql_bin_log.start_union_events(thd); + /* + In case of artificially constructed events for function calls + we have separate union for each such event and hence can't use + query_id of real calling statement as the start of all these + unions (this will break logic of replication of user-defined + variables). So we use artifical value which is guaranteed to + be greater than all query_id's of all statements belonging + to previous events/unions. + Possible alternative to this is logging of all function invocations + as one select and not resetting THD::user_var_events before + each invocation. + */ + VOID(pthread_mutex_lock(&LOCK_thread_count)); + q= global_query_id; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + mysql_bin_log.start_union_events(thd, q + 1); binlog_save_options= thd->options; thd->options&= ~OPTION_BIN_LOG; } @@ -2426,16 +2444,11 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, m_lex->mark_as_requiring_prelocking(lex_query_tables_own_last); } } - + reinit_stmt_before_use(thd, m_lex); - /* - If requested check whenever we have access to tables in LEX's table list - and open and lock them before executing instructtions core function. - */ - if (open_tables && - (check_table_access(thd, SELECT_ACL, m_lex->query_tables, 0) || - open_and_lock_tables(thd, m_lex->query_tables))) - res= -1; + + if (open_tables) + res= instr->exec_open_and_lock_tables(thd, m_lex->query_tables, nextp); if (!res) { @@ -2487,6 +2500,33 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, sp_instr class functions */ +int sp_instr::exec_open_and_lock_tables(THD *thd, TABLE_LIST *tables, + uint *nextp) +{ + int result; + + /* + Check whenever we have access to tables for this statement + and open and lock them before executing instructions core function. + */ + if (check_table_access(thd, SELECT_ACL, tables, 0) + || open_and_lock_tables(thd, tables)) + { + get_cont_dest(nextp); + result= -1; + } + else + result= 0; + + return result; +} + +void sp_instr::get_cont_dest(uint *nextp) +{ + *nextp= m_ip+1; +} + + int sp_instr::exec_core(THD *thd, uint *nextp) { DBUG_ASSERT(0); @@ -2672,6 +2712,15 @@ sp_instr_set_trigger_field::print(String *str) value->print(str); } +/* + sp_instr_opt_meta +*/ + +void sp_instr_opt_meta::get_cont_dest(uint *nextp) +{ + *nextp= m_cont_dest; +} + /* sp_instr_jump class functions diff --git a/sql/sp_head.h b/sql/sp_head.h index be6eefa2ea4..46ad3dd96d8 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -477,6 +477,28 @@ public: virtual int execute(THD *thd, uint *nextp) = 0; + /** + Execute <code>open_and_lock_tables()</code> for this statement. + Open and lock the tables used by this statement, as a pre-requisite + to execute the core logic of this instruction with + <code>exec_core()</code>. + If this statement fails, the next instruction to execute is also returned. + This is useful when a user defined SQL continue handler needs to be + executed. + @param thd the current thread + @param tables the list of tables to open and lock + @param nextp the continuation instruction, returned to the caller if this + method fails. + @return zero on success, non zero on failure. + */ + int exec_open_and_lock_tables(THD *thd, TABLE_LIST *tables, uint *nextp); + + /** + Get the continuation destination of this instruction. + @param nextp the continuation destination (output) + */ + virtual void get_cont_dest(uint *nextp); + /* Execute core function of instruction after all preparations (e.g. setting of proper LEX, saving part of the thread context have been @@ -741,6 +763,8 @@ public: virtual void set_destination(uint old_dest, uint new_dest) = 0; + virtual void get_cont_dest(uint *nextp); + protected: sp_instr *m_optdest; // Used during optimization diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 3d3b51bab42..ee7127fcd8d 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -3001,6 +3001,13 @@ bool mysql_table_grant(THD *thd, TABLE_LIST *table_list, tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_WRITE; tables[0].db=tables[1].db=tables[2].db=(char*) "mysql"; + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + #ifdef HAVE_REPLICATION /* GRANT and REVOKE are applied the slave in/exclusion rules as they are @@ -3218,6 +3225,13 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, tables[0].lock_type=tables[1].lock_type=TL_WRITE; tables[0].db=tables[1].db=(char*) "mysql"; + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + #ifdef HAVE_REPLICATION /* GRANT and REVOKE are applied the slave in/exclusion rules as they are @@ -3357,6 +3371,13 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, tables[0].lock_type=tables[1].lock_type=TL_WRITE; tables[0].db=tables[1].db=(char*) "mysql"; + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + #ifdef HAVE_REPLICATION /* GRANT and REVOKE are applied the slave in/exclusion rules as they are @@ -5401,6 +5422,13 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list) TABLE_LIST tables[GRANT_TABLES]; DBUG_ENTER("mysql_create_user"); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + /* CREATE USER may be skipped on replication client. */ if ((result= open_grant_tables(thd, tables))) DBUG_RETURN(result != 1); @@ -5473,6 +5501,13 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list) TABLE_LIST tables[GRANT_TABLES]; DBUG_ENTER("mysql_drop_user"); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + /* DROP USER may be skipped on replication client. */ if ((result= open_grant_tables(thd, tables))) DBUG_RETURN(result != 1); @@ -5537,6 +5572,13 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list) TABLE_LIST tables[GRANT_TABLES]; DBUG_ENTER("mysql_rename_user"); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + /* RENAME USER may be skipped on replication client. */ if ((result= open_grant_tables(thd, tables))) DBUG_RETURN(result != 1); @@ -5612,6 +5654,13 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) TABLE_LIST tables[GRANT_TABLES]; DBUG_ENTER("mysql_revoke_all"); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + if ((result= open_grant_tables(thd, tables))) DBUG_RETURN(result != 1); @@ -5802,6 +5851,13 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, rw_wrlock(&LOCK_grant); VOID(pthread_mutex_lock(&acl_cache->lock)); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + /* Remove procedure access */ do { diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 02f7044a4bf..44325fe7b12 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -28,6 +28,59 @@ #include <io.h> #endif +/** + This internal handler is used to trap internally + errors that can occur when executing open table + during the prelocking phase. +*/ +class Prelock_error_handler : public Internal_error_handler +{ +public: + Prelock_error_handler() + : m_handled_errors(0), m_unhandled_errors(0) + {} + + virtual ~Prelock_error_handler() {} + + virtual bool handle_error(uint sql_errno, + MYSQL_ERROR::enum_warning_level level, + THD *thd); + + bool safely_trapped_errors(); + +private: + int m_handled_errors; + int m_unhandled_errors; +}; + + +bool +Prelock_error_handler::handle_error(uint sql_errno, + MYSQL_ERROR::enum_warning_level /* level */, + THD * /* thd */) +{ + if (sql_errno == ER_NO_SUCH_TABLE) + { + m_handled_errors++; + return TRUE; // 'TRUE', as per coding style + } + + m_unhandled_errors++; + return FALSE; // 'FALSE', as per coding style +} + + +bool Prelock_error_handler::safely_trapped_errors() +{ + /* + If m_unhandled_errors != 0, something else, unanticipated, happened, + so the error is not trapped but returned to the caller. + Multiple ER_NO_SUCH_TABLE can be raised in case of views. + */ + return ((m_handled_errors > 0) && (m_unhandled_errors == 0)); +} + + TABLE *unused_tables; /* Used by mysql_test */ HASH open_cache; /* Used by mysql_test */ static HASH table_def_cache; @@ -1141,12 +1194,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 +1836,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; @@ -1856,6 +1903,13 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, key_length= (create_table_def_key(thd, key, table_list, 1) - TMP_TABLE_KEY_EXTRA); + /* + Unless requested otherwise, try to resolve this table in the list + of temporary tables of this thread. In MySQL temporary tables + are always thread-local and "shadow" possible base tables with the + same name. This block implements the behaviour. + TODO: move this block into a separate function. + */ if (!table_list->skip_temporary) { for (table= thd->temporary_tables; table ; table=table->next) @@ -1865,6 +1919,12 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, !memcmp(table->s->table_cache_key.str, key, key_length + TMP_TABLE_KEY_EXTRA)) { + /* + We're trying to use the same temporary table twice in a query. + Right now we don't support this because a temporary table + is always represented by only one TABLE object in THD, and + it can not be cloned. Emit an error for an unsupported behaviour. + */ if (table->query_id == thd->query_id || thd->prelocked_mode && table->query_id) { @@ -1884,6 +1944,13 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, } } + /* + The table is not temporary - if we're in pre-locked or LOCK TABLES + mode, let's try to find the requested table in the list of pre-opened + and locked tables. If the table is not there, return an error - we can't + open not pre-opened tables in pre-locked/LOCK TABLES mode. + TODO: move this block into a separate function. + */ if (!(flags & MYSQL_OPEN_IGNORE_LOCKED_TABLES) && (thd->locked_tables || thd->prelocked_mode)) { // Using table locks @@ -1955,7 +2022,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, goto reset; } /* - is it view? + Is this table a view and not a base table? (it is work around to allow to open view with locked tables, real fix will be made after definition cache will be made) */ @@ -1983,12 +2050,39 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, VOID(pthread_mutex_unlock(&LOCK_open)); } } - my_error(ER_TABLE_NOT_LOCKED, MYF(0), alias); + if ((thd->locked_tables) && (thd->locked_tables->lock_count > 0)) + my_error(ER_TABLE_NOT_LOCKED, MYF(0), alias); + else + my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias); DBUG_RETURN(0); } + /* + Non pre-locked/LOCK TABLES mode, and the table is not temporary: + this is the normal use case. + Now we should: + - try to find the table in the table cache. + - if one of the discovered TABLE instances is name-locked + (table->s->version == 0) or some thread has started FLUSH TABLES + (refresh_version > table->s->version), back off -- we have to wait + until no one holds a name lock on the table. + - if there is no such TABLE in the name cache, read the table definition + and insert it into the cache. + We perform all of the above under LOCK_open which currently protects + the open cache (also known as table cache) and table definitions stored + on disk. + */ + VOID(pthread_mutex_lock(&LOCK_open)); + /* + If it's the first table from a list of tables used in a query, + remember refresh_version (the version of open_cache state). + If the version changes while we're opening the remaining tables, + we will have to back off, close all the tables opened-so-far, + and try to reopen them. + Note: refresh_version is currently changed only during FLUSH TABLES. + */ if (!thd->open_tables) thd->version=refresh_version; else if ((thd->version != refresh_version) && @@ -2005,6 +2099,16 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, if (thd->handler_tables) mysql_ha_flush(thd, (TABLE_LIST*) NULL, MYSQL_HA_REOPEN_ON_USAGE, TRUE); + /* + Actually try to find the table in the open_cache. + The cache may contain several "TABLE" instances for the same + physical table. The instances that are currently "in use" by + some thread have their "in_use" member != NULL. + There is no good reason for having more than one entry in the + hash for the same physical table, except that we use this as + an implicit "pending locks queue" - see + wait_for_locked_table_names for details. + */ for (table= (TABLE*) hash_first(&open_cache, (byte*) key, key_length, &state); table && table->in_use ; @@ -2014,6 +2118,21 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, /* Here we flush tables marked for flush. However we never flush log tables here. They are flushed only on FLUSH LOGS. + Normally, table->s->version contains the value of + refresh_version from the moment when this table was + (re-)opened and added to the cache. + If since then we did (or just started) FLUSH TABLES + statement, refresh_version has been increased. + For "name-locked" TABLE instances, table->s->version is set + to 0 (see lock_table_name for details). + In case there is a pending FLUSH TABLES or a name lock, we + need to back off and re-start opening tables. + If we do not back off now, we may dead lock in case of lock + order mismatch with some other thread: + c1: name lock t1; -- sort of exclusive lock + c2: open t2; -- sort of shared lock + c1: name lock t2; -- blocks + c2: open t1; -- blocks */ if (table->s->version != refresh_version && !table->s->log_table) { @@ -2029,16 +2148,35 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, } /* - There is a refresh in progress for this table - Wait until the table is freed or the thread is killed. + Back off, part 1: mark the table as "unused" for the + purpose of name-locking by setting table->db_stat to 0. Do + that only for the tables in this thread that have an old + table->s->version (this is an optimization (?)). + table->db_stat == 0 signals wait_for_locked_table_names + that the tables in question are not used any more. See + table_is_used call for details. */ close_old_data_files(thd,thd->open_tables,0,0); + /* + Back-off part 2: try to avoid "busy waiting" on the table: + if the table is in use by some other thread, we suspend + and wait till the operation is complete: when any + operation that juggles with table->s->version completes, + it broadcasts COND_refresh condition variable. + */ if (table->in_use != thd) + { + /* wait_for_conditionwill unlock LOCK_open for us */ wait_for_condition(thd, &LOCK_open, &COND_refresh); + } else { VOID(pthread_mutex_unlock(&LOCK_open)); } + /* + There is a refresh in progress for this table. + Signal the caller that it has to try again. + */ if (refresh) *refresh=1; DBUG_RETURN(0); @@ -2046,6 +2184,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, } if (table) { + /* Unlink the table from "unused_tables" list. */ if (table == unused_tables) { // First unused unused_tables=unused_tables->next; // Remove from link @@ -2058,6 +2197,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, } else { + /* Insert a new TABLE instance into the open cache */ int error; /* Free cache if too big */ while (open_cache.records > table_cache_size && unused_tables) @@ -2866,6 +3006,8 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) MEM_ROOT new_frm_mem; /* Also used for indicating that prelocking is need */ TABLE_LIST **query_tables_last_own; + bool safe_to_ignore_table; + DBUG_ENTER("open_tables"); /* temporary mem_root for new .frm parsing. @@ -2912,8 +3054,13 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) } } + /* + For every table in the list of tables to open, try to find or open + a table. + */ for (tables= *start; tables ;tables= tables->next_global) { + safe_to_ignore_table= FALSE; // 'FALSE', as per coding style /* Ignore placeholders for derived tables. After derived tables processing, link to created temporary table will be put here. @@ -2926,6 +3073,12 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) goto process_view_routines; continue; } + /* + If this TABLE_LIST object is a placeholder for an information_schema + table, create a temporary table to represent the information_schema + table in the query. Do not fill it yet - will be filled during + execution. + */ if (tables->schema_table) { if (!mysql_schema_table(thd, thd->lex, tables)) @@ -2933,9 +3086,32 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) DBUG_RETURN(-1); } (*counter)++; - - if (!tables->table && - !(tables->table= open_table(thd, tables, &new_frm_mem, &refresh, flags))) + + /* + Not a placeholder: must be a base table or a view, and the table is + not opened yet. Try to open the table. + */ + if (!tables->table) + { + if (tables->prelocking_placeholder) + { + /* + For the tables added by the pre-locking code, attempt to open + the table but fail silently if the table does not exist. + The real failure will occur when/if a statement attempts to use + that table. + */ + Prelock_error_handler prelock_handler; + thd->push_internal_handler(& prelock_handler); + tables->table= open_table(thd, tables, &new_frm_mem, &refresh, flags); + thd->pop_internal_handler(); + safe_to_ignore_table= prelock_handler.safely_trapped_errors(); + } + else + tables->table= open_table(thd, tables, &new_frm_mem, &refresh, flags); + } + + if (!tables->table) { free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC)); @@ -2986,6 +3162,14 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) close_tables_for_reopen(thd, start); goto restart; } + + if (safe_to_ignore_table) + { + DBUG_PRINT("info", ("open_table: ignoring table '%s'.'%s'", + tables->db, tables->alias)); + continue; + } + result= -1; // Fatal error break; } @@ -3040,7 +3224,7 @@ process_view_routines: { /* Serious error during reading stored routines from mysql.proc table. - Something's wrong with the table or its contents, and an error has + Something is wrong with the table or its contents, and an error has been emitted; we must abort. */ result= -1; @@ -3289,7 +3473,7 @@ bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags) static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table) { for (; table; table= table->next_global) - if (!table->placeholder() && !table->schema_table) + if (!table->placeholder()) table->table->query_id= 0; } @@ -3362,7 +3546,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) DBUG_RETURN(-1); for (table= tables; table; table= table->next_global) { - if (!table->placeholder() && !table->schema_table) + if (!table->placeholder()) *(ptr++)= table->table; } @@ -3415,7 +3599,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) for (table= tables; table != first_not_own; table= table->next_global) { - if (!table->placeholder() && !table->schema_table) + if (!table->placeholder()) { table->table->query_id= thd->query_id; if (check_lock_and_start_stmt(thd, table->table, table->lock_type)) @@ -3442,7 +3626,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) TABLE_LIST *first_not_own= thd->lex->first_not_own_table(); for (table= tables; table != first_not_own; table= table->next_global) { - if (!table->placeholder() && !table->schema_table && + if (!table->placeholder() && check_lock_and_start_stmt(thd, table->table, table->lock_type)) { ha_rollback_stmt(thd); 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..836a4d07f0b 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -192,14 +192,10 @@ void **thd_ha_data(const THD *thd, const struct handlerton *hton) } -/* - Pass nominal parameters to Statement constructor only to ensure that - the destructor works OK in case of error. The main_mem_root will be - re-initialized in init(). -*/ THD::THD() - :Statement(CONVENTIONAL_EXECUTION, 0, ALLOC_ROOT_MIN_BLOCK_SIZE, 0), + :Statement(&main_lex, &main_mem_root, CONVENTIONAL_EXECUTION, + /* statement id */ 0), Open_tables_state(refresh_version), rli_fake(0), lock_id(&main_lock_id), user_time(0), in_sub_stmt(0), @@ -216,6 +212,12 @@ THD::THD() { ulong tmp; + /* + Pass nominal parameters to init_alloc_root only to ensure that + the destructor works OK in case of an error. The main_mem_root + will be re-initialized in init_for_queries(). + */ + init_sql_alloc(&main_mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0); stmt_arena= this; thread_stack= 0; db= 0; @@ -309,6 +311,38 @@ THD::THD() substitute_null_with_insert_id = FALSE; thr_lock_info_init(&lock_info); /* safety: will be reset after start */ thr_lock_owner_init(&main_lock_id, &lock_info); + + m_internal_handler= NULL; +} + + +void THD::push_internal_handler(Internal_error_handler *handler) +{ + /* + TODO: The current implementation is limited to 1 handler at a time only. + THD and sp_rcontext need to be modified to use a common handler stack. + */ + DBUG_ASSERT(m_internal_handler == NULL); + m_internal_handler= handler; +} + + +bool THD::handle_error(uint sql_errno, + MYSQL_ERROR::enum_warning_level level) +{ + if (m_internal_handler) + { + return m_internal_handler->handle_error(sql_errno, level, this); + } + + return FALSE; // 'FALSE', as per coding style +} + + +void THD::pop_internal_handler() +{ + DBUG_ASSERT(m_internal_handler != NULL); + m_internal_handler= NULL; } @@ -354,6 +388,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, @@ -483,6 +518,7 @@ THD::~THD() delete rli_fake; #endif + free_root(&main_mem_root, MYF(0)); DBUG_VOID_RETURN; } @@ -996,6 +1032,7 @@ sql_exchange::sql_exchange(char *name,bool flag) enclosed= line_start= &my_empty_string; line_term= &default_line_term; escaped= &default_escaped; + cs= NULL; } bool select_send::send_fields(List<Item> &list, uint flags) @@ -1677,18 +1714,17 @@ void Query_arena::cleanup_stmt() Statement functions */ -Statement::Statement(enum enum_state state_arg, ulong id_arg, - ulong alloc_block_size, ulong prealloc_size) - :Query_arena(&main_mem_root, state_arg), +Statement::Statement(LEX *lex_arg, MEM_ROOT *mem_root_arg, + enum enum_state state_arg, ulong id_arg) + :Query_arena(mem_root_arg, state_arg), id(id_arg), mark_used_columns(MARK_COLUMNS_READ), - lex(&main_lex), + lex(lex_arg), query(0), query_length(0), cursor(0) { name.str= NULL; - init_sql_alloc(&main_mem_root, alloc_block_size, prealloc_size); } @@ -1730,7 +1766,7 @@ void Statement::restore_backup_statement(Statement *stmt, Statement *backup) void THD::end_statement() { - /* Cleanup SQL processing state to resuse this statement in next query. */ + /* Cleanup SQL processing state to reuse this statement in next query. */ lex_end(lex); delete lex->result; lex->result= 0; @@ -1771,12 +1807,6 @@ void THD::restore_active_arena(Query_arena *set, Query_arena *backup) Statement::~Statement() { - /* - We must free `main_mem_root', not `mem_root' (pointer), to work - correctly if this statement is used as a backup statement, - for which `mem_root' may point to some other statement. - */ - free_root(&main_mem_root, MYF(0)); } C_MODE_START @@ -2157,7 +2187,12 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup, !current_stmt_binlog_row_based) { options&= ~OPTION_BIN_LOG; - } + } + + if ((backup->options & OPTION_BIN_LOG) && is_update_query(lex->sql_command)&& + !current_stmt_binlog_row_based) + mysql_bin_log.start_union_events(this, this->query_id); + /* Disable result sets */ client_capabilities &= ~CLIENT_MULTI_RESULTS; in_sub_stmt|= new_state; @@ -2201,6 +2236,10 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup) sent_row_count= backup->sent_row_count; client_capabilities= backup->client_capabilities; + if ((options & OPTION_BIN_LOG) && is_update_query(lex->sql_command) && + !current_stmt_binlog_row_based) + mysql_bin_log.stop_union_events(this); + /* The following is added to the old values as we are interested in the total complexity of the query diff --git a/sql/sql_class.h b/sql/sql_class.h index 1f5f7aedbb4..c24e18ccffa 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -452,8 +452,10 @@ public: class Server_side_cursor; -/* - State of a single command executed against this connection. +/** + @class Statement + @brief State of a single command executed against this connection. + One connection can contain a lot of simultaneously running statements, some of which could be: - prepared, that is, contain placeholders, @@ -471,10 +473,6 @@ class Statement: public ilink, public Query_arena Statement(const Statement &rhs); /* not implemented: */ Statement &operator=(const Statement &rhs); /* non-copyable */ public: - /* FIXME: these must be protected */ - MEM_ROOT main_mem_root; - LEX main_lex; - /* Uniquely identifies each statement object in thread scope; change during statement lifetime. FIXME: must be const @@ -525,10 +523,10 @@ public: public: /* This constructor is called for backup statements */ - Statement() { clear_alloc_root(&main_mem_root); } + Statement() {} - Statement(enum enum_state state_arg, ulong id_arg, - ulong alloc_block_size, ulong prealloc_size); + Statement(LEX *lex_arg, MEM_ROOT *mem_root_arg, + enum enum_state state_arg, ulong id_arg); virtual ~Statement(); /* Assign execution context (note: not all members) of given stmt to self */ @@ -540,7 +538,7 @@ public: }; -/* +/** Container for all statements created/used in a connection. Statements in Statement_map have unique Statement::id (guaranteed by id assignment in Statement::Statement) @@ -620,6 +618,10 @@ bool xid_cache_insert(XID *xid, enum xa_states xa_state); bool xid_cache_insert(XID_STATE *xid_state); void xid_cache_delete(XID_STATE *xid_state); +/** + @class Security_context + @brief A set of THD members describing the current authenticated user. +*/ class Security_context { public: @@ -651,7 +653,7 @@ public: }; -/* +/** A registry for item tree transformations performed during query optimization. We register only those changes which require a rollback to re-execute a prepared statement or stored procedure @@ -662,7 +664,7 @@ struct Item_change_record; typedef I_List<Item_change_record> Item_change_list; -/* +/** Type of prelocked mode. See comment for THD::prelocked_mode for complete description. */ @@ -671,7 +673,7 @@ enum prelocked_mode_type {NON_PRELOCKED= 0, PRELOCKED= 1, PRELOCKED_UNDER_LOCK_TABLES= 2}; -/* +/** Class that holds information about tables which were opened and locked by the thread. It is also used to save/restore this information in push_open_tables_state()/pop_open_tables_state(). @@ -774,14 +776,17 @@ public: } }; - -/* class to save context when executing a function or trigger */ +/** + @class Sub_statement_state + @brief Used to save context when executing a function or trigger +*/ /* Defines used for Sub_statement_state::in_sub_stmt */ #define SUB_STMT_TRIGGER 1 #define SUB_STMT_FUNCTION 2 + class Sub_statement_state { public: @@ -813,7 +818,51 @@ enum enum_thread_type }; -/* +/** + This class represents the interface for internal error handlers. + Internal error handlers are exception handlers used by the server + implementation. +*/ +class Internal_error_handler +{ +protected: + Internal_error_handler() {} + virtual ~Internal_error_handler() {} + +public: + /** + Handle an error condition. + This method can be implemented by a subclass to achieve any of the + following: + - mask an error internally, prevent exposing it to the user, + - mask an error and throw another one instead. + When this method returns true, the error condition is considered + 'handled', and will not be propagated to upper layers. + It is the responsability of the code installing an internal handler + to then check for trapped conditions, and implement logic to recover + from the anticipated conditions trapped during runtime. + + This mechanism is similar to C++ try/throw/catch: + - 'try' correspond to <code>THD::push_internal_handler()</code>, + - 'throw' correspond to <code>my_error()</code>, + which invokes <code>my_message_sql()</code>, + - 'catch' correspond to checking how/if an internal handler was invoked, + before removing it from the exception stack with + <code>THD::pop_internal_handler()</code>. + + @param sql_errno the error number + @param level the error level + @param thd the calling thread + @return true if the error is handled + */ + virtual bool handle_error(uint sql_errno, + MYSQL_ERROR::enum_warning_level level, + THD *thd) = 0; +}; + + +/** + @class THD For each client connection we create a separate thread with THD serving as a thread/connection descriptor */ @@ -1632,6 +1681,47 @@ public: return FALSE; } thd_scheduler scheduler; + +public: + /** + Add an internal error handler to the thread execution context. + @param handler the exception handler to add + */ + void push_internal_handler(Internal_error_handler *handler); + + /** + Handle an error condition. + @param sql_errno the error number + @param level the error level + @return true if the error is handled + */ + virtual bool handle_error(uint sql_errno, + MYSQL_ERROR::enum_warning_level level); + + /** + Remove the error handler last pushed. + */ + void pop_internal_handler(); + +private: + /** The current internal error handler for this thread, or NULL. */ + Internal_error_handler *m_internal_handler; + /** + The lex to hold the parsed tree of conventional (non-prepared) queries. + Whereas for prepared and stored procedure statements we use an own lex + instance for each new query, for conventional statements we reuse + the same lex. (@see mysql_parse for details). + */ + LEX main_lex; + /** + This memory root is used for two purposes: + - for conventional queries, to allocate structures stored in main_lex + during parsing, and allocate runtime data (execution plan, etc.) + during execution. + - for prepared queries, only to allocate runtime data. The parsed + tree itself is reused between executions and thus is stored elsewhere. + */ + MEM_ROOT main_mem_root; }; @@ -1656,6 +1746,7 @@ public: bool opt_enclosed; bool dumpfile; ulong skip_lines; + CHARSET_INFO *cs; sql_exchange(char *name,bool dumpfile_flag); }; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 00a68086514..f58d08b8dea 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -59,6 +59,7 @@ #include "sql_trigger.h" #include "sql_select.h" #include "sql_show.h" +#include "slave.h" #ifndef EMBEDDED_LIBRARY static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list); @@ -363,6 +364,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, Name_resolution_context_state ctx_state; #ifndef EMBEDDED_LIBRARY char *query= thd->query; +#endif /* log_on is about delayed inserts only. By default, both logs are enabled (this won't cause problems if the server @@ -370,7 +372,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, */ bool log_on= ((thd->options & OPTION_BIN_LOG) || (!(thd->security_ctx->master_access & SUPER_ACL))); -#endif thr_lock_type lock_type = table_list->lock_type; Item *unused_conds= 0; DBUG_ENTER("mysql_insert"); @@ -391,6 +392,36 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, (duplic == DUP_UPDATE)) lock_type=TL_WRITE; #endif + if ((lock_type == TL_WRITE_DELAYED) && + (global_system_variables.binlog_format == BINLOG_FORMAT_STMT) && + log_on && mysql_bin_log.is_open() && + (values_list.elements > 1)) + { + /* + Statement-based binary logging does not work in this case, because: + a) two concurrent statements may have their rows intermixed in the + queue, leading to autoincrement replication problems on slave (because + the values generated used for one statement don't depend only on the + value generated for the first row of this statement, so are not + replicable) + b) if first row of the statement has an error the full statement is + not binlogged, while next rows of the statement may be inserted. + c) if first row succeeds, statement is binlogged immediately with a + zero error code (i.e. "no error"), if then second row fails, query + will fail on slave too and slave will stop (wrongly believing that the + master got no error). + So we fallback to non-delayed INSERT. + Note that to be fully correct, we should test the "binlog format which + the delayed thread is going to use for this row". But in the common case + where the global binlog format is not changed and the session binlog + format may be changed, that is equal to the global binlog format. + We test it without mutex for speed reasons (condition rarely true), and + in the common case (global not changed) it is as good as without mutex; + if global value is changed, anyway there is uncertainty as the delayed + thread may be old and use the before-the-change value. + */ + lock_type= TL_WRITE; + } table_list->lock_type= lock_type; #ifndef EMBEDDED_LIBRARY @@ -505,6 +536,14 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, thd->cuted_fields = 0L; table->next_number_field=table->found_next_number_field; +#ifdef HAVE_REPLICATION + if (thd->slave_thread && + (info.handle_duplicates == DUP_UPDATE) && + (table->next_number_field != NULL) && + rpl_master_has_bug(&active_mi->rli, 24432)) + goto abort; +#endif + error=0; thd->proc_info="update"; if (duplic != DUP_ERROR || ignore) @@ -1195,14 +1234,13 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) if (res == VIEW_CHECK_ERROR) goto before_trg_err; + table->file->restore_auto_increment(prev_insert_id); if ((error=table->file->ha_update_row(table->record[1], table->record[0]))) { if (info->ignore && !table->file->is_fatal_error(error, HA_CHECK_DUP_KEY)) { - table->file->restore_auto_increment(prev_insert_id); - goto ok_or_after_trg_err; } goto err; @@ -2559,6 +2597,15 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) } restore_record(table,s->default_values); // Get empty record table->next_number_field=table->found_next_number_field; + +#ifdef HAVE_REPLICATION + if (thd->slave_thread && + (info.handle_duplicates == DUP_UPDATE) && + (table->next_number_field != NULL) && + rpl_master_has_bug(&active_mi->rli, 24432)) + DBUG_RETURN(1); +#endif + thd->cuted_fields=0; if (info.ignore || info.handle_duplicates != DUP_ERROR) table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); @@ -3162,8 +3209,7 @@ void select_create::send_error(uint errcode,const char *err) ("Current statement %s row-based", thd->current_stmt_binlog_row_based ? "is" : "is NOT")); DBUG_PRINT("info", - ("Current table (at 0x%lx) %s a temporary (or non-existing) " - "table", + ("Current table (at 0x%lu) %s a temporary (or non-existant) table", (ulong) table, table && !table->s->tmp_table ? "is NOT" : "is")); DBUG_PRINT("info", diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index c7cad2761bf..ef1f0592051 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; @@ -1655,6 +1666,36 @@ void st_select_lex::print_limit(THD *thd, String *str) } } +/** + @brief Restore the LEX and THD in case of a parse error. + + This is a clean up call that is invoked by the Bison generated + parser before returning an error from MYSQLparse. If your + semantic actions manipulate with the global thread state (which + is a very bad practice and should not normally be employed) and + need a clean-up in case of error, and you can not use %destructor + rule in the grammar file itself, this function should be used + to implement the clean up. +*/ + +void st_lex::cleanup_lex_after_parse_error(THD *thd) +{ + /* + Delete sphead for the side effect of restoring of the original + LEX state, thd->lex, thd->mem_root and thd->free_list if they + were replaced when parsing stored procedure statements. We + will never use sphead object after a parse error, so it's okay + to delete it only for the sake of the side effect. + TODO: make this functionality explicit in sp_head class. + Sic: we must nullify the member of the main lex, not the + current one that will be thrown away + */ + if (thd->lex->sphead) + { + delete thd->lex->sphead; + thd->lex->sphead= NULL; + } +} /* Initialize (or reset) Query_tables_list object. diff --git a/sql/sql_lex.h b/sql/sql_lex.h index fa0f9d7cbbb..33d028c829e 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(); }; @@ -1235,6 +1233,10 @@ typedef struct st_lex : public Query_tables_list { return context_stack.head(); } + /* + Restore the LEX and THD in case of a parse error. + */ + static void cleanup_lex_after_parse_error(THD *thd); void reset_n_backup_query_tables_list(Query_tables_list *backup); void restore_backup_query_tables_list(Query_tables_list *backup); diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 364fda2c94b..802eed5116e 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -316,7 +316,8 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, info.handle_duplicates=handle_duplicates; info.escape_char=escaped->length() ? (*escaped)[0] : INT_MAX; - READ_INFO read_info(file,tot_length,thd->variables.collation_database, + READ_INFO read_info(file,tot_length, + ex->cs ? ex->cs : thd->variables.collation_database, *field_term,*ex->line_start, *ex->line_term, *enclosed, info.escape_char, read_file_from_client, is_fifo); if (read_info.error) @@ -458,7 +459,6 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, } sprintf(name, ER(ER_LOAD_INFO), (ulong) info.records, (ulong) info.deleted, (ulong) (info.records - info.copied), (ulong) thd->cuted_fields); - send_ok(thd,info.copied+info.deleted,0L,name); if (!transactional_table) thd->options|=OPTION_STATUS_NO_TRANS_UPDATE; @@ -494,6 +494,8 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, if (transactional_table) error=ha_autocommit_or_rollback(thd,error); + /* ok to client sent only after binlog write and engine commit */ + send_ok(thd, info.copied + info.deleted, 0L, name); err: table->file->ha_release_auto_increment(); if (thd->lock) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 11eb510d6c8..88c27415d5c 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -27,6 +27,7 @@ #include "sp_cache.h" #include "events.h" #include "event_data_objects.h" +#include "sql_trigger.h" /* Used in error handling only */ #define SP_TYPE_STRING(LP) \ @@ -373,6 +374,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) @@ -1512,6 +1514,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 @@ -1596,6 +1683,30 @@ mysql_execute_command(THD *thd) #ifdef HAVE_REPLICATION if (unlikely(thd->slave_thread)) { + if (lex->sql_command == SQLCOM_DROP_TRIGGER) + { + /* + When dropping a trigger, we need to load its table name + before checking slave filter rules. + */ + add_table_for_trigger(thd, thd->lex->spname, 1, &all_tables); + + if (!all_tables) + { + /* + If table name cannot be loaded, + it means the trigger does not exists possibly because + CREATE TRIGGER was previously skipped for this trigger + according to slave filtering rules. + Returning success without producing any errors in this case. + */ + DBUG_RETURN(0); + } + + // force searching in slave.cc:tables_ok() + all_tables->updating= 1; + } + /* Check if statment should be skipped because of slave filtering rules @@ -2321,7 +2432,9 @@ end_with_restore_list: /* ! we write after unlocking the table */ if (!res && !lex->no_write_to_binlog) { - /* Presumably, REPAIR and binlog writing doesn't require synchronization */ + /* + Presumably, REPAIR and binlog writing doesn't require synchronization + */ if (mysql_bin_log.is_open()) { thd->clear_error(); // No binlog error generated @@ -2354,7 +2467,9 @@ end_with_restore_list: /* ! we write after unlocking the table */ if (!res && !lex->no_write_to_binlog) { - /* Presumably, ANALYZE and binlog writing doesn't require synchronization */ + /* + Presumably, ANALYZE and binlog writing doesn't require synchronization + */ if (mysql_bin_log.is_open()) { thd->clear_error(); // No binlog error generated @@ -2379,7 +2494,9 @@ end_with_restore_list: /* ! we write after unlocking the table */ if (!res && !lex->no_write_to_binlog) { - /* Presumably, OPTIMIZE and binlog writing doesn't require synchronization */ + /* + Presumably, OPTIMIZE and binlog writing doesn't require synchronization + */ if (mysql_bin_log.is_open()) { thd->clear_error(); // No binlog error generated @@ -3015,6 +3132,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 +3191,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; @@ -3512,83 +3633,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) { @@ -3628,9 +3674,6 @@ end_with_restore_list: clean up the environment. */ create_sp_error: - lex->unit.cleanup(); - delete lex->sphead; - lex->sphead= 0; if (sp_result != SP_OK ) goto error; send_ok(thd); @@ -3850,7 +3893,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 @@ -4006,9 +4049,6 @@ create_sp_error: /* Conditionally writes to binlog. */ res= mysql_create_or_drop_trigger(thd, all_tables, 1); - /* We don't care about trigger body after this point */ - delete lex->sphead; - lex->sphead= 0; break; } case SQLCOM_DROP_TRIGGER: @@ -5162,12 +5202,7 @@ void mysql_parse(THD *thd, char *inBuf, uint length) else #endif { - if (thd->net.report_error) - { - delete lex->sphead; - lex->sphead= NULL; - } - else + if (! thd->net.report_error) { /* Binlog logs a string starting from thd->query and having length @@ -5187,7 +5222,6 @@ void mysql_parse(THD *thd, char *inBuf, uint length) query_cache_end_of_result(thd); } } - lex->unit.cleanup(); } else { @@ -5195,19 +5229,14 @@ void mysql_parse(THD *thd, char *inBuf, uint length) DBUG_PRINT("info",("Command aborted. Fatal_error: %d", thd->is_fatal_error)); - /* - 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; - } query_cache_abort(&thd->net); - lex->unit.cleanup(); } + if (thd->lex->sphead) + { + delete thd->lex->sphead; + thd->lex->sphead= 0; + } + lex->unit.cleanup(); thd->proc_info="freeing items"; thd->end_statement(); thd->cleanup_after_query(); @@ -6170,7 +6199,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, 0); /* purecov: inspected */ *write_to_binlog= tmp_write_to_binlog; return result; } diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 7de8a9a64ec..ce6072b2a63 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -99,9 +99,12 @@ public: #endif }; -/****************************************************************************** - Prepared_statement: a statement that can contain placeholders -******************************************************************************/ +/****************************************************************************/ + +/** + @class Prepared_statement + @brief Prepared_statement: a statement that can contain placeholders +*/ class Prepared_statement: public Statement { @@ -141,6 +144,16 @@ public: bool execute(String *expanded_query, bool open_cursor); /* Destroy this statement */ bool deallocate(); +private: + /** + Store the parsed tree of a prepared statement here. + */ + LEX main_lex; + /** + The memory root to allocate parsed tree elements (instances of Item, + SELECT_LEX and other classes). + */ + MEM_ROOT main_mem_root; }; @@ -2073,6 +2086,7 @@ void mysql_sql_stmt_prepare(THD *thd) delete stmt; DBUG_VOID_RETURN; } + if (thd->stmt_map.insert(thd, stmt)) { /* The statement is deleted and an error is set if insert fails */ @@ -2667,17 +2681,18 @@ Select_fetch_protocol_prep::send_data(List<Item> &fields) ****************************************************************************/ Prepared_statement::Prepared_statement(THD *thd_arg, Protocol *protocol_arg) - :Statement(INITIALIZED, ++thd_arg->statement_id_counter, - thd_arg->variables.query_alloc_block_size, - thd_arg->variables.query_prealloc_size), + :Statement(&main_lex, &main_mem_root, + INITIALIZED, ++thd_arg->statement_id_counter), thd(thd_arg), result(thd_arg), protocol(protocol_arg), param_array(0), param_count(0), last_errno(0), - flags((uint) IS_IN_USE) + flags((uint) IS_IN_USE) { + init_alloc_root(&main_mem_root, thd_arg->variables.query_alloc_block_size, + thd_arg->variables.query_prealloc_size); *last_error= '\0'; } @@ -2727,6 +2742,7 @@ Prepared_statement::~Prepared_statement() */ free_items(); delete lex->result; + free_root(&main_mem_root, MYF(0)); DBUG_VOID_RETURN; } @@ -2742,6 +2758,7 @@ void Prepared_statement::cleanup_stmt() DBUG_ENTER("Prepared_statement::cleanup_stmt"); DBUG_PRINT("enter",("stmt: 0x%lx", (long) this)); + DBUG_ASSERT(lex->sphead == 0); /* The order is important */ lex->unit.cleanup(); cleanup_items(free_list); @@ -2827,18 +2844,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) error= MYSQLparse((void *)thd) || thd->is_fatal_error || thd->net.report_error || init_param_array(this); - /* - The first thing we do after parse error is freeing sp_head to - ensure that we have restored original memroot. - */ - if (error && lex->sphead) - { - delete lex->sphead; - lex->sphead= NULL; - } - lex->safe_to_cache_query= FALSE; - /* While doing context analysis of the query (in check_prepared_statement) we allocate a lot of additional memory: for open tables, JOINs, derived @@ -2864,12 +2870,18 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) if (error == 0) error= check_prepared_statement(this, name.str != 0); - /* Free sp_head if check_prepared_statement() failed. */ - if (error && lex->sphead) + /* + Currently CREATE PROCEDURE/TRIGGER/EVENT are prohibited in prepared + statements: ensure we have no memory leak here if by someone tries + to PREPARE stmt FROM "CREATE PROCEDURE ..." + */ + DBUG_ASSERT(lex->sphead == NULL || error != 0); + if (lex->sphead) { delete lex->sphead; lex->sphead= NULL; } + lex_end(lex); cleanup_stmt(); thd->restore_backup_statement(this, &stmt_backup); diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 1a8446a86e9..debc9a7b572 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -886,12 +886,14 @@ int start_slave(THD* thd , MASTER_INFO* mi, bool net_report) int stop_slave(THD* thd, MASTER_INFO* mi, bool net_report ) { + DBUG_ENTER("stop_slave"); + int slave_errno; if (!thd) thd = current_thd; if (check_access(thd, SUPER_ACL, any_db,0,0,0,0)) - return 1; + DBUG_RETURN(1); thd->proc_info = "Killing slave"; int thread_mask; lock_slave_threads(mi); @@ -925,12 +927,12 @@ int stop_slave(THD* thd, MASTER_INFO* mi, bool net_report ) { if (net_report) my_message(slave_errno, ER(slave_errno), MYF(0)); - return 1; + DBUG_RETURN(1); } else if (net_report) send_ok(thd); - return 0; + DBUG_RETURN(0); } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 250d9d917eb..881cf7dc6c4 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2532,7 +2532,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) ST_SCHEMA_TABLE *schema_table= tables->schema_table; SELECT_LEX sel; INDEX_FIELD_VALUES idx_field_vals; - char path[FN_REFLEN], *end, *base_name, *orig_base_name, *file_name; + char path[FN_REFLEN], *base_name, *orig_base_name, *file_name; uint len; bool with_i_schema; enum enum_schema_tables schema_table_idx; @@ -2550,7 +2550,6 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) #endif DBUG_ENTER("get_all_tables"); - LINT_INIT(end); LINT_INIT(len); lex->view_prepare_mode= TRUE; @@ -2642,7 +2641,6 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) else { len= build_table_filename(path, sizeof(path), base_name, "", "", 0); - end= path + len; len= FN_LEN - len; find_files_result res= find_files(thd, &files, base_name, path, idx_field_vals.table_value, 0); @@ -2692,7 +2690,9 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) } else { - my_snprintf(end, len, "/%s%s", file_name, reg_ext); + build_table_filename(path, sizeof(path), + base_name, file_name, reg_ext, 0); + switch (mysql_frm_type(thd, path, ¬_used)) { case FRMTYPE_ERROR: table->field[3]->store(STRING_WITH_LEN("ERROR"), diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index df363c3c21c..c8680aecd50 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -106,10 +106,6 @@ const LEX_STRING trg_event_type_names[]= }; -static int -add_table_for_trigger(THD *thd, sp_name *trig, bool if_exists, - TABLE_LIST ** table); - class Handle_old_incorrect_sql_modes_hook: public Unknown_key_hook { private: @@ -985,11 +981,8 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, thd->spcont= 0; if (MYSQLparse((void *)thd) || thd->is_fatal_error) { - /* - Free lex associated resources. - QQ: Do we really need all this stuff here ? - */ - delete lex.sphead; + /* Currently sphead is always deleted in case of a parse error */ + DBUG_ASSERT(lex.sphead == 0); goto err_with_lex_cleanup; } @@ -1180,7 +1173,7 @@ bool Table_triggers_list::get_trigger_info(THD *thd, trg_event_type event, 1 Error */ -static int +int add_table_for_trigger(THD *thd, sp_name *trig, bool if_exists, TABLE_LIST **table) { diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h index 75dda6be1cf..707fcc4e380 100644 --- a/sql/sql_trigger.h +++ b/sql/sql_trigger.h @@ -137,3 +137,7 @@ private: extern const LEX_STRING trg_action_time_type_names[]; extern const LEX_STRING trg_event_type_names[]; + +int +add_table_for_trigger(THD *thd, sp_name *trig, bool if_exists, + TABLE_LIST **table); diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 02a90b942bf..27a58a295ff 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -907,7 +907,7 @@ reopen_tables: tl->lock_type= using_update_log ? TL_READ_NO_INSERT : TL_READ; tl->updating= 0; /* Update TABLE::lock_type accordingly. */ - if (!tl->placeholder() && !tl->schema_table && !using_lock_tables) + if (!tl->placeholder() && !using_lock_tables) tl->table->reginfo.lock_type= tl->lock_type; } } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 297bd9f2737..5d24fb4fa65 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -47,11 +47,18 @@ const LEX_STRING null_lex_str={0,0}; #define yyoverflow(A,B,C,D,E,F) {ulong val= *(F); if (my_yyoverflow((B), (D), &val)) { yyerror((char*) (A)); return 2; } else { *(F)= (YYSIZE_T)val; }} -#define YYERROR_UNLESS(A) \ +#define MYSQL_YYABORT \ + do \ + { \ + LEX::cleanup_lex_after_parse_error(YYTHD);\ + YYABORT; \ + } while (0) + +#define MYSQL_YYABORT_UNLESS(A) \ if (!(A)) \ - { \ - yyerror(ER(ER_SYNTAX_ERROR)); \ - YYABORT; \ + { \ + my_parse_error(ER(ER_SYNTAX_ERROR));\ + MYSQL_YYABORT; \ } /* @@ -76,19 +83,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 @@ -96,6 +90,66 @@ inline Item *is_truth_value(THD *thd, Item *A, bool v1, bool v2) #define YYDEBUG 0 #endif +/** + @brief Push an error message into MySQL error stack with line + and position information. + + This function provides semantic action implementers with a way + to push the famous "You have a syntax error near..." error + message into the error stack, which is normally produced only if + a parse error is discovered internally by the Bison generated + parser. +*/ + +void my_parse_error(const char *s) +{ + THD *thd= current_thd; + + char *yytext= (char*) thd->lex->tok_start; + /* Push an error into the error stack */ + my_printf_error(ER_PARSE_ERROR, ER(ER_PARSE_ERROR), MYF(0), s, + (yytext ? (char*) yytext : ""), + thd->lex->yylineno); +} + +/** + @brief Bison callback to report a syntax/OOM error + + This function is invoked by the bison-generated parser + when a syntax error, a parse error or an out-of-memory + condition occurs. This function is not invoked when the + parser is requested to abort by semantic action code + by means of YYABORT or YYACCEPT macros. This is why these + macros should not be used (use MYSQL_YYABORT/MYSQL_YYACCEPT + instead). + + The parser will abort immediately after invoking this callback. + + This function is not for use in semantic actions and is internal to + the parser, as it performs some pre-return cleanup. + In semantic actions, please use my_parse_error or my_error to + push an error into the error stack and MYSQL_YYABORT + to abort from the parser. +*/ + +void MYSQLerror(const char *s) +{ + THD *thd= current_thd; + + /* + Restore the original LEX if it was replaced when parsing + a stored procedure. We must ensure that a parsing error + does not leave any side effects in the THD. + */ + LEX::cleanup_lex_after_parse_error(thd); + + /* "parse error" changed into "syntax error" between bison 1.75 and 1.875 */ + if (strcmp(s,"parse error") == 0 || strcmp(s,"syntax error") == 0) + s= ER(ER_SYNTAX_ERROR); + my_parse_error(s); +} + + #ifndef DBUG_OFF void turn_parser_debug_on() { @@ -310,6 +364,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; @@ -359,6 +488,11 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %} %pure_parser /* We have threads */ +/* + Currently there is 287 shift/reduce conflict. We should not introduce + new conflicts any more. +*/ +%expect 287 /* Comments for TOKENS. @@ -1043,7 +1177,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 @@ -1067,6 +1201,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); old_or_new_charset_name_or_default collation_name collation_name_or_default + opt_load_data_charset %type <variable> internal_variable_name @@ -1150,7 +1285,7 @@ query: (!(thd->lex->select_lex.options & OPTION_FOUND_COMMENT))) { my_message(ER_EMPTY_QUERY, ER(ER_EMPTY_QUERY), MYF(0)); - YYABORT; + MYSQL_YYABORT; } else { @@ -1226,8 +1361,8 @@ deallocate: LEX *lex= thd->lex; if (lex->stmt_prepare_mode) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } lex->sql_command= SQLCOM_DEALLOCATE_PREPARE; lex->prepared_stmt_name= $3; @@ -1246,8 +1381,8 @@ prepare: LEX *lex= thd->lex; if (lex->stmt_prepare_mode) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } lex->sql_command= SQLCOM_PREPARE; lex->prepared_stmt_name= $2; @@ -1276,8 +1411,8 @@ execute: LEX *lex= thd->lex; if (lex->stmt_prepare_mode) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } lex->sql_command= SQLCOM_EXECUTE; lex->prepared_stmt_name= $2; @@ -1301,7 +1436,7 @@ execute_var_ident: '@' ident_or_text LEX *lex=Lex; LEX_STRING *lexstr= (LEX_STRING*)sql_memdup(&$2, sizeof(LEX_STRING)); if (!lexstr || lex->prepared_stmt_params.push_back(lexstr)) - YYABORT; + MYSQL_YYABORT; } ; @@ -1313,7 +1448,7 @@ help: if (Lex->sphead) { my_error(ER_SP_BADSTATEMENT, MYF(0), "HELP"); - YYABORT; + MYSQL_YYABORT; } } ident_or_text @@ -1440,7 +1575,7 @@ create: (using_update_log ? TL_READ_NO_INSERT: TL_READ))) - YYABORT; + MYSQL_YYABORT; lex->create_list.empty(); lex->key_list.empty(); lex->col_list.empty(); @@ -1463,7 +1598,7 @@ create: if (!lex->current_select->add_table_to_list(lex->thd, $7, NULL, TL_OPTION_UPDATING)) - YYABORT; + MYSQL_YYABORT; lex->create_list.empty(); lex->key_list.empty(); lex->col_list.empty(); @@ -1474,8 +1609,8 @@ create: LEX *lex=Lex; if ($2 != Key::FULLTEXT && lex->key_create_info.parser_name.str) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } lex->key_list.push_back(new Key($2, $4.str, &lex->key_create_info, 0, lex->col_list)); @@ -1579,7 +1714,7 @@ event_tail: Lex->create_info.options= $2; if (!(Lex->event_parse_data= Event_parse_data::new_instance(YYTHD))) - YYABORT; + MYSQL_YYABORT; Lex->event_parse_data->identifier= $3; /* @@ -1710,11 +1845,11 @@ ev_sql_stmt: if (lex->sphead) { my_error(ER_EVENT_RECURSIVITY_FORBIDDEN, MYF(0)); - YYABORT; + MYSQL_YYABORT; } if (!(lex->sphead= new sp_head())) - YYABORT; + MYSQL_YYABORT; lex->sphead->reset_thd_mem_root(YYTHD); lex->sphead->init(lex); @@ -1780,12 +1915,12 @@ sp_name: if (!$1.str || check_db_name(&$1)) { my_error(ER_WRONG_DB_NAME, MYF(0), $1.str); - YYABORT; + MYSQL_YYABORT; } if (check_routine_name($3)) { my_error(ER_SP_WRONG_NAME, MYF(0), $3.str); - YYABORT; + MYSQL_YYABORT; } $$= new sp_name($1, $3); $$->init_qname(YYTHD); @@ -1797,10 +1932,10 @@ sp_name: if (check_routine_name($1)) { my_error(ER_SP_WRONG_NAME, MYF(0), $1.str); - YYABORT; + MYSQL_YYABORT; } if (thd->copy_db_to(&db.str, &db.length)) - YYABORT; + MYSQL_YYABORT; $$= new sp_name(db, $1); if ($$) $$->init_qname(YYTHD); @@ -1821,13 +1956,13 @@ create_function_tail: and is considered a parsing error. */ my_error(ER_WRONG_USAGE, MYF(0), "SONAME", "DEFINER"); - YYABORT; + MYSQL_YYABORT; } if (is_native_function(thd, & lex->spname->m_name)) { my_error(ER_NATIVE_FCT_NAME_COLLISION, MYF(0), lex->spname->m_name.str); - YYABORT; + MYSQL_YYABORT; } lex->sql_command = SQLCOM_CREATE_FUNCTION; lex->udf.name = lex->spname->m_name; @@ -1846,13 +1981,13 @@ create_function_tail: if (lex->udf.type == UDFTYPE_AGGREGATE) { my_error(ER_SP_NO_AGGREGATE, MYF(0)); - YYABORT; + MYSQL_YYABORT; } if (lex->sphead) { my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "FUNCTION"); - YYABORT; + MYSQL_YYABORT; } /* Order is important here: new - reset - init */ sp= new sp_head(); @@ -1898,13 +2033,13 @@ create_function_tail: && (lex->type & BINCMP_FLAG)) { my_error(ER_NOT_SUPPORTED_YET, MYF(0), "return value collation"); - YYABORT; + MYSQL_YYABORT; } if (sp->fill_field_definition(YYTHD, lex, (enum enum_field_types) $8, &sp->m_return_field_def)) - YYABORT; + MYSQL_YYABORT; bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); } @@ -1922,14 +2057,14 @@ create_function_tail: sp_head *sp= lex->sphead; if (sp->is_not_allowed_in_function("function")) - YYABORT; + MYSQL_YYABORT; lex->sql_command= SQLCOM_CREATE_SPFUNCTION; sp->init_strings(thd, lex); if (!(sp->m_flags & sp_head::HAS_RETURN)) { my_error(ER_SP_NORETURN, MYF(0), sp->m_qname.str); - YYABORT; + MYSQL_YYABORT; } if (is_native_function(thd, & sp->m_name)) { @@ -2093,7 +2228,7 @@ sp_fdparam: if (spc->find_variable(&$1, TRUE)) { my_error(ER_SP_DUP_PARAM, MYF(0), $1.str); - YYABORT; + MYSQL_YYABORT; } sp_variable_t *spvar= spc->push_variable(&$1, (enum enum_field_types)$3, @@ -2103,7 +2238,7 @@ sp_fdparam: (enum enum_field_types) $3, &spvar->field_def)) { - YYABORT; + MYSQL_YYABORT; } spvar->field_def.field_name= spvar->name.str; spvar->field_def.pack_flag |= FIELDFLAG_MAYBE_NULL; @@ -2130,7 +2265,7 @@ sp_pdparam: if (spc->find_variable(&$3, TRUE)) { my_error(ER_SP_DUP_PARAM, MYF(0), $3.str); - YYABORT; + MYSQL_YYABORT; } sp_variable_t *spvar= spc->push_variable(&$3, (enum enum_field_types)$4, @@ -2140,7 +2275,7 @@ sp_pdparam: (enum enum_field_types) $4, &spvar->field_def)) { - YYABORT; + MYSQL_YYABORT; } spvar->field_def.field_name= spvar->name.str; spvar->field_def.pack_flag |= FIELDFLAG_MAYBE_NULL; @@ -2179,13 +2314,13 @@ sp_decls: { /* Variable or condition following cursor or handler */ my_message(ER_SP_VARCOND_AFTER_CURSHNDLR, ER(ER_SP_VARCOND_AFTER_CURSHNDLR), MYF(0)); - YYABORT; + MYSQL_YYABORT; } if ($2.curs && $1.hndlrs) { /* Cursor following handler */ my_message(ER_SP_CURSOR_AFTER_HANDLER, ER(ER_SP_CURSOR_AFTER_HANDLER), MYF(0)); - YYABORT; + MYSQL_YYABORT; } $$.vars= $1.vars + $2.vars; $$.conds= $1.conds + $2.conds; @@ -2223,7 +2358,7 @@ sp_decl: sp_variable_t *spvar= pctx->find_variable(var_idx); if (!spvar) - YYABORT; + MYSQL_YYABORT; spvar->type= var_type; spvar->dflt= dflt_value_item; @@ -2231,7 +2366,7 @@ sp_decl: if (lex->sphead->fill_field_definition(YYTHD, lex, var_type, &spvar->field_def)) { - YYABORT; + MYSQL_YYABORT; } spvar->field_def.field_name= spvar->name.str; @@ -2259,7 +2394,7 @@ sp_decl: if (spc->find_cond(&$2, TRUE)) { my_error(ER_SP_DUP_COND, MYF(0), $2.str); - YYABORT; + MYSQL_YYABORT; } YYTHD->lex->spcont->push_cond(&$2, $5); $$.vars= $$.hndlrs= $$.curs= 0; @@ -2317,7 +2452,7 @@ sp_decl: { my_error(ER_SP_DUP_CURS, MYF(0), $2.str); delete $5; - YYABORT; + MYSQL_YYABORT; } i= new sp_instr_cpush(sp->instructions(), ctx, $5, ctx->current_cursor_count()); @@ -2348,13 +2483,13 @@ sp_cursor_stmt: { my_message(ER_SP_BAD_CURSOR_QUERY, ER(ER_SP_BAD_CURSOR_QUERY), MYF(0)); - YYABORT; + MYSQL_YYABORT; } if (lex->result) { my_message(ER_SP_BAD_CURSOR_SELECT, ER(ER_SP_BAD_CURSOR_SELECT), MYF(0)); - YYABORT; + MYSQL_YYABORT; } lex->sp_lex_in_use= TRUE; $$= lex; @@ -2378,7 +2513,7 @@ sp_hcond_list: if (ctx->find_handler($1)) { my_message(ER_SP_DUP_HANDLER, ER(ER_SP_DUP_HANDLER), MYF(0)); - YYABORT; + MYSQL_YYABORT; } else { @@ -2399,7 +2534,7 @@ sp_hcond_list: if (ctx->find_handler($3)) { my_message(ER_SP_DUP_HANDLER, ER(ER_SP_DUP_HANDLER), MYF(0)); - YYABORT; + MYSQL_YYABORT; } else { @@ -2425,7 +2560,7 @@ sp_cond: if (!sp_cond_check(&$3)) { my_error(ER_SP_BAD_SQLSTATE, MYF(0), $3.str); - YYABORT; + MYSQL_YYABORT; } $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t)); $$->type= sp_cond_type_t::state; @@ -2450,7 +2585,7 @@ sp_hcond: if ($$ == NULL) { my_error(ER_SP_COND_MISMATCH, MYF(0), $1.str); - YYABORT; + MYSQL_YYABORT; } } | SQLWARNING_SYM /* SQLSTATEs 01??? */ @@ -2481,7 +2616,7 @@ sp_decl_idents: if (spc->find_variable(&$1, TRUE)) { my_error(ER_SP_DUP_VAR, MYF(0), $1.str); - YYABORT; + MYSQL_YYABORT; } spc->push_variable(&$1, (enum_field_types)0, sp_param_in); $$= 1; @@ -2496,7 +2631,7 @@ sp_decl_idents: if (spc->find_variable(&$3, TRUE)) { my_error(ER_SP_DUP_VAR, MYF(0), $3.str); - YYABORT; + MYSQL_YYABORT; } spc->push_variable(&$3, (enum_field_types)0, sp_param_in); $$= $1 + 1; @@ -2544,7 +2679,7 @@ sp_proc_stmt_statement: if (lex->sql_command == SQLCOM_CHANGE_DB) { /* "USE db" doesn't work in a procedure */ my_error(ER_SP_BADSTATEMENT, MYF(0), "USE"); - YYABORT; + MYSQL_YYABORT; } /* Don't add an instruction for SET statements, since all @@ -2587,7 +2722,7 @@ sp_proc_stmt_return: if (sp->m_type != TYPE_ENUM_FUNCTION) { my_message(ER_SP_BADRETURN, ER(ER_SP_BADRETURN), MYF(0)); - YYABORT; + MYSQL_YYABORT; } else { @@ -2627,7 +2762,7 @@ sp_proc_stmt_leave: if (! lab) { my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "LEAVE", $2.str); - YYABORT; + MYSQL_YYABORT; } else { @@ -2659,7 +2794,7 @@ sp_proc_stmt_iterate: if (! lab || lab->type != SP_LAB_ITER) { my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "ITERATE", $2.str); - YYABORT; + MYSQL_YYABORT; } else { @@ -2690,7 +2825,7 @@ sp_proc_stmt_open: if (! lex->spcont->find_cursor(&$2, &offset)) { my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $2.str); - YYABORT; + MYSQL_YYABORT; } i= new sp_instr_copen(sp->instructions(), lex->spcont, offset); sp->add_instr(i); @@ -2708,7 +2843,7 @@ sp_proc_stmt_fetch: if (! lex->spcont->find_cursor(&$3, &offset)) { my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $3.str); - YYABORT; + MYSQL_YYABORT; } i= new sp_instr_cfetch(sp->instructions(), lex->spcont, offset); sp->add_instr(i); @@ -2728,7 +2863,7 @@ sp_proc_stmt_close: if (! lex->spcont->find_cursor(&$2, &offset)) { my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $2.str); - YYABORT; + MYSQL_YYABORT; } i= new sp_instr_cclose(sp->instructions(), lex->spcont, offset); sp->add_instr(i); @@ -2752,7 +2887,7 @@ sp_fetch_list: if (!spc || !(spv = spc->find_variable(&$1))) { my_error(ER_SP_UNDECLARED_VAR, MYF(0), $1.str); - YYABORT; + MYSQL_YYABORT; } else { @@ -2773,7 +2908,7 @@ sp_fetch_list: if (!spc || !(spv = spc->find_variable(&$3))) { my_error(ER_SP_UNDECLARED_VAR, MYF(0), $3.str); - YYABORT; + MYSQL_YYABORT; } else { @@ -2842,7 +2977,7 @@ simple_case_stmt: { LEX *lex= Lex; if (case_stmt_action_expr(lex, $3)) - YYABORT; + MYSQL_YYABORT; lex->sphead->restore_lex(YYTHD); /* For expr $3 */ } @@ -2945,7 +3080,7 @@ sp_labeled_control: if (lab) { my_error(ER_SP_LABEL_REDEFINE, MYF(0), $1.str); - YYABORT; + MYSQL_YYABORT; } else { @@ -2966,7 +3101,7 @@ sp_labeled_control: my_strcasecmp(system_charset_info, $5.str, lab->name) != 0) { my_error(ER_SP_LABEL_MISMATCH, MYF(0), $5.str); - YYABORT; + MYSQL_YYABORT; } } lex->sphead->backpatch(lex->spcont->pop_label()); @@ -3347,7 +3482,7 @@ opt_ts_nodegroup: if (lex->alter_tablespace_info->nodegroup_id != UNDEF_NODEGROUP) { my_error(ER_FILEGROUP_OPTION_ONLY_ONCE,MYF(0),"NODEGROUP"); - YYABORT; + MYSQL_YYABORT; } lex->alter_tablespace_info->nodegroup_id= $3; }; @@ -3359,7 +3494,7 @@ opt_ts_comment: if (lex->alter_tablespace_info->ts_comment != NULL) { my_error(ER_FILEGROUP_OPTION_ONLY_ONCE,MYF(0),"COMMENT"); - YYABORT; + MYSQL_YYABORT; } lex->alter_tablespace_info->ts_comment= $3.str; }; @@ -3372,7 +3507,7 @@ opt_ts_engine: { my_error(ER_FILEGROUP_OPTION_ONLY_ONCE,MYF(0), "STORAGE ENGINE"); - YYABORT; + MYSQL_YYABORT; } lex->alter_tablespace_info->storage_engine= $4; }; @@ -3394,7 +3529,7 @@ ts_wait: if (!(lex->alter_tablespace_info->wait_until_completed)) { my_error(ER_FILEGROUP_OPTION_ONLY_ONCE,MYF(0),"NO_WAIT"); - YYABORT; + MYSQL_YYABORT; } lex->alter_tablespace_info->wait_until_completed= FALSE; }; @@ -3428,20 +3563,20 @@ size_number: default: { my_error(ER_WRONG_SIZE_NUMBER, MYF(0)); - YYABORT; + MYSQL_YYABORT; } } if (prefix_number >> 31) { my_error(ER_SIZE_OVERFLOW_ERROR, MYF(0)); - YYABORT; + MYSQL_YYABORT; } number= prefix_number << text_shift_number; } else { my_error(ER_WRONG_SIZE_NUMBER, MYF(0)); - YYABORT; + MYSQL_YYABORT; } $$= number; } @@ -3461,11 +3596,11 @@ create2: THD *thd= YYTHD; LEX *lex= thd->lex; if (!(lex->like_name= $2)) - YYABORT; + MYSQL_YYABORT; if ($2->db.str == NULL && thd->copy_db_to(&($2->db.str), &($2->db.length))) { - YYABORT; + MYSQL_YYABORT; } } | '(' LIKE table_ident ')' @@ -3473,11 +3608,11 @@ create2: THD *thd= YYTHD; LEX *lex= thd->lex; if (!(lex->like_name= $3)) - YYABORT; + MYSQL_YYABORT; if ($3->db.str == NULL && thd->copy_db_to(&($3->db.str), &($3->db.length))) { - YYABORT; + MYSQL_YYABORT; } } ; @@ -3537,7 +3672,7 @@ partitioning: if (!lex->part_info) { mem_alloc_error(sizeof(partition_info)); - YYABORT; + MYSQL_YYABORT; } if (lex->sql_command == SQLCOM_ALTER_TABLE) { @@ -3546,7 +3681,7 @@ partitioning: #else my_error(ER_FEATURE_DISABLED, MYF(0), "partitioning", "--with-partition"); - YYABORT; + MYSQL_YYABORT; #endif } @@ -3559,8 +3694,8 @@ partition_entry: LEX *lex= Lex; if (!lex->part_info) { - yyerror(ER(ER_PARTITION_ENTRY_ERROR)); - YYABORT; + my_parse_error(ER(ER_PARTITION_ENTRY_ERROR)); + MYSQL_YYABORT; } /* We enter here when opening the frm file to translate @@ -3614,7 +3749,7 @@ part_field_item: if (Lex->part_info->part_field_list.push_back($1.str)) { mem_alloc_error(1); - YYABORT; + MYSQL_YYABORT; } } ; @@ -3653,7 +3788,7 @@ opt_no_parts: if (no_parts == 0) { my_error(ER_NO_PARTS_ERROR, MYF(0), "partitions"); - YYABORT; + MYSQL_YYABORT; } lex->part_info->no_parts= no_parts; @@ -3687,7 +3822,7 @@ sub_part_field_item: if (Lex->part_info->subpart_field_list.push_back($1.str)) { mem_alloc_error(1); - YYABORT; + MYSQL_YYABORT; } } ; @@ -3701,8 +3836,8 @@ part_func_expr: lex->safe_to_cache_query= 1; if (not_corr_func) { - yyerror(ER(ER_CONST_EXPR_IN_PARTITION_FUNC_ERROR)); - YYABORT; + my_parse_error(ER(ER_CONST_EXPR_IN_PARTITION_FUNC_ERROR)); + MYSQL_YYABORT; } $$=$1; } @@ -3717,7 +3852,7 @@ opt_no_subparts: if (no_parts == 0) { my_error(ER_NO_PARTS_ERROR, MYF(0), "subpartitions"); - YYABORT; + MYSQL_YYABORT; } lex->part_info->no_subparts= no_parts; lex->part_info->use_default_no_subpartitions= FALSE; @@ -3737,8 +3872,8 @@ part_defs: if (part_info->no_parts != count_curr_parts) { - yyerror(ER(ER_PARTITION_WRONG_NO_PART_ERROR)); - YYABORT; + my_parse_error(ER(ER_PARTITION_WRONG_NO_PART_ERROR)); + MYSQL_YYABORT; } } else if (count_curr_parts > 0) @@ -3764,7 +3899,7 @@ part_definition: if (!p_elem || part_info->partitions.push_back(p_elem)) { mem_alloc_error(sizeof(partition_element)); - YYABORT; + MYSQL_YYABORT; } p_elem->part_state= PART_NORMAL; part_info->curr_part_elem= p_elem; @@ -3798,13 +3933,13 @@ opt_part_values: { my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0), "RANGE", "LESS THAN"); - YYABORT; + MYSQL_YYABORT; } if (lex->part_info->part_type == LIST_PARTITION) { my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0), "LIST", "IN"); - YYABORT; + MYSQL_YYABORT; } } else @@ -3819,7 +3954,7 @@ opt_part_values: { my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0), "RANGE", "LESS THAN"); - YYABORT; + MYSQL_YYABORT; } } else @@ -3834,7 +3969,7 @@ opt_part_values: { my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0), "LIST", "IN"); - YYABORT; + MYSQL_YYABORT; } } else @@ -3848,8 +3983,8 @@ part_func_max: LEX *lex= Lex; if (lex->part_info->defined_max_value) { - yyerror(ER(ER_PARTITION_MAXVALUE_ERROR)); - YYABORT; + my_parse_error(ER(ER_PARTITION_MAXVALUE_ERROR)); + MYSQL_YYABORT; } lex->part_info->defined_max_value= TRUE; lex->part_info->curr_part_elem->max_value= TRUE; @@ -3859,13 +3994,13 @@ part_func_max: { if (Lex->part_info->defined_max_value) { - yyerror(ER(ER_PARTITION_MAXVALUE_ERROR)); - YYABORT; + my_parse_error(ER(ER_PARTITION_MAXVALUE_ERROR)); + MYSQL_YYABORT; } if (Lex->part_info->curr_part_elem->has_null_value) { - yyerror(ER(ER_NULL_IN_VALUES_LESS_THAN)); - YYABORT; + my_parse_error(ER(ER_NULL_IN_VALUES_LESS_THAN)); + MYSQL_YYABORT; } } ; @@ -3902,7 +4037,7 @@ part_list_item: list_val_list.push_back(value_ptr)) { mem_alloc_error(sizeof(part_elem_value)); - YYABORT; + MYSQL_YYABORT; } } ; @@ -3925,13 +4060,13 @@ part_bit_expr: if (!value_ptr) { mem_alloc_error(sizeof(part_elem_value)); - YYABORT; + MYSQL_YYABORT; } if (part_expr->walk(&Item::check_partition_func_processor, 0, NULL)) { my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0)); - YYABORT; + MYSQL_YYABORT; } if (part_expr->fix_fields(YYTHD, (Item**)0) || ((context->table_list= save_list), FALSE) || @@ -3939,7 +4074,7 @@ part_bit_expr: (!lex->safe_to_cache_query)) { my_error(ER_NO_CONST_EXPR_IN_RANGE_OR_LIST_ERROR, MYF(0)); - YYABORT; + MYSQL_YYABORT; } thd->where= save_where; value_ptr->value= part_expr->val_int(); @@ -3952,15 +4087,15 @@ part_bit_expr: if (Lex->part_info->curr_part_elem->has_null_value) { my_error(ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR, MYF(0)); - YYABORT; + MYSQL_YYABORT; } Lex->part_info->curr_part_elem->has_null_value= TRUE; } else if (part_expr->result_type() != INT_RESULT && !part_expr->null_value) { - yyerror(ER(ER_INCONSISTENT_TYPE_OF_FUNCTIONS_ERROR)); - YYABORT; + my_parse_error(ER(ER_INCONSISTENT_TYPE_OF_FUNCTIONS_ERROR)); + MYSQL_YYABORT; } $$= value_ptr; } @@ -3972,8 +4107,8 @@ opt_sub_partition: if (Lex->part_info->no_subparts != 0 && !Lex->part_info->use_default_subpartitions) { - yyerror(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR)); - YYABORT; + my_parse_error(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR)); + MYSQL_YYABORT; } } | '(' sub_part_list ')' @@ -3985,16 +4120,16 @@ opt_sub_partition: if (part_info->no_subparts != part_info->count_curr_subparts) { - yyerror(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR)); - YYABORT; + my_parse_error(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR)); + MYSQL_YYABORT; } } else if (part_info->count_curr_subparts > 0) { if (part_info->partitions.elements > 1) { - yyerror(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR)); - YYABORT; + my_parse_error(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR)); + MYSQL_YYABORT; } part_info->no_subparts= part_info->count_curr_subparts; } @@ -4018,7 +4153,7 @@ sub_part_definition: curr_part->subpartitions.push_back(sub_p_elem)) { mem_alloc_error(sizeof(partition_element)); - YYABORT; + MYSQL_YYABORT; } part_info->curr_part_elem= sub_p_elem; part_info->use_default_subpartitions= FALSE; @@ -4170,8 +4305,8 @@ create_table_option: Lex->create_info.table_options|= HA_OPTION_PACK_KEYS; break; default: - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } Lex->create_info.used_fields|= HA_CREATE_USED_PACK_KEYS; } @@ -4226,7 +4361,7 @@ default_charset: my_error(ER_CONFLICTING_DECLARATIONS, MYF(0), "CHARACTER SET ", cinfo->default_table_charset->csname, "CHARACTER SET ", $4->csname); - YYABORT; + MYSQL_YYABORT; } Lex->create_info.default_table_charset= $4; Lex->create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET; @@ -4242,25 +4377,37 @@ default_collation: { my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0), $4->name, cinfo->default_table_charset->csname); - YYABORT; + MYSQL_YYABORT; } Lex->create_info.default_table_charset= $4; 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); + MYSQL_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); + MYSQL_YYABORT; + } + + push_warning_printf(YYTHD, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_STORAGE_ENGINE, ER(ER_UNKNOWN_STORAGE_ENGINE), $1.str); } @@ -4317,8 +4464,8 @@ key_def: LEX *lex=Lex; if ($1 != Key::FULLTEXT && lex->key_create_info.parser_name.str) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } lex->key_list.push_back(new Key($1,$2, &lex->key_create_info, 0, lex->col_list)); @@ -4397,7 +4544,7 @@ field_spec: &lex->comment, lex->change,&lex->interval_list,lex->charset, lex->uint_geom_type)) - YYABORT; + MYSQL_YYABORT; }; type: @@ -4466,7 +4613,7 @@ type: #else my_error(ER_FEATURE_DISABLED, MYF(0), sym_group_geom.name, sym_group_geom.needed_define); - YYABORT; + MYSQL_YYABORT; #endif } | MEDIUMBLOB { Lex->charset=&my_charset_bin; @@ -4627,7 +4774,7 @@ attribute: { my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0), $2->name,Lex->charset->csname); - YYABORT; + MYSQL_YYABORT; } else { @@ -4652,7 +4799,7 @@ charset_name: if (!($$=get_charset_by_csname($1.str,MY_CS_PRIMARY,MYF(0)))) { my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), $1.str); - YYABORT; + MYSQL_YYABORT; } } | BINARY { $$= &my_charset_bin; } @@ -4662,6 +4809,10 @@ charset_name_or_default: charset_name { $$=$1; } | DEFAULT { $$=NULL; } ; +opt_load_data_charset: + /* Empty */ { $$= NULL; } + | charset charset_name_or_default { $$= $2; } + ; old_or_new_charset_name: ident_or_text @@ -4670,7 +4821,7 @@ old_or_new_charset_name: !($$=get_old_charset_by_name($1.str))) { my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), $1.str); - YYABORT; + MYSQL_YYABORT; } } | BINARY { $$= &my_charset_bin; } @@ -4686,7 +4837,7 @@ collation_name: if (!($$=get_charset_by_name($1.str,MYF(0)))) { my_error(ER_UNKNOWN_COLLATION, MYF(0), $1.str); - YYABORT; + MYSQL_YYABORT; } }; @@ -4713,7 +4864,7 @@ opt_binary: MY_CS_PRIMARY,MYF(0)))) { my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), "ucs2"); - YYABORT; + MYSQL_YYABORT; } } | charset charset_name opt_bin_mod { Lex->charset=$2; } @@ -4732,7 +4883,7 @@ opt_bin_charset: MY_CS_PRIMARY,MYF(0)))) { my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), "ucs2"); - YYABORT; + MYSQL_YYABORT; } } | charset charset_name { Lex->charset=$2; } ; @@ -4795,7 +4946,7 @@ key_type: #else my_error(ER_FEATURE_DISABLED, MYF(0), sym_group_geom.name, sym_group_geom.needed_define); - YYABORT; + MYSQL_YYABORT; #endif }; @@ -4828,7 +4979,7 @@ opt_unique_or_fulltext: #else my_error(ER_FEATURE_DISABLED, MYF(0), sym_group_geom.name, sym_group_geom.needed_define); - YYABORT; + MYSQL_YYABORT; #endif } ; @@ -4876,7 +5027,7 @@ key_opt: else { my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), $3.str); - YYABORT; + MYSQL_YYABORT; } } ; @@ -4933,7 +5084,7 @@ alter: lex->duplicates= DUP_ERROR; if (!lex->select_lex.add_table_to_list(thd, $4, NULL, TL_OPTION_UPDATING)) - YYABORT; + MYSQL_YYABORT; lex->create_list.empty(); lex->key_list.empty(); lex->col_list.empty(); @@ -4965,7 +5116,7 @@ alter: lex->name= $3; if (lex->name.str == NULL && thd->copy_db_to(&lex->name.str, &lex->name.length)) - YYABORT; + MYSQL_YYABORT; } | ALTER PROCEDURE sp_name { @@ -4974,7 +5125,7 @@ alter: if (lex->sphead) { my_error(ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE"); - YYABORT; + MYSQL_YYABORT; } bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); } @@ -4992,7 +5143,7 @@ alter: if (lex->sphead) { my_error(ER_SP_NO_DROP_SP, MYF(0), "FUNCTION"); - YYABORT; + MYSQL_YYABORT; } bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); } @@ -5015,7 +5166,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 @@ -5030,8 +5181,8 @@ alter: */ if (!(Lex->event_parse_data= Event_parse_data::new_instance(YYTHD))) - YYABORT; - Lex->event_parse_data->identifier= $3; + MYSQL_YYABORT; + Lex->event_parse_data->identifier= $4; /* We have to turn off CLIENT_MULTI_QUERIES while parsing a @@ -5051,16 +5202,17 @@ 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; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } /* sql_command is set here because some rules in ev_sql_stmt @@ -5219,7 +5371,7 @@ add_partition_rule: if (!lex->part_info) { mem_alloc_error(sizeof(partition_info)); - YYABORT; + MYSQL_YYABORT; } lex->alter_info.flags|= ALTER_ADD_PARTITION; lex->no_write_to_binlog= $3; @@ -5249,7 +5401,7 @@ reorg_partition_rule: if (!lex->part_info) { mem_alloc_error(sizeof(partition_info)); - YYABORT; + MYSQL_YYABORT; } lex->no_write_to_binlog= $3; } @@ -5284,7 +5436,7 @@ alt_part_name_item: if (Lex->alter_info.partition_names.push_back($1.str)) { mem_alloc_error(1); - YYABORT; + MYSQL_YYABORT; } } ; @@ -5342,7 +5494,7 @@ alter_list_item: &lex->comment, $3.str, &lex->interval_list, lex->charset, lex->uint_geom_type)) - YYABORT; + MYSQL_YYABORT; } opt_place | DROP opt_column field_ident opt_restrict @@ -5404,13 +5556,13 @@ alter_list_item: if (lex->select_lex.db == NULL && thd->copy_db_to(&lex->select_lex.db, &dummy)) { - YYABORT; + MYSQL_YYABORT; } if (check_table_name($3->table.str,$3->table.length) || $3->db.str && check_db_name(&$3->db)) { my_error(ER_WRONG_TABLE_NAME, MYF(0), $3->table.str); - YYABORT; + MYSQL_YYABORT; } lex->name= $3->table; lex->alter_info.flags|= ALTER_RENAME; @@ -5427,7 +5579,7 @@ alter_list_item: { my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0), $5->name, $4->csname); - YYABORT; + MYSQL_YYABORT; } LEX *lex= Lex; lex->create_info.table_charset= @@ -5565,7 +5717,7 @@ slave_until: { my_message(ER_BAD_SLAVE_UNTIL_COND, ER(ER_BAD_SLAVE_UNTIL_COND), MYF(0)); - YYABORT; + MYSQL_YYABORT; } } @@ -5669,7 +5821,7 @@ check: if (lex->sphead) { my_error(ER_SP_BADSTATEMENT, MYF(0), "CHECK"); - YYABORT; + MYSQL_YYABORT; } lex->sql_command = SQLCOM_CHECK; lex->check_opt.init(); @@ -5736,12 +5888,12 @@ rename_list: user TO_SYM user { if (Lex->users_list.push_back($1) || Lex->users_list.push_back($3)) - YYABORT; + MYSQL_YYABORT; } | rename_list ',' user TO_SYM user { if (Lex->users_list.push_back($3) || Lex->users_list.push_back($5)) - YYABORT; + MYSQL_YYABORT; } ; @@ -5758,7 +5910,7 @@ table_to_table: TL_IGNORE) || !sl->add_table_to_list(lex->thd, $3,NULL,TL_OPTION_UPDATING, TL_IGNORE)) - YYABORT; + MYSQL_YYABORT; }; db_to_db: @@ -5769,7 +5921,7 @@ db_to_db: sql_memdup(&$1, sizeof(LEX_STRING))) || lex->db_list.push_back((LEX_STRING*) sql_memdup(&$3, sizeof(LEX_STRING)))) - YYABORT; + MYSQL_YYABORT; }; keycache: @@ -5794,7 +5946,7 @@ assign_to_keycache: TL_READ, sel->get_use_index(), (List<String> *)0)) - YYABORT; + MYSQL_YYABORT; } ; @@ -5826,7 +5978,7 @@ preload_keys: TL_READ, sel->get_use_index(), (List<String> *)0)) - YYABORT; + MYSQL_YYABORT; } ; @@ -5881,16 +6033,16 @@ select_paren: SELECT_LEX * sel= lex->current_select; if (sel->set_braces(1)) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } if (sel->linkage == UNION_TYPE && !sel->master_unit()->first_select()->braces && sel->master_unit()->first_select()->linkage == UNION_TYPE) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } /* select in braces, can't contain global parameters */ if (sel->master_unit()->fake_select_lex) @@ -5906,14 +6058,14 @@ select_init2: SELECT_LEX * sel= lex->current_select; if (lex->current_select->set_braces(0)) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } if (sel->linkage == UNION_TYPE && sel->master_unit()->first_select()->braces) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } } union_clause @@ -5962,7 +6114,7 @@ select_options: if (Select->options & SELECT_DISTINCT && Select->options & SELECT_ALL) { my_error(ER_WRONG_USAGE, MYF(0), "ALL", "DISTINCT"); - YYABORT; + MYSQL_YYABORT; } } ; @@ -5976,7 +6128,7 @@ select_option: | HIGH_PRIORITY { if (check_simple_select()) - YYABORT; + MYSQL_YYABORT; Lex->lock_option= TL_READ_HIGH_PRIORITY; } | DISTINCT { Select->options|= SELECT_DISTINCT; } @@ -5985,13 +6137,13 @@ select_option: | SQL_BUFFER_RESULT { if (check_simple_select()) - YYABORT; + MYSQL_YYABORT; Select->options|= OPTION_BUFFER_RESULT; } | SQL_CALC_FOUND_ROWS { if (check_simple_select()) - YYABORT; + MYSQL_YYABORT; Select->options|= OPTION_FOUND_ROWS; } | SQL_NO_CACHE_SYM @@ -6040,7 +6192,7 @@ select_item_list: new Item_field(&thd->lex->current_select-> context, NULL, NULL, "*"))) - YYABORT; + MYSQL_YYABORT; (thd->lex->current_select->with_wild)++; }; @@ -6049,7 +6201,7 @@ select_item: remember_name select_item2 remember_end select_alias { if (add_item_to_list(YYTHD, $2)) - YYABORT; + MYSQL_YYABORT; if ($4.str) { $2->is_autogenerated_name= FALSE; @@ -6140,13 +6292,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 +6316,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); } @@ -6329,7 +6492,7 @@ simple_expr: lex->dec ? atoi(lex->dec) : 0, lex->charset); if (!$$) - YYABORT; + MYSQL_YYABORT; } | CASE_SYM opt_expr when_list opt_else END { $$= new (YYTHD->mem_root) Item_func_case(* $3, $2, $4 ); } @@ -6340,7 +6503,7 @@ simple_expr: Lex->dec ? atoi(Lex->dec) : 0, Lex->charset); if (!$$) - YYABORT; + MYSQL_YYABORT; } | CONVERT_SYM '(' expr USING charset_name ')' { $$= new (YYTHD->mem_root) Item_func_conv_charset($3,$5); } @@ -6351,7 +6514,7 @@ simple_expr: Item_splocal *il= static_cast<Item_splocal *>($3); my_error(ER_WRONG_COLUMN_NAME, MYF(0), il->my_name()->str); - YYABORT; + MYSQL_YYABORT; } $$= new (YYTHD->mem_root) Item_default_value(Lex->current_context(), $3); @@ -6366,8 +6529,8 @@ simple_expr: { if ($1->type() != Item::ROW_ITEM) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } $$= new (YYTHD->mem_root) Item_func_interval((Item_row *)$1); } @@ -6608,7 +6771,7 @@ function_call_conflict: #else my_error(ER_FEATURE_DISABLED, MYF(0), sym_group_geom.name, sym_group_geom.needed_define); - YYABORT; + MYSQL_YYABORT; #endif } ; @@ -6687,8 +6850,8 @@ function_call_generic: { if (lex->current_select->inc_in_sum_expr()) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } } /* Temporary placing the result of find_udf in $3 */ @@ -6741,7 +6904,7 @@ function_call_generic: if (! ($$= item)) { - YYABORT; + MYSQL_YYABORT; } } | ident '.' ident '(' opt_expr_list ')' @@ -6770,7 +6933,7 @@ function_call_generic: if (! ($$= item)) { - YYABORT; + MYSQL_YYABORT; } } ; @@ -6899,7 +7062,7 @@ variable: if (! Lex->parsing_options.allows_variable) { my_error(ER_VIEW_SELECT_VARIABLE, MYF(0)); - YYABORT; + MYSQL_YYABORT; } } variable_aux @@ -6925,11 +7088,11 @@ variable_aux: { if ($3.str && $4.str && check_reserved_words(&$3)) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } if (!($$= get_system_var(YYTHD, $2, $3, $4))) - YYABORT; + MYSQL_YYABORT; } ; @@ -6963,8 +7126,8 @@ in_sum_expr: LEX *lex= Lex; if (lex->current_select->inc_in_sum_expr()) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } } expr @@ -7044,12 +7207,12 @@ table_ref: { LEX *lex= Lex; if (!($$= lex->current_select->nest_last_join(lex->thd))) - YYABORT; + MYSQL_YYABORT; } ; join_table_list: - derived_table_list { YYERROR_UNLESS($$=$1); } + derived_table_list { MYSQL_YYABORT_UNLESS($$=$1); } ; /* Warning - may return NULL in case of incomplete SELECT */ @@ -7057,7 +7220,7 @@ derived_table_list: table_ref { $$=$1; } | derived_table_list ',' table_ref { - YYERROR_UNLESS($1 && ($$=$3)); + MYSQL_YYABORT_UNLESS($1 && ($$=$3)); } ; @@ -7076,16 +7239,16 @@ join_table: left-associative joins. */ table_ref %prec TABLE_REF_PRIORITY normal_join table_ref - { YYERROR_UNLESS($1 && ($$=$3)); } + { MYSQL_YYABORT_UNLESS($1 && ($$=$3)); } | table_ref STRAIGHT_JOIN table_factor - { YYERROR_UNLESS($1 && ($$=$3)); $3->straight=1; } + { MYSQL_YYABORT_UNLESS($1 && ($$=$3)); $3->straight=1; } | table_ref normal_join table_ref ON { - YYERROR_UNLESS($1 && $3); + MYSQL_YYABORT_UNLESS($1 && $3); /* Change the current name resolution context to a local context. */ if (push_new_name_resolution_context(YYTHD, $1, $3)) - YYABORT; + MYSQL_YYABORT; Select->parsing_place= IN_ON; } expr @@ -7097,10 +7260,10 @@ join_table: | table_ref STRAIGHT_JOIN table_factor ON { - YYERROR_UNLESS($1 && $3); + MYSQL_YYABORT_UNLESS($1 && $3); /* Change the current name resolution context to a local context. */ if (push_new_name_resolution_context(YYTHD, $1, $3)) - YYABORT; + MYSQL_YYABORT; Select->parsing_place= IN_ON; } expr @@ -7113,13 +7276,13 @@ join_table: | table_ref normal_join table_ref USING { - YYERROR_UNLESS($1 && $3); + MYSQL_YYABORT_UNLESS($1 && $3); } '(' using_list ')' { add_join_natural($1,$3,$7,Select); $$=$3; } | table_ref NATURAL JOIN_SYM table_factor { - YYERROR_UNLESS($1 && ($$=$4)); + MYSQL_YYABORT_UNLESS($1 && ($$=$4)); add_join_natural($1,$4,NULL,Select); } @@ -7127,10 +7290,10 @@ join_table: | table_ref LEFT opt_outer JOIN_SYM table_ref ON { - YYERROR_UNLESS($1 && $5); + MYSQL_YYABORT_UNLESS($1 && $5); /* Change the current name resolution context to a local context. */ if (push_new_name_resolution_context(YYTHD, $1, $5)) - YYABORT; + MYSQL_YYABORT; Select->parsing_place= IN_ON; } expr @@ -7143,7 +7306,7 @@ join_table: } | table_ref LEFT opt_outer JOIN_SYM table_factor { - YYERROR_UNLESS($1 && $5); + MYSQL_YYABORT_UNLESS($1 && $5); } USING '(' using_list ')' { @@ -7153,7 +7316,7 @@ join_table: } | table_ref NATURAL LEFT opt_outer JOIN_SYM table_factor { - YYERROR_UNLESS($1 && $6); + MYSQL_YYABORT_UNLESS($1 && $6); add_join_natural($1,$6,NULL,Select); $6->outer_join|=JOIN_TYPE_LEFT; $$=$6; @@ -7163,39 +7326,39 @@ join_table: | table_ref RIGHT opt_outer JOIN_SYM table_ref ON { - YYERROR_UNLESS($1 && $5); + MYSQL_YYABORT_UNLESS($1 && $5); /* Change the current name resolution context to a local context. */ if (push_new_name_resolution_context(YYTHD, $1, $5)) - YYABORT; + MYSQL_YYABORT; Select->parsing_place= IN_ON; } expr { LEX *lex= Lex; if (!($$= lex->current_select->convert_right_join())) - YYABORT; + MYSQL_YYABORT; add_join_on($$, $8); Lex->pop_context(); Select->parsing_place= NO_MATTER; } | table_ref RIGHT opt_outer JOIN_SYM table_factor { - YYERROR_UNLESS($1 && $5); + MYSQL_YYABORT_UNLESS($1 && $5); } USING '(' using_list ')' { LEX *lex= Lex; if (!($$= lex->current_select->convert_right_join())) - YYABORT; + MYSQL_YYABORT; add_join_natural($$,$5,$9,Select); } | table_ref NATURAL RIGHT opt_outer JOIN_SYM table_factor { - YYERROR_UNLESS($1 && $6); + MYSQL_YYABORT_UNLESS($1 && $6); add_join_natural($6,$1,NULL,Select); LEX *lex= Lex; if (!($$= lex->current_select->convert_right_join())) - YYABORT; + MYSQL_YYABORT; }; normal_join: @@ -7220,7 +7383,7 @@ table_factor: lex->lock_option, sel->get_use_index(), sel->get_ignore_index()))) - YYABORT; + MYSQL_YYABORT; sel->add_joined_table($$); } | '{' ident table_ref LEFT OUTER JOIN_SYM table_ref @@ -7228,19 +7391,19 @@ table_factor: { /* Change the current name resolution context to a local context. */ if (push_new_name_resolution_context(YYTHD, $3, $7)) - YYABORT; + MYSQL_YYABORT; } expr '}' { LEX *lex= Lex; - YYERROR_UNLESS($3 && $7); + MYSQL_YYABORT_UNLESS($3 && $7); add_join_on($7,$10); Lex->pop_context(); $7->outer_join|=JOIN_TYPE_LEFT; $$=$7; if (!($$= lex->current_select->nest_last_join(lex->thd))) - YYABORT; + MYSQL_YYABORT; } | select_derived_init get_select_lex select_derived2 { @@ -7250,8 +7413,8 @@ table_factor: { if (sel->set_braces(1)) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } /* select in braces, can't contain global parameters */ if (sel->master_unit()->fake_select_lex) @@ -7259,7 +7422,7 @@ table_factor: sel->master_unit()->fake_select_lex; } if ($2->init_nested_join(lex->thd)) - YYABORT; + MYSQL_YYABORT; $$= 0; /* incomplete derived tables return NULL, we must be nested in select_derived rule to be here. */ @@ -7293,7 +7456,7 @@ table_factor: TL_READ,(List<String> *)0, (List<String> *)0))) - YYABORT; + MYSQL_YYABORT; sel->add_joined_table($$); lex->pop_context(); } @@ -7301,8 +7464,8 @@ table_factor: if ($4 || $6) { /* simple nested joins cannot have aliases or unions */ - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } else $$= $3; @@ -7315,7 +7478,7 @@ select_derived: { LEX *lex= Lex; if ($1->init_nested_join(lex->thd)) - YYABORT; + MYSQL_YYABORT; } derived_table_list { @@ -7324,11 +7487,11 @@ select_derived: for derived tables, both must equal NULL */ if (!($$= $1->end_nested_join(lex->thd)) && $3) - YYABORT; + MYSQL_YYABORT; if (!$3 && $$) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } } ; @@ -7339,12 +7502,12 @@ select_derived2: lex->derived_tables|= DERIVED_SUBQUERY; if (!lex->expr_allows_subselect) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE || mysql_new_select(lex, 1)) - YYABORT; + MYSQL_YYABORT; mysql_init_select(lex); lex->current_select->linkage= DERIVED_TABLE_TYPE; lex->current_select->parsing_place= SELECT_LIST; @@ -7368,7 +7531,7 @@ select_derived_init: if (! lex->parsing_options.allows_derived) { my_error(ER_VIEW_SELECT_DERIVED, MYF(0)); - YYABORT; + MYSQL_YYABORT; } SELECT_LEX *sel= lex->current_select; @@ -7376,8 +7539,8 @@ select_derived_init: if (!sel->embedding || sel->end_nested_join(lex->thd)) { /* we are not in parentheses */ - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } embedding= Select->embedding; $$= embedding && @@ -7441,7 +7604,7 @@ using_list: ident { if (!($$= new List<String>)) - YYABORT; + MYSQL_YYABORT; $$->push_back(new (YYTHD->mem_root) String((const char *) $1.str, $1.length, system_charset_info)); @@ -7561,9 +7724,9 @@ group_clause: group_list: group_list ',' order_ident order_dir - { if (add_group_to_list(YYTHD, $3,(bool) $4)) YYABORT; } + { if (add_group_to_list(YYTHD, $3,(bool) $4)) MYSQL_YYABORT; } | order_ident order_dir - { if (add_group_to_list(YYTHD, $1,(bool) $2)) YYABORT; }; + { if (add_group_to_list(YYTHD, $1,(bool) $2)) MYSQL_YYABORT; }; olap_opt: /* empty */ {} @@ -7574,11 +7737,11 @@ olap_opt: { my_error(ER_WRONG_USAGE, MYF(0), "WITH CUBE", "global union parameters"); - YYABORT; + MYSQL_YYABORT; } lex->current_select->olap= CUBE_TYPE; my_error(ER_NOT_SUPPORTED_YET, MYF(0), "CUBE"); - YYABORT; /* To be deleted in 5.1 */ + MYSQL_YYABORT; /* To be deleted in 5.1 */ } | WITH ROLLUP_SYM { @@ -7587,7 +7750,7 @@ olap_opt: { my_error(ER_WRONG_USAGE, MYF(0), "WITH ROLLUP", "global union parameters"); - YYABORT; + MYSQL_YYABORT; } lex->current_select->olap= ROLLUP_TYPE; } @@ -7612,7 +7775,7 @@ alter_order_item: THD *thd= YYTHD; bool ascending= ($2 == 1) ? true : false; if (add_order_to_list(thd, $1, ascending)) - YYABORT; + MYSQL_YYABORT; } ; @@ -7635,7 +7798,7 @@ order_clause: { my_error(ER_WRONG_USAGE, MYF(0), "CUBE/ROLLUP", "ORDER BY"); - YYABORT; + MYSQL_YYABORT; } if (lex->sql_command != SQLCOM_ALTER_TABLE && !unit->fake_select_lex) { @@ -7652,15 +7815,15 @@ order_clause: (first_sl->order_list.elements || first_sl->select_limit) && unit->add_fake_select_lex(lex->thd)) - YYABORT; + MYSQL_YYABORT; } } order_list; order_list: order_list ',' order_ident order_dir - { if (add_order_to_list(YYTHD, $3,(bool) $4)) YYABORT; } + { if (add_order_to_list(YYTHD, $3,(bool) $4)) MYSQL_YYABORT; } | order_ident order_dir - { if (add_order_to_list(YYTHD, $1,(bool) $2)) YYABORT; }; + { if (add_order_to_list(YYTHD, $1,(bool) $2)) MYSQL_YYABORT; }; order_dir: /* empty */ { $$ = 1; } @@ -7745,7 +7908,7 @@ real_ulong_num: | HEX_NUM { $$= (ulong) strtol($1.str, (char**) 0, 16); } | LONG_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } | ULONGLONG_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } - | dec_num_error { YYABORT; } + | dec_num_error { MYSQL_YYABORT; } ; ulonglong_num: @@ -7760,12 +7923,12 @@ real_ulonglong_num: NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } | ULONGLONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } | LONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } - | dec_num_error { YYABORT; } + | dec_num_error { MYSQL_YYABORT; } ; dec_num_error: dec_num - { yyerror(ER(ER_ONLY_INTEGERS_ALLOWED)); } + { my_parse_error(ER(ER_ONLY_INTEGERS_ALLOWED)); } ; dec_num: @@ -7782,13 +7945,13 @@ procedure_clause: if (! lex->parsing_options.allows_select_procedure) { my_error(ER_VIEW_SELECT_CLAUSE, MYF(0), "PROCEDURE"); - YYABORT; + MYSQL_YYABORT; } if (&lex->select_lex != lex->current_select) { my_error(ER_WRONG_USAGE, MYF(0), "PROCEDURE", "subquery"); - YYABORT; + MYSQL_YYABORT; } lex->proc_list.elements=0; lex->proc_list.first=0; @@ -7797,7 +7960,7 @@ procedure_clause: current_select-> context, NULL,NULL,$2.str))) - YYABORT; + MYSQL_YYABORT; Lex->uncacheable(UNCACHEABLE_SIDEEFFECT); } '(' procedure_list ')'; @@ -7816,7 +7979,7 @@ procedure_item: { LEX *lex= Lex; if (add_proc_to_list(lex->thd, $2)) - YYABORT; + MYSQL_YYABORT; if (!$2->name) $2->set_name($1,(uint) ((char*) lex->tok_end - $1), YYTHD->charset()); @@ -7828,7 +7991,7 @@ select_var_list_init: { LEX *lex=Lex; if (!lex->describe && (!(lex->result= new select_dumpvar()))) - YYABORT; + MYSQL_YYABORT; } select_var_list {} @@ -7860,7 +8023,7 @@ select_var_ident: if (!lex->spcont || !(t=lex->spcont->find_variable(&$1))) { my_error(ER_SP_UNDECLARED_VAR, MYF(0), $1.str); - YYABORT; + MYSQL_YYABORT; } if (lex->result) { @@ -7889,7 +8052,7 @@ into: if (! Lex->parsing_options.allows_select_into) { my_error(ER_VIEW_SELECT_CLAUSE, MYF(0), "INTO"); - YYABORT; + MYSQL_YYABORT; } } into_destination @@ -7902,7 +8065,7 @@ into_destination: lex->uncacheable(UNCACHEABLE_SIDEEFFECT); if (!(lex->exchange= new sql_exchange($2.str, 0)) || !(lex->result= new select_export(lex->exchange))) - YYABORT; + MYSQL_YYABORT; } opt_field_term opt_line_term | DUMPFILE TEXT_STRING_filesystem @@ -7912,9 +8075,9 @@ into_destination: { lex->uncacheable(UNCACHEABLE_SIDEEFFECT); if (!(lex->exchange= new sql_exchange($2.str,1))) - YYABORT; + MYSQL_YYABORT; if (!(lex->result= new select_dump(lex->exchange))) - YYABORT; + MYSQL_YYABORT; } } | select_var_list_init @@ -7960,7 +8123,7 @@ drop: $3.str)); if (!lex->current_select->add_table_to_list(lex->thd, $5, NULL, TL_OPTION_UPDATING)) - YYABORT; + MYSQL_YYABORT; } | DROP DATABASE if_exists ident { @@ -7975,7 +8138,7 @@ drop: if (lex->sphead) { my_error(ER_SP_NO_DROP_SP, MYF(0), "FUNCTION"); - YYABORT; + MYSQL_YYABORT; } lex->sql_command = SQLCOM_DROP_FUNCTION; lex->drop_if_exists= $3; @@ -7987,7 +8150,7 @@ drop: if (lex->sphead) { my_error(ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE"); - YYABORT; + MYSQL_YYABORT; } lex->sql_command = SQLCOM_DROP_PROCEDURE; lex->drop_if_exists= $3; @@ -8043,7 +8206,7 @@ table_name: table_ident { if (!Select->add_table_to_list(YYTHD, $1, NULL, TL_OPTION_UPDATING)) - YYABORT; + MYSQL_YYABORT; } ; @@ -8141,7 +8304,7 @@ insert_field_spec: LEX *lex=Lex; if (!(lex->insert_list = new List_item) || lex->many_values.push_back(lex->insert_list)) - YYABORT; + MYSQL_YYABORT; } ident_eq_list; @@ -8171,7 +8334,7 @@ ident_eq_value: LEX *lex=Lex; if (lex->field_list.push_back($1) || lex->insert_list->push_back($3)) - YYABORT; + MYSQL_YYABORT; }; equal: EQ {} @@ -8187,13 +8350,13 @@ no_braces: '(' { if (!(Lex->insert_list = new List_item)) - YYABORT; + MYSQL_YYABORT; } opt_values ')' { LEX *lex=Lex; if (lex->many_values.push_back(lex->insert_list)) - YYABORT; + MYSQL_YYABORT; }; opt_values: @@ -8204,12 +8367,12 @@ values: values ',' expr_or_default { if (Lex->insert_list->push_back($3)) - YYABORT; + MYSQL_YYABORT; } | expr_or_default { if (Lex->insert_list->push_back($1)) - YYABORT; + MYSQL_YYABORT; } ; @@ -8246,7 +8409,7 @@ update: /* it is single table update and it is update of derived table */ my_error(ER_NON_UPDATABLE_TABLE, MYF(0), lex->select_lex.get_table_list()->alias, "UPDATE"); - YYABORT; + MYSQL_YYABORT; } /* In case of multi-update setting write lock for all tables may @@ -8266,7 +8429,7 @@ update_elem: simple_ident_nospvar equal expr_or_default { if (add_item_to_list(YYTHD, $1) || add_value_to_list(YYTHD, $3)) - YYABORT; + MYSQL_YYABORT; }; insert_update_list: @@ -8279,7 +8442,7 @@ insert_update_elem: LEX *lex= Lex; if (lex->update_list.push_back($1) || lex->value_list.push_back($3)) - YYABORT; + MYSQL_YYABORT; }; opt_low_priority: @@ -8306,7 +8469,7 @@ single_multi: { if (!Select->add_table_to_list(YYTHD, $2, NULL, TL_OPTION_UPDATING, Lex->lock_option)) - YYABORT; + MYSQL_YYABORT; } where_clause opt_order_clause delete_limit_clause {} @@ -8315,14 +8478,14 @@ single_multi: FROM join_table_list where_clause { if (multi_delete_set_locks_and_link_aux_tables(Lex)) - YYABORT; + MYSQL_YYABORT; } | FROM table_wild_list { mysql_init_multi_delete(Lex); } USING join_table_list where_clause { if (multi_delete_set_locks_and_link_aux_tables(Lex)) - YYABORT; + MYSQL_YYABORT; } ; @@ -8336,7 +8499,7 @@ table_wild_one: if (!Select->add_table_to_list(YYTHD, new Table_ident($1), $3, TL_OPTION_UPDATING | TL_OPTION_ALIAS, Lex->lock_option)) - YYABORT; + MYSQL_YYABORT; } | ident '.' ident opt_wild opt_table_alias { @@ -8346,7 +8509,7 @@ table_wild_one: TL_OPTION_UPDATING | TL_OPTION_ALIAS, Lex->lock_option)) - YYABORT; + MYSQL_YYABORT; } ; @@ -8400,7 +8563,7 @@ show_param: LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_DATABASES; if (prepare_schema_table(YYTHD, lex, 0, SCH_SCHEMATA)) - YYABORT; + MYSQL_YYABORT; } | opt_full TABLES opt_db wild_and_where { @@ -8408,7 +8571,7 @@ show_param: lex->sql_command= SQLCOM_SHOW_TABLES; lex->select_lex.db= $3; if (prepare_schema_table(YYTHD, lex, 0, SCH_TABLE_NAMES)) - YYABORT; + MYSQL_YYABORT; } | opt_full TRIGGERS_SYM opt_db wild_and_where { @@ -8416,7 +8579,7 @@ show_param: lex->sql_command= SQLCOM_SHOW_TRIGGERS; lex->select_lex.db= $3; if (prepare_schema_table(YYTHD, lex, 0, SCH_TRIGGERS)) - YYABORT; + MYSQL_YYABORT; } | EVENTS_SYM opt_db wild_and_where { @@ -8424,7 +8587,7 @@ show_param: lex->sql_command= SQLCOM_SHOW_EVENTS; lex->select_lex.db= $2; if (prepare_schema_table(YYTHD, lex, 0, SCH_EVENTS)) - YYABORT; + MYSQL_YYABORT; } | TABLE_SYM STATUS_SYM opt_db wild_and_where { @@ -8432,7 +8595,7 @@ show_param: lex->sql_command= SQLCOM_SHOW_TABLE_STATUS; lex->select_lex.db= $3; if (prepare_schema_table(YYTHD, lex, 0, SCH_TABLES)) - YYABORT; + MYSQL_YYABORT; } | OPEN_SYM TABLES opt_db wild_and_where { @@ -8440,7 +8603,7 @@ show_param: lex->sql_command= SQLCOM_SHOW_OPEN_TABLES; lex->select_lex.db= $3; if (prepare_schema_table(YYTHD, lex, 0, SCH_OPEN_TABLES)) - YYABORT; + MYSQL_YYABORT; } | opt_full PLUGIN_SYM { @@ -8448,21 +8611,19 @@ show_param: WARN_DEPRECATED(yythd, "5.2", "SHOW PLUGIN", "'SHOW PLUGINS'"); lex->sql_command= SQLCOM_SHOW_PLUGINS; if (prepare_schema_table(YYTHD, lex, 0, SCH_PLUGINS)) - YYABORT; + MYSQL_YYABORT; } | PLUGINS_SYM { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_PLUGINS; if (prepare_schema_table(YYTHD, lex, 0, SCH_PLUGINS)) - YYABORT; + MYSQL_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; @@ -8470,7 +8631,7 @@ show_param: if ($5) $4->change_db($5); if (prepare_schema_table(YYTHD, lex, $4, SCH_COLUMNS)) - YYABORT; + MYSQL_YYABORT; } | NEW_SYM MASTER_SYM FOR_SYM SLAVE WITH MASTER_LOG_FILE_SYM EQ TEXT_STRING_sys AND_SYM MASTER_LOG_POS_SYM EQ ulonglong_num @@ -8502,7 +8663,7 @@ show_param: if ($4) $3->change_db($4); if (prepare_schema_table(YYTHD, lex, $3, SCH_STATISTICS)) - YYABORT; + MYSQL_YYABORT; } | COLUMN_SYM TYPES_SYM { @@ -8520,7 +8681,7 @@ show_param: LEX *lex=Lex; lex->sql_command= SQLCOM_SHOW_STORAGE_ENGINES; if (prepare_schema_table(YYTHD, lex, 0, SCH_ENGINES)) - YYABORT; + MYSQL_YYABORT; } | AUTHORS_SYM { @@ -8551,7 +8712,7 @@ show_param: lex->sql_command= SQLCOM_SHOW_STATUS; lex->option_type= $1; if (prepare_schema_table(YYTHD, lex, 0, SCH_STATUS)) - YYABORT; + MYSQL_YYABORT; } | INNOBASE_SYM STATUS_SYM { @@ -8561,7 +8722,7 @@ show_param: ha_resolve_by_legacy_type(YYTHD, DB_TYPE_INNODB))) { my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), "InnoDB"); - YYABORT; + MYSQL_YYABORT; } WARN_DEPRECATED(yythd, "5.2", "SHOW INNODB STATUS", "'SHOW ENGINE INNODB STATUS'"); } @@ -8573,7 +8734,7 @@ show_param: ha_resolve_by_legacy_type(YYTHD, DB_TYPE_INNODB))) { my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), "InnoDB"); - YYABORT; + MYSQL_YYABORT; } WARN_DEPRECATED(yythd, "5.2", "SHOW MUTEX STATUS", "'SHOW ENGINE INNODB MUTEX'"); } @@ -8585,21 +8746,21 @@ show_param: lex->sql_command= SQLCOM_SHOW_VARIABLES; lex->option_type= $1; if (prepare_schema_table(YYTHD, lex, 0, SCH_VARIABLES)) - YYABORT; + MYSQL_YYABORT; } | charset wild_and_where { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_CHARSETS; if (prepare_schema_table(YYTHD, lex, 0, SCH_CHARSETS)) - YYABORT; + MYSQL_YYABORT; } | COLLATION_SYM wild_and_where { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_COLLATIONS; if (prepare_schema_table(YYTHD, lex, 0, SCH_COLLATIONS)) - YYABORT; + MYSQL_YYABORT; } | GRANTS { @@ -8607,7 +8768,7 @@ show_param: lex->sql_command= SQLCOM_SHOW_GRANTS; LEX_USER *curr_user; if (!(curr_user= (LEX_USER*) lex->thd->alloc(sizeof(st_lex_user)))) - YYABORT; + MYSQL_YYABORT; bzero(curr_user, sizeof(st_lex_user)); lex->grant_user= curr_user; } @@ -8629,7 +8790,7 @@ show_param: LEX *lex= Lex; lex->sql_command = SQLCOM_SHOW_CREATE; if (!lex->select_lex.add_table_to_list(YYTHD, $3, NULL,0)) - YYABORT; + MYSQL_YYABORT; lex->only_view= 0; lex->create_info.storage_media= HA_SM_DEFAULT; } @@ -8638,7 +8799,7 @@ show_param: LEX *lex= Lex; lex->sql_command = SQLCOM_SHOW_CREATE; if (!lex->select_lex.add_table_to_list(YYTHD, $3, NULL, 0)) - YYABORT; + MYSQL_YYABORT; lex->only_view= 1; } | MASTER_SYM STATUS_SYM @@ -8668,24 +8829,24 @@ show_param: LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_STATUS_PROC; if (!sp_add_to_query_tables(YYTHD, lex, "mysql", "proc", TL_READ)) - YYABORT; + MYSQL_YYABORT; if (prepare_schema_table(YYTHD, lex, 0, SCH_PROCEDURES)) - YYABORT; + MYSQL_YYABORT; } | FUNCTION_SYM STATUS_SYM wild_and_where { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_STATUS_FUNC; if (!sp_add_to_query_tables(YYTHD, lex, "mysql", "proc", TL_READ)) - YYABORT; + MYSQL_YYABORT; if (prepare_schema_table(YYTHD, lex, 0, SCH_PROCEDURES)) - YYABORT; + MYSQL_YYABORT; } | PROCEDURE CODE_SYM sp_name { #ifdef DBUG_OFF - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; #else Lex->sql_command= SQLCOM_SHOW_PROC_CODE; Lex->spname= $3; @@ -8694,8 +8855,8 @@ show_param: | FUNCTION_SYM CODE_SYM sp_name { #ifdef DBUG_OFF - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; #else Lex->sql_command= SQLCOM_SHOW_FUNC_CODE; Lex->spname= $3; @@ -8770,7 +8931,7 @@ describe: lex->select_lex.db= 0; lex->verbose= 0; if (prepare_schema_table(YYTHD, lex, $2, SCH_COLUMNS)) - YYABORT; + MYSQL_YYABORT; } opt_describe_column {} | describe_command opt_extended_describe @@ -8915,7 +9076,7 @@ load: LOAD DATA_SYM if (lex->sphead) { my_error(ER_SP_BADSTATEMENT, MYF(0), "LOAD DATA"); - YYABORT; + MYSQL_YYABORT; } lex->fname_start= lex->ptr; } @@ -8930,11 +9091,11 @@ load: LOAD DATA_SYM if (lex->sphead) { my_error(ER_SP_BADSTATEMENT, MYF(0), "LOAD TABLE"); - YYABORT; + MYSQL_YYABORT; } lex->sql_command = SQLCOM_LOAD_MASTER_TABLE; if (!Select->add_table_to_list(YYTHD, $3, NULL, TL_OPTION_UPDATING)) - YYABORT; + MYSQL_YYABORT; }; load_data: @@ -8947,7 +9108,7 @@ load_data: lex->duplicates= DUP_ERROR; lex->ignore= 0; if (!(lex->exchange= new sql_exchange($4.str, 0))) - YYABORT; + MYSQL_YYABORT; } opt_duplicate INTO { @@ -8959,11 +9120,13 @@ load_data: LEX *lex=Lex; if (!Select->add_table_to_list(YYTHD, $10, NULL, TL_OPTION_UPDATING, lex->lock_option)) - YYABORT; + MYSQL_YYABORT; lex->field_list.empty(); lex->update_list.empty(); lex->value_list.empty(); } + opt_load_data_charset + { Lex->exchange->cs= $12; } opt_field_term opt_line_term opt_ignore_lines opt_field_or_var_spec opt_load_data_set_spec {} @@ -9136,13 +9299,13 @@ param_marker: if (! lex->parsing_options.allows_variable) { my_error(ER_VIEW_SELECT_VARIABLE, MYF(0)); - YYABORT; + MYSQL_YYABORT; } item= new Item_param((uint) (lex->tok_start - (uchar *) thd->query)); if (!($$= item) || lex->param_list.push_back(item)) { my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); - YYABORT; + MYSQL_YYABORT; } } ; @@ -9208,7 +9371,7 @@ NUM_literal: $$= new Item_decimal($1.str, $1.length, YYTHD->charset()); if (YYTHD->net.report_error) { - YYABORT; + MYSQL_YYABORT; } } | FLOAT_NUM @@ -9216,7 +9379,7 @@ NUM_literal: $$ = new Item_float($1.str, $1.length); if (YYTHD->net.report_error) { - YYABORT; + MYSQL_YYABORT; } } ; @@ -9261,7 +9424,7 @@ simple_ident: if (! lex->parsing_options.allows_variable) { my_error(ER_VIEW_SELECT_VARIABLE, MYF(0)); - YYABORT; + MYSQL_YYABORT; } Item_splocal *splocal; @@ -9321,14 +9484,14 @@ simple_ident_q: !new_row) { my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "OLD", "on INSERT"); - YYABORT; + MYSQL_YYABORT; } if (lex->trg_chistics.event == TRG_EVENT_DELETE && new_row) { my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "NEW", "on DELETE"); - YYABORT; + MYSQL_YYABORT; } DBUG_ASSERT(!new_row || @@ -9343,7 +9506,7 @@ simple_ident_q: $3.str, SELECT_ACL, read_only))) - YYABORT; + MYSQL_YYABORT; /* Let us add this item to list of all Item_trigger_field objects @@ -9414,13 +9577,13 @@ field_ident: if (my_strcasecmp(table_alias_charset, $1.str, table->db)) { my_error(ER_WRONG_DB_NAME, MYF(0), $1.str); - YYABORT; + MYSQL_YYABORT; } if (my_strcasecmp(table_alias_charset, $3.str, table->table_name)) { my_error(ER_WRONG_TABLE_NAME, MYF(0), $3.str); - YYABORT; + MYSQL_YYABORT; } $$=$5; } @@ -9430,7 +9593,7 @@ field_ident: if (my_strcasecmp(table_alias_charset, $1.str, table->alias)) { my_error(ER_WRONG_TABLE_NAME, MYF(0), $1.str); - YYABORT; + MYSQL_YYABORT; } $$=$3; } @@ -9462,7 +9625,7 @@ IDENT_sys: { my_error(ER_INVALID_CHARACTER_STRING, MYF(0), cs->csname, $1.str + wlen); - YYABORT; + MYSQL_YYABORT; } $$= $1; } @@ -9545,32 +9708,32 @@ user: { THD *thd= YYTHD; if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) - YYABORT; + MYSQL_YYABORT; $$->user = $1; $$->host.str= (char *) "%"; $$->host.length= 1; if (check_string_length(&$$->user, ER(ER_USERNAME), USERNAME_LENGTH)) - YYABORT; + MYSQL_YYABORT; } | ident_or_text '@' ident_or_text { THD *thd= YYTHD; if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) - YYABORT; + MYSQL_YYABORT; $$->user = $1; $$->host=$3; if (check_string_length(&$$->user, ER(ER_USERNAME), USERNAME_LENGTH) || check_string_length(&$$->host, ER(ER_HOSTNAME), HOSTNAME_LENGTH)) - YYABORT; + MYSQL_YYABORT; } | CURRENT_USER optional_braces { if (!($$=(LEX_USER*) YYTHD->alloc(sizeof(st_lex_user)))) - YYABORT; + MYSQL_YYABORT; /* empty LEX_USER means current_user and will be handled in the get_current_user() function @@ -9959,7 +10122,7 @@ option_type_value: if (!(i= new sp_instr_stmt(sp->instructions(), lex->spcont, lex))) - YYABORT; + MYSQL_YYABORT; /* Extract the query statement from the tokenizer. The @@ -9972,7 +10135,7 @@ option_type_value: qbuff.length= lex->tok_end - sp->m_tmp_query; if (!(qbuff.str= alloc_root(YYTHD->mem_root, qbuff.length + 5))) - YYABORT; + MYSQL_YYABORT; strmake(strmake(qbuff.str, "SET ", 4), (char *)sp->m_tmp_query, qbuff.length); @@ -10028,8 +10191,8 @@ sys_option_value: LINT_INIT(sp_fld); if ($1) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } if ($4) it= $4; @@ -10051,7 +10214,7 @@ sys_option_value: lex->spcont, trg_fld, it, lex))) - YYABORT; + MYSQL_YYABORT; /* Let us add this item to list of all Item_trigger_field @@ -10078,8 +10241,8 @@ sys_option_value: Item *it; if ($1) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } spv= ctx->find_variable(&$2.base_name); @@ -10134,9 +10297,9 @@ option_value: if (spc && spc->find_variable(&names)) my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), names.str); else - yyerror(ER(ER_SYNTAX_ERROR)); + my_parse_error(ER(ER_SYNTAX_ERROR)); - YYABORT; + MYSQL_YYABORT; } | NAMES_SYM charset_name_or_default opt_collate { @@ -10147,7 +10310,7 @@ option_value: { my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0), $3->name, $2->csname); - YYABORT; + MYSQL_YYABORT; } lex->var_list.push_back(new set_var_collation_client($3,$3,$3)); } @@ -10164,10 +10327,10 @@ option_value: if (spc && spc->find_variable(&pw)) { my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), pw.str); - YYABORT; + MYSQL_YYABORT; } if (!(user=(LEX_USER*) thd->alloc(sizeof(LEX_USER)))) - YYABORT; + MYSQL_YYABORT; user->host=null_lex_str; user->user.str=thd->security_ctx->priv_user; thd->lex->var_list.push_back(new set_var_password(user, $3)); @@ -10191,7 +10354,7 @@ internal_variable_name: /* Not an SP local variable */ sys_var *tmp=find_sys_var($1.str, $1.length); if (!tmp) - YYABORT; + MYSQL_YYABORT; $$.var= tmp; $$.base_name= null_lex_str; /* @@ -10200,7 +10363,7 @@ internal_variable_name: */ if (tmp == &sys_time_zone && lex->add_time_zone_tables_to_query_tables(YYTHD)) - YYABORT; + MYSQL_YYABORT; else if (spc && tmp == &sys_autocommit) { /* @@ -10222,8 +10385,8 @@ internal_variable_name: LEX *lex= Lex; if (check_reserved_words(&$1)) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } if (lex->sphead && lex->sphead->m_type == TYPE_ENUM_TRIGGER && (!my_strcasecmp(system_charset_info, $1.str, "NEW") || @@ -10232,18 +10395,18 @@ internal_variable_name: if ($1.str[0]=='O' || $1.str[0]=='o') { my_error(ER_TRG_CANT_CHANGE_ROW, MYF(0), "OLD", ""); - YYABORT; + MYSQL_YYABORT; } if (lex->trg_chistics.event == TRG_EVENT_DELETE) { my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "NEW", "on DELETE"); - YYABORT; + MYSQL_YYABORT; } if (lex->trg_chistics.action_time == TRG_ACTION_AFTER) { my_error(ER_TRG_CANT_CHANGE_ROW, MYF(0), "NEW", "after "); - YYABORT; + MYSQL_YYABORT; } /* This special combination will denote field of NEW row */ $$.var= trg_new_row_fake_var; @@ -10253,7 +10416,7 @@ internal_variable_name: { sys_var *tmp=find_sys_var($3.str, $3.length); if (!tmp) - YYABORT; + MYSQL_YYABORT; if (!tmp->is_struct()) my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), $3.str); $$.var= tmp; @@ -10264,7 +10427,7 @@ internal_variable_name: { sys_var *tmp=find_sys_var($3.str, $3.length); if (!tmp) - YYABORT; + MYSQL_YYABORT; if (!tmp->is_struct()) my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), $3.str); $$.var= tmp; @@ -10316,7 +10479,7 @@ lock: if (lex->sphead) { my_error(ER_SP_BADSTATEMENT, MYF(0), "LOCK"); - YYABORT; + MYSQL_YYABORT; } lex->sql_command= SQLCOM_LOCK_TABLES; } @@ -10336,7 +10499,7 @@ table_lock: table_ident opt_table_alias lock_option { if (!Select->add_table_to_list(YYTHD, $1, $2, 0, (thr_lock_type) $3)) - YYABORT; + MYSQL_YYABORT; } ; @@ -10355,7 +10518,7 @@ unlock: if (lex->sphead) { my_error(ER_SP_BADSTATEMENT, MYF(0), "UNLOCK"); - YYABORT; + MYSQL_YYABORT; } lex->sql_command= SQLCOM_UNLOCK_TABLES; } @@ -10375,11 +10538,11 @@ handler: if (lex->sphead) { my_error(ER_SP_BADSTATEMENT, MYF(0), "HANDLER"); - YYABORT; + MYSQL_YYABORT; } lex->sql_command = SQLCOM_HA_OPEN; if (!lex->current_select->add_table_to_list(lex->thd, $2, $4, 0)) - YYABORT; + MYSQL_YYABORT; } | HANDLER_SYM table_ident_nodb CLOSE_SYM { @@ -10387,11 +10550,11 @@ handler: if (lex->sphead) { my_error(ER_SP_BADSTATEMENT, MYF(0), "HANDLER"); - YYABORT; + MYSQL_YYABORT; } lex->sql_command = SQLCOM_HA_CLOSE; if (!lex->current_select->add_table_to_list(lex->thd, $2, 0, 0)) - YYABORT; + MYSQL_YYABORT; } | HANDLER_SYM table_ident_nodb READ_SYM { @@ -10399,7 +10562,7 @@ handler: if (lex->sphead) { my_error(ER_SP_BADSTATEMENT, MYF(0), "HANDLER"); - YYABORT; + MYSQL_YYABORT; } lex->expr_allows_subselect= FALSE; lex->sql_command = SQLCOM_HA_READ; @@ -10407,7 +10570,7 @@ handler: lex->current_select->select_limit= new Item_int((int32) 1); lex->current_select->offset_limit= 0; if (!lex->current_select->add_table_to_list(lex->thd, $2, 0, 0)) - YYABORT; + MYSQL_YYABORT; } handler_read_or_scan where_clause opt_limit_clause { @@ -10436,7 +10599,7 @@ handler_rkey_function: lex->ha_read_mode = RKEY; lex->ha_rkey_mode=$1; if (!(lex->insert_list = new List_item)) - YYABORT; + MYSQL_YYABORT; } '(' values ')' { } ; @@ -10468,8 +10631,8 @@ revoke_command: LEX *lex= Lex; if (lex->columns.elements) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } lex->sql_command= SQLCOM_REVOKE; lex->type= TYPE_ENUM_FUNCTION; @@ -10481,8 +10644,8 @@ revoke_command: LEX *lex= Lex; if (lex->columns.elements) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } lex->sql_command= SQLCOM_REVOKE; lex->type= TYPE_ENUM_PROCEDURE; @@ -10514,8 +10677,8 @@ grant_command: LEX *lex= Lex; if (lex->columns.elements) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } lex->sql_command= SQLCOM_GRANT; lex->type= TYPE_ENUM_FUNCTION; @@ -10527,8 +10690,8 @@ grant_command: LEX *lex= Lex; if (lex->columns.elements) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } lex->sql_command= SQLCOM_GRANT; lex->type= TYPE_ENUM_PROCEDURE; @@ -10607,7 +10770,7 @@ require_list_element: if (lex->x509_subject) { my_error(ER_DUP_ARGUMENT, MYF(0), "SUBJECT"); - YYABORT; + MYSQL_YYABORT; } lex->x509_subject=$2.str; } @@ -10617,7 +10780,7 @@ require_list_element: if (lex->x509_issuer) { my_error(ER_DUP_ARGUMENT, MYF(0), "ISSUER"); - YYABORT; + MYSQL_YYABORT; } lex->x509_issuer=$2.str; } @@ -10627,7 +10790,7 @@ require_list_element: if (lex->ssl_cipher) { my_error(ER_DUP_ARGUMENT, MYF(0), "CIPHER"); - YYABORT; + MYSQL_YYABORT; } lex->ssl_cipher=$2.str; } @@ -10640,14 +10803,14 @@ grant_ident: LEX *lex= thd->lex; uint dummy; if (thd->copy_db_to(&lex->current_select->db, &dummy)) - YYABORT; + MYSQL_YYABORT; if (lex->grant == GLOBAL_ACLS) lex->grant = DB_ACLS & ~GRANT_ACL; else if (lex->columns.elements) { my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE), MYF(0)); - YYABORT; + MYSQL_YYABORT; } } | ident '.' '*' @@ -10660,7 +10823,7 @@ grant_ident: { my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE), MYF(0)); - YYABORT; + MYSQL_YYABORT; } } | '*' '.' '*' @@ -10673,14 +10836,14 @@ grant_ident: { my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE), MYF(0)); - YYABORT; + MYSQL_YYABORT; } } | table_ident { LEX *lex=Lex; if (!lex->current_select->add_table_to_list(lex->thd, $1,NULL,0)) - YYABORT; + MYSQL_YYABORT; if (lex->grant == GLOBAL_ACLS) lex->grant = TABLE_ACLS & ~GRANT_ACL; } @@ -10688,21 +10851,21 @@ grant_ident: user_list: - user { if (Lex->users_list.push_back($1)) YYABORT;} + user { if (Lex->users_list.push_back($1)) MYSQL_YYABORT;} | user_list ',' user { if (Lex->users_list.push_back($3)) - YYABORT; + MYSQL_YYABORT; } ; grant_list: - grant_user { if (Lex->users_list.push_back($1)) YYABORT;} + grant_user { if (Lex->users_list.push_back($1)) MYSQL_YYABORT;} | grant_list ',' grant_user { if (Lex->users_list.push_back($3)) - YYABORT; + MYSQL_YYABORT; } ; @@ -10925,17 +11088,17 @@ union_list: { /* Only the last SELECT can have INTO...... */ my_error(ER_WRONG_USAGE, MYF(0), "UNION", "INTO"); - YYABORT; + MYSQL_YYABORT; } if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } /* This counter shouldn't be incremented for UNION parts */ Lex->nest_level--; if (mysql_new_select(lex, 0)) - YYABORT; + MYSQL_YYABORT; mysql_init_select(lex); lex->current_select->linkage=UNION_TYPE; if ($2) /* UNION DISTINCT - remember position */ @@ -11028,8 +11191,8 @@ subselect_start: LEX *lex=Lex; if (!lex->expr_allows_subselect) { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; } /* we are making a "derived table" for the parenthesis @@ -11039,7 +11202,7 @@ subselect_start: SELECT * FROM ((SELECT ...) UNION ...) */ if (mysql_new_select(Lex, 1)) - YYABORT; + MYSQL_YYABORT; }; subselect_end: @@ -11159,7 +11322,7 @@ view_tail: lex->sql_command= SQLCOM_CREATE_VIEW; /* first table in list is target VIEW name */ if (!lex->select_lex.add_table_to_list(thd, $3, NULL, TL_OPTION_UPDATING)) - YYABORT; + MYSQL_YYABORT; } view_list_opt AS view_select view_check_option {} @@ -11250,11 +11413,11 @@ trigger_tail: if (lex->sphead) { my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "TRIGGER"); - YYABORT; + MYSQL_YYABORT; } if (!(sp= new sp_head())) - YYABORT; + MYSQL_YYABORT; sp->reset_thd_mem_root(YYTHD); sp->init(lex); sp->init_sp_name(YYTHD, $3); @@ -11292,7 +11455,7 @@ trigger_tail: sp->restore_thd_mem_root(YYTHD); if (sp->is_not_allowed_in_function("trigger")) - YYABORT; + MYSQL_YYABORT; /* We have to do it after parsing trigger body, because some of @@ -11303,7 +11466,7 @@ trigger_tail: (LEX_STRING*) 0, TL_OPTION_UPDATING, TL_IGNORE)) - YYABORT; + MYSQL_YYABORT; } ; @@ -11331,7 +11494,7 @@ sp_tail: if (lex->sphead) { my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "PROCEDURE"); - YYABORT; + MYSQL_YYABORT; } lex->stmt_definition_begin= $2; @@ -11420,23 +11583,23 @@ xa: XA_SYM begin_or_start xid opt_join_or_resume xid: text_string { - YYERROR_UNLESS($1->length() <= MAXGTRIDSIZE); + MYSQL_YYABORT_UNLESS($1->length() <= MAXGTRIDSIZE); if (!(Lex->xid=(XID *)YYTHD->alloc(sizeof(XID)))) - YYABORT; + MYSQL_YYABORT; Lex->xid->set(1L, $1->ptr(), $1->length(), 0, 0); } | text_string ',' text_string { - YYERROR_UNLESS($1->length() <= MAXGTRIDSIZE && $3->length() <= MAXBQUALSIZE); + MYSQL_YYABORT_UNLESS($1->length() <= MAXGTRIDSIZE && $3->length() <= MAXBQUALSIZE); if (!(Lex->xid=(XID *)YYTHD->alloc(sizeof(XID)))) - YYABORT; + MYSQL_YYABORT; Lex->xid->set(1L, $1->ptr(), $1->length(), $3->ptr(), $3->length()); } | text_string ',' text_string ',' ulong_num { - YYERROR_UNLESS($1->length() <= MAXGTRIDSIZE && $3->length() <= MAXBQUALSIZE); + MYSQL_YYABORT_UNLESS($1->length() <= MAXGTRIDSIZE && $3->length() <= MAXBQUALSIZE); if (!(Lex->xid=(XID *)YYTHD->alloc(sizeof(XID)))) - YYABORT; + MYSQL_YYABORT; Lex->xid->set($5, $1->ptr(), $1->length(), $3->ptr(), $3->length()); } ; diff --git a/sql/table.cc b/sql/table.cc index ed3cac85214..560f53bae26 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 @@ -2891,7 +2890,9 @@ void st_table_list::hide_view_error(THD *thd) thd->net.last_errno == ER_SP_DOES_NOT_EXIST || thd->net.last_errno == ER_PROCACCESS_DENIED_ERROR || thd->net.last_errno == ER_COLUMNACCESS_DENIED_ERROR || - thd->net.last_errno == ER_TABLEACCESS_DENIED_ERROR) + thd->net.last_errno == ER_TABLEACCESS_DENIED_ERROR || + thd->net.last_errno == ER_TABLE_NOT_LOCKED || + thd->net.last_errno == ER_NO_SUCH_TABLE) { TABLE_LIST *top= top_table(); thd->clear_error(); diff --git a/sql/table.h b/sql/table.h index fc2f25f3aa8..54c820d391c 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 */ @@ -408,6 +408,10 @@ struct st_table { /* If true, the current table row is considered to have all columns set to NULL, including columns declared as "not null" (see maybe_null). + + TODO: Each of these flags take up 8 bits. They can just as easily + be put into one single unsigned long and instead of taking up 18 + bytes, it would take up 4. */ my_bool null_row; my_bool force_index; @@ -415,6 +419,7 @@ struct st_table { my_bool key_read, no_keyread; my_bool locked_by_flush; my_bool locked_by_logger; + my_bool no_replicate; my_bool locked_by_name; my_bool fulltext_searched; my_bool no_cache; @@ -851,7 +856,7 @@ typedef struct st_table_list int view_check_option(THD *thd, bool ignore_failure); bool setup_underlying(THD *thd); void cleanup_items(); - bool placeholder() {return derived || view; } + bool placeholder() {return derived || view || schema_table || !table; } void print(THD *thd, String *str); bool check_single_table(st_table_list **table, table_map map, st_table_list *view); diff --git a/storage/ndb/include/ndb_global.h.in b/storage/ndb/include/ndb_global.h.in index 60d32f62ee3..dd4303f949c 100644 --- a/storage/ndb/include/ndb_global.h.in +++ b/storage/ndb/include/ndb_global.h.in @@ -115,8 +115,6 @@ static const char table_name_separator = '/'; #endif #ifdef __cplusplus -inline void* operator new(size_t, void* __p) { return __p; } -inline void* operator new[](size_t, void* __p) { return __p; } extern "C" { #endif |