diff options
author | unknown <kostja@bodhi.local> | 2006-08-02 14:13:01 +0400 |
---|---|---|
committer | unknown <kostja@bodhi.local> | 2006-08-02 14:13:01 +0400 |
commit | f3919e928468ebd4906fd310f9032a9c3cf87a71 (patch) | |
tree | ea898f8f24b260b3a08d72dd19588e5815d47e74 | |
parent | ed19079caa192e368a1b82e7e35e43c59dc23c34 (diff) | |
parent | d57c41b79d972012ae76fe0b36ee31b1e97989ef (diff) | |
download | mariadb-git-f3919e928468ebd4906fd310f9032a9c3cf87a71.tar.gz |
Merge bk-internal.mysql.com:/home/bk/mysql-5.0
into bodhi.local:/opt/local/work/mysql-5.0-runtime-merge
include/mysql.h:
Auto merged
include/sql_common.h:
Auto merged
libmysql/libmysql.c:
Auto merged
mysql-test/mysql-test-run.pl:
Auto merged
mysql-test/r/mysqldump.result:
Auto merged
mysql-test/t/udf.test:
Auto merged
ndb/src/mgmsrv/ConfigInfo.cpp:
Auto merged
sql/item.cc:
Auto merged
sql/item_func.h:
Auto merged
sql/mysql_priv.h:
Auto merged
sql/slave.cc:
Auto merged
sql/sp.cc:
Auto merged
sql/sql_lex.cc:
Auto merged
sql/sql_parse.cc:
Auto merged
sql-common/client.c:
Auto merged
sql/sql_yacc.yy:
Auto merged
sql/table.h:
Auto merged
mysql-test/r/ps.result:
Manual merge.
mysql-test/r/sp.result:
Manual merge.
mysql-test/t/ps.test:
Manual merge.
mysql-test/t/sp.test:
Manual merge.
sql/sql_prepare.cc:
Manual merge.
sql/table.cc:
Manual merge.
tests/mysql_client_test.c:
Manual merge.
61 files changed, 2084 insertions, 630 deletions
diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index 518ab7cf832..9cecdb4eafc 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -36,6 +36,7 @@ /* That one is necessary for defines of OPTION_NO_FOREIGN_KEY_CHECKS etc */ #include "mysql_priv.h" #include "log_event.h" +#include "sql_common.h" #define BIN_LOG_HEADER_SIZE 4 #define PROBE_HEADER_LEN (EVENT_LEN_OFFSET+4) @@ -1077,7 +1078,7 @@ could be out of memory"); const char *error_msg; Log_event *ev; - len = net_safe_read(mysql); + len= cli_safe_read(mysql); if (len == packet_error) { fprintf(stderr, "Got error reading packet from server: %s\n", diff --git a/include/mysql.h b/include/mysql.h index 7ed205024e2..72e5c457d5a 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -848,7 +848,6 @@ int STDCALL mysql_drop_db(MYSQL *mysql, const char *DB); #define stmt_command(mysql, command, arg, length, stmt) \ (*(mysql)->methods->advanced_command)(mysql, command, NullS, \ 0, arg, length, 1, stmt) -unsigned long net_safe_read(MYSQL* mysql); #ifdef __NETWARE__ #pragma pack(pop) /* restore alignment */ diff --git a/include/mysql_com.h b/include/mysql_com.h index ec1c133799f..60e726396cb 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -140,7 +140,6 @@ enum enum_server_command #define SERVER_STATUS_IN_TRANS 1 /* Transaction has started */ #define SERVER_STATUS_AUTOCOMMIT 2 /* Server in auto_commit mode */ -#define SERVER_STATUS_MORE_RESULTS 4 /* More results on server */ #define SERVER_MORE_RESULTS_EXISTS 8 /* Multi query - next query exists */ #define SERVER_QUERY_NO_GOOD_INDEX_USED 16 #define SERVER_QUERY_NO_INDEX_USED 32 diff --git a/include/sql_common.h b/include/sql_common.h index 7ea8b6c87e0..29a9cbd4b08 100644 --- a/include/sql_common.h +++ b/include/sql_common.h @@ -36,7 +36,7 @@ cli_advanced_command(MYSQL *mysql, enum enum_server_command command, const char *header, ulong header_length, const char *arg, ulong arg_length, my_bool skip_check, MYSQL_STMT *stmt); - +unsigned long cli_safe_read(MYSQL *mysql); void set_stmt_errmsg(MYSQL_STMT * stmt, const char *err, int errcode, const char *sqlstate); void set_mysql_error(MYSQL *mysql, int errcode, const char *sqlstate); diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 4efc3c2fd1c..21fb84fb19a 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -645,7 +645,7 @@ int cli_read_change_user_result(MYSQL *mysql, char *buff, const char *passwd) NET *net= &mysql->net; ulong pkt_length; - pkt_length= net_safe_read(mysql); + pkt_length= cli_safe_read(mysql); if (pkt_length == packet_error) return 1; @@ -666,7 +666,7 @@ int cli_read_change_user_result(MYSQL *mysql, char *buff, const char *passwd) return 1; } /* Read what server thinks about out new auth message report */ - if (net_safe_read(mysql) == packet_error) + if (cli_safe_read(mysql) == packet_error) return 1; } return 0; @@ -1887,7 +1887,7 @@ my_bool cli_read_prepare_result(MYSQL *mysql, MYSQL_STMT *stmt) DBUG_ENTER("cli_read_prepare_result"); mysql= mysql->last_used_con; - if ((packet_length= net_safe_read(mysql)) == packet_error) + if ((packet_length= cli_safe_read(mysql)) == packet_error) DBUG_RETURN(1); mysql->warning_count= 0; @@ -2505,7 +2505,8 @@ int cli_stmt_execute(MYSQL_STMT *stmt) if (stmt->param_count) { - NET *net= &stmt->mysql->net; + MYSQL *mysql= stmt->mysql; + NET *net= &mysql->net; MYSQL_BIND *param, *param_end; char *param_data; ulong length; @@ -2517,7 +2518,8 @@ int cli_stmt_execute(MYSQL_STMT *stmt) set_stmt_error(stmt, CR_PARAMS_NOT_BOUND, unknown_sqlstate); DBUG_RETURN(1); } - if (stmt->mysql->status != MYSQL_STATUS_READY) + if (mysql->status != MYSQL_STATUS_READY || + mysql->server_status & SERVER_MORE_RESULTS_EXISTS) { set_stmt_error(stmt, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); DBUG_RETURN(1); @@ -4532,7 +4534,7 @@ static int stmt_fetch_row(MYSQL_STMT *stmt, uchar *row) int cli_unbuffered_fetch(MYSQL *mysql, char **row) { - if (packet_error == net_safe_read(mysql)) + if (packet_error == cli_safe_read(mysql)) return 1; *row= ((mysql->net.read_pos[0] == 254) ? NULL : @@ -4641,7 +4643,7 @@ int cli_read_binary_rows(MYSQL_STMT *stmt) mysql= mysql->last_used_con; - while ((pkt_len= net_safe_read(mysql)) != packet_error) + while ((pkt_len= cli_safe_read(mysql)) != packet_error) { cp= net->read_pos; if (cp[0] != 254 || pkt_len >= 8) diff --git a/mysql-test/include/im_check_env.inc b/mysql-test/include/im_check_env.inc new file mode 100644 index 00000000000..169edbac6b3 --- /dev/null +++ b/mysql-test/include/im_check_env.inc @@ -0,0 +1,27 @@ +# This file is intended to be used in each IM-test. It contains stamements, +# that ensure that starting conditions (environment) for the IM-test are as +# expected. + +# Wait for mysqld1 (guarded instance) to start. + +--exec $MYSQL_TEST_DIR/t/wait_for_process.sh $IM_MYSQLD1_PATH_PID 30 started + +# Check the running instances. + +--connect (mysql1_con,localhost,root,,mysql,$IM_MYSQLD1_PORT,$IM_MYSQLD1_SOCK) + +--connection mysql1_con + +SHOW VARIABLES LIKE 'server_id'; + +--connection default + +# Let IM detect that mysqld1 is online. This delay should be longer than +# monitoring interval. + +--sleep 2 + +# Check that IM understands that mysqld1 is online, while mysqld2 is +# offline. + +SHOW INSTANCES; diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 91a9a758d1d..a5d857845ba 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -2898,12 +2898,16 @@ sub im_stop($) { while (1) { + # Check that IM-main died. + if (kill (0, $instance_manager->{'pid'})) { mtr_debug("IM-main is still alive."); last; } + # Check that IM-angel died. + if (defined $instance_manager->{'angel_pid'} && kill (0, $instance_manager->{'angel_pid'})) { @@ -2911,21 +2915,39 @@ sub im_stop($) { last; } + # Check that all guarded mysqld-instances died. + + my $guarded_mysqlds_dead= 1; + foreach my $pid (@mysqld_pids) { if (kill (0, $pid)) { mtr_debug("Guarded mysqld ($pid) is still alive."); + $guarded_mysqlds_dead= 0; last; } } + last unless $guarded_mysqlds_dead; + + # Ok, all necessary processes are dead. + $clean_shutdown= 1; last; } # Kill leftovers (the order is important). + if ($clean_shutdown) + { + mtr_debug("IM-shutdown was clean -- all processed died."); + } + else + { + mtr_debug("IM failed to shutdown gracefully. We have to clean the mess..."); + } + unless ($clean_shutdown) { @@ -2946,17 +2968,24 @@ sub im_stop($) { mtr_kill_processes(\@mysqld_pids); # Complain in error log so that a warning will be shown. - - my $errlog= "$opt_vardir/log/mysql-test-run.pl.err"; - - open (ERRLOG, ">>$errlog") || - mtr_error("Can not open error log ($errlog)"); + # + # TODO: unless BUG#20761 is fixed, we will print the warning + # to stdout, so that it can be seen on console and does not + # produce pushbuild error. + + # my $errlog= "$opt_vardir/log/mysql-test-run.pl.err"; + # + # open (ERRLOG, ">>$errlog") || + # mtr_error("Can not open error log ($errlog)"); + # + # my $ts= localtime(); + # print ERRLOG + # "Warning: [$ts] Instance Manager did not shutdown gracefully.\n"; + # + # close ERRLOG; my $ts= localtime(); - print ERRLOG - "Warning: [$ts] Instance Manager did not shutdown gracefully.\n"; - - close ERRLOG; + print "Warning: [$ts] Instance Manager did not shutdown gracefully.\n"; } # That's all. diff --git a/mysql-test/r/im_daemon_life_cycle.result b/mysql-test/r/im_daemon_life_cycle.result index ea27fcb6db1..4f7dd77a88f 100644 --- a/mysql-test/r/im_daemon_life_cycle.result +++ b/mysql-test/r/im_daemon_life_cycle.result @@ -1,4 +1,7 @@ Success: the process has been started. +SHOW VARIABLES LIKE 'server_id'; +Variable_name Value +server_id 1 SHOW INSTANCES; instance_name status mysqld1 online diff --git a/mysql-test/r/im_life_cycle.result b/mysql-test/r/im_life_cycle.result index a9f78aea7d3..8394ec491e5 100644 --- a/mysql-test/r/im_life_cycle.result +++ b/mysql-test/r/im_life_cycle.result @@ -1,8 +1,7 @@ - --------------------------------------------------------------------- --- 1.1.1. --------------------------------------------------------------------- Success: the process has been started. +SHOW VARIABLES LIKE 'server_id'; +Variable_name Value +server_id 1 SHOW INSTANCES; instance_name status mysqld1 online @@ -40,10 +39,6 @@ ERROR HY000: Bad instance name. Check that the instance with such a name exists -------------------------------------------------------------------- -- 1.1.6. -------------------------------------------------------------------- -SHOW INSTANCES; -instance_name status -mysqld1 online -mysqld2 offline Killing the process... Sleeping... Success: the process was restarted. diff --git a/mysql-test/r/im_options_set.result b/mysql-test/r/im_options_set.result index 5e6c740624e..c3035079b39 100644 --- a/mysql-test/r/im_options_set.result +++ b/mysql-test/r/im_options_set.result @@ -1,8 +1,11 @@ -server_id = 1 -server_id = 2 +Success: the process has been started. SHOW VARIABLES LIKE 'server_id'; Variable_name Value server_id 1 +SHOW INSTANCES; +instance_name status +mysqld1 online +mysqld2 offline SET mysqld1.server_id = 11; server_id =11 server_id = 2 diff --git a/mysql-test/r/im_options_unset.result b/mysql-test/r/im_options_unset.result index bf54025edb7..ba468c78a5b 100644 --- a/mysql-test/r/im_options_unset.result +++ b/mysql-test/r/im_options_unset.result @@ -1,8 +1,11 @@ -server_id = 1 -server_id = 2 +Success: the process has been started. SHOW VARIABLES LIKE 'server_id'; Variable_name Value server_id 1 +SHOW INSTANCES; +instance_name status +mysqld1 online +mysqld2 offline UNSET mysqld1.server_id; server_id = 2 SHOW VARIABLES LIKE 'server_id'; diff --git a/mysql-test/r/im_utils.result b/mysql-test/r/im_utils.result index e6a5e007ed4..be696921812 100644 --- a/mysql-test/r/im_utils.result +++ b/mysql-test/r/im_utils.result @@ -1,4 +1,7 @@ Success: the process has been started. +SHOW VARIABLES LIKE 'server_id'; +Variable_name Value +server_id 1 SHOW INSTANCES; instance_name status mysqld1 online diff --git a/mysql-test/r/mysqldump.result b/mysql-test/r/mysqldump.result index ff745021efb..0160d26527d 100644 --- a/mysql-test/r/mysqldump.result +++ b/mysql-test/r/mysqldump.result @@ -2220,7 +2220,7 @@ RETURN a+b */;; /*!50003 SET SESSION SQL_MODE=@OLD_SQL_MODE*/;; /*!50003 DROP FUNCTION IF EXISTS `bug9056_func2` */;; /*!50003 SET SESSION SQL_MODE=""*/;; -/*!50003 CREATE*/ /*!50020 DEFINER=`root`@`localhost`*/ /*!50003 FUNCTION `bug9056_func2`(f1 char binary) RETURNS char(1) +/*!50003 CREATE*/ /*!50020 DEFINER=`root`@`localhost`*/ /*!50003 FUNCTION `bug9056_func2`(f1 char binary) RETURNS char(1) CHARSET latin1 begin set f1= concat( 'hello', f1 ); return f1; diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 3d352a02ad2..70d0bae2d1d 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -485,20 +485,6 @@ execute stmt; pnum deallocate prepare stmt; drop table t1, t2; -drop table if exists t1; -create temporary table if not exists t1 (a1 int); -prepare stmt from "delete t1 from t1 where (cast(a1/3 as unsigned) * 3) = a1"; -drop temporary table t1; -create temporary table if not exists t1 (a1 int); -execute stmt; -drop temporary table t1; -create temporary table if not exists t1 (a1 int); -execute stmt; -drop temporary table t1; -create temporary table if not exists t1 (a1 int); -execute stmt; -drop temporary table t1; -deallocate prepare stmt; create table t1 (a varchar(20)); insert into t1 values ('foo'); prepare stmt FROM 'SELECT char_length (a) FROM t1'; @@ -650,47 +636,6 @@ id 3 deallocate prepare stmt; drop table t1, t2; -create table t1 (a int); -insert into t1 (a) values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10); -prepare stmt from "select * from t1 limit ?, ?"; -set @offset=0, @limit=1; -execute stmt using @offset, @limit; -a -1 -select * from t1 limit 0, 1; -a -1 -set @offset=3, @limit=2; -execute stmt using @offset, @limit; -a -4 -5 -select * from t1 limit 3, 2; -a -4 -5 -prepare stmt from "select * from t1 limit ?"; -execute stmt using @limit; -a -1 -2 -prepare stmt from "select * from t1 where a in (select a from t1 limit ?)"; -ERROR 42000: This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery' -prepare stmt from "select * from t1 union all select * from t1 limit ?, ?"; -set @offset=9; -set @limit=2; -execute stmt using @offset, @limit; -a -10 -1 -prepare stmt from "(select * from t1 limit ?, ?) union all - (select * from t1 limit ?, ?) order by a limit ?"; -execute stmt using @offset, @limit, @offset, @limit, @limit; -a -10 -10 -drop table t1; -deallocate prepare stmt; create table t1 (id int); prepare stmt from "insert into t1 (id) select id from t1 union select id from t1"; execute stmt; @@ -791,15 +736,6 @@ ERROR 42000: You have an error in your SQL syntax; check the manual that corresp select ? from t1; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '? from t1' at line 1 drop table t1; -CREATE TABLE b12651_T1(a int) ENGINE=MYISAM; -CREATE TABLE b12651_T2(b int) ENGINE=MYISAM; -CREATE VIEW b12651_V1 as SELECT b FROM b12651_T2; -PREPARE b12651 FROM 'SELECT 1 FROM b12651_T1 WHERE a IN (SELECT b FROM b12651_V1)'; -EXECUTE b12651; -1 -DROP VIEW b12651_V1; -DROP TABLE b12651_T1, b12651_T2; -DEALLOCATE PREPARE b12651; prepare stmt from "select @@time_zone"; execute stmt; @@time_zone @@ -1016,6 +952,147 @@ select @@max_prepared_stmt_count, @@prepared_stmt_count; @@max_prepared_stmt_count @@prepared_stmt_count 3 0 set global max_prepared_stmt_count= @old_max_prepared_stmt_count; +drop table if exists t1; +create temporary table if not exists t1 (a1 int); +prepare stmt from "delete t1 from t1 where (cast(a1/3 as unsigned) * 3) = a1"; +drop temporary table t1; +create temporary table if not exists t1 (a1 int); +execute stmt; +drop temporary table t1; +create temporary table if not exists t1 (a1 int); +execute stmt; +drop temporary table t1; +create temporary table if not exists t1 (a1 int); +execute stmt; +drop temporary table t1; +deallocate prepare stmt; +End of 4.1 tests +create table t1 (a varchar(20)); +insert into t1 values ('foo'); +prepare stmt FROM 'SELECT char_length (a) FROM t1'; +ERROR 42000: FUNCTION test.char_length does not exist +drop table t1; +create table t1 (a char(3) not null, b char(3) not null, +c char(3) not null, primary key (a, b, c)); +create table t2 like t1; +prepare stmt from +"select t1.a from (t1 left outer join t2 on t2.a=1 and t1.b=t2.b) + where t1.a=1"; +execute stmt; +a +execute stmt; +a +execute stmt; +a +prepare stmt from +"select t1.a, t1.b, t1.c, t2.a, t2.b, t2.c from +(t1 left outer join t2 on t2.a=? and t1.b=t2.b) +left outer join t2 t3 on t3.a=? where t1.a=?"; +set @a:=1, @b:=1, @c:=1; +execute stmt using @a, @b, @c; +a b c a b c +execute stmt using @a, @b, @c; +a b c a b c +execute stmt using @a, @b, @c; +a b c a b c +deallocate prepare stmt; +drop table t1,t2; +SET @aux= "SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS A, + INFORMATION_SCHEMA.COLUMNS B + WHERE A.TABLE_SCHEMA = B.TABLE_SCHEMA + AND A.TABLE_NAME = B.TABLE_NAME + AND A.COLUMN_NAME = B.COLUMN_NAME AND + A.TABLE_NAME = 'user'"; +prepare my_stmt from @aux; +execute my_stmt; +COUNT(*) +37 +execute my_stmt; +COUNT(*) +37 +execute my_stmt; +COUNT(*) +37 +deallocate prepare my_stmt; +drop procedure if exists p1| +drop table if exists t1| +create table t1 (id int)| +insert into t1 values(1)| +create procedure p1(a int, b int) +begin +declare c int; +select max(id)+1 into c from t1; +insert into t1 select a+b; +insert into t1 select a-b; +insert into t1 select a-c; +end| +set @a= 3, @b= 4| +prepare stmt from "call p1(?, ?)"| +execute stmt using @a, @b| +execute stmt using @a, @b| +select * from t1| +id +1 +7 +-1 +1 +7 +-1 +-5 +deallocate prepare stmt| +drop procedure p1| +drop table t1| +create table t1 (a int); +insert into t1 (a) values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10); +prepare stmt from "select * from t1 limit ?, ?"; +set @offset=0, @limit=1; +execute stmt using @offset, @limit; +a +1 +select * from t1 limit 0, 1; +a +1 +set @offset=3, @limit=2; +execute stmt using @offset, @limit; +a +4 +5 +select * from t1 limit 3, 2; +a +4 +5 +prepare stmt from "select * from t1 limit ?"; +execute stmt using @limit; +a +1 +2 +prepare stmt from "select * from t1 where a in (select a from t1 limit ?)"; +ERROR 42000: This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery' +prepare stmt from "select * from t1 union all select * from t1 limit ?, ?"; +set @offset=9; +set @limit=2; +execute stmt using @offset, @limit; +a +10 +1 +prepare stmt from "(select * from t1 limit ?, ?) union all + (select * from t1 limit ?, ?) order by a limit ?"; +execute stmt using @offset, @limit, @offset, @limit, @limit; +a +10 +10 +drop table t1; +deallocate prepare stmt; +CREATE TABLE b12651_T1(a int) ENGINE=MYISAM; +CREATE TABLE b12651_T2(b int) ENGINE=MYISAM; +CREATE VIEW b12651_V1 as SELECT b FROM b12651_T2; +PREPARE b12651 FROM 'SELECT 1 FROM b12651_T1 WHERE a IN (SELECT b FROM b12651_V1)'; +EXECUTE b12651; +1 +DROP VIEW b12651_V1; +DROP TABLE b12651_T1, b12651_T2; +DEALLOCATE PREPARE b12651; create table t1 (id int); prepare ins_call from "insert into t1 (id) values (1)"; execute ins_call; diff --git a/mysql-test/r/rpl_sp.result b/mysql-test/r/rpl_sp.result index 5dfda16c763..7b096b27733 100644 --- a/mysql-test/r/rpl_sp.result +++ b/mysql-test/r/rpl_sp.result @@ -420,4 +420,48 @@ SELECT * FROM t1; col test DROP PROCEDURE p1; + +---> Test for BUG#20438 + +---> Preparing environment... +---> connection: master +DROP PROCEDURE IF EXISTS p1; +DROP FUNCTION IF EXISTS f1; + +---> Synchronizing slave with master... + +---> connection: master + +---> Creating procedure... +/*!50003 CREATE PROCEDURE p1() SET @a = 1 */; +/*!50003 CREATE FUNCTION f1() RETURNS INT RETURN 0 */; + +---> Checking on master... +SHOW CREATE PROCEDURE p1; +Procedure sql_mode Create Procedure +p1 CREATE DEFINER=`root`@`localhost` PROCEDURE `p1`() +SET @a = 1 +SHOW CREATE FUNCTION f1; +Function sql_mode Create Function +f1 CREATE DEFINER=`root`@`localhost` FUNCTION `f1`() RETURNS int(11) +RETURN 0 + +---> Synchronizing slave with master... +---> connection: master + +---> Checking on slave... +SHOW CREATE PROCEDURE p1; +Procedure sql_mode Create Procedure +p1 CREATE DEFINER=`root`@`localhost` PROCEDURE `p1`() +SET @a = 1 +SHOW CREATE FUNCTION f1; +Function sql_mode Create Function +f1 CREATE DEFINER=`root`@`localhost` FUNCTION `f1`() RETURNS int(11) +RETURN 0 + +---> connection: master + +---> Cleaning up... +DROP PROCEDURE p1; +DROP FUNCTION f1; drop table t1; diff --git a/mysql-test/r/rpl_trigger.result b/mysql-test/r/rpl_trigger.result index 3e4a3349e13..49f0f5c4c44 100644 --- a/mysql-test/r/rpl_trigger.result +++ b/mysql-test/r/rpl_trigger.result @@ -896,3 +896,50 @@ Tables_in_test (t_) SHOW TRIGGERS; Trigger Event Table Statement Timing Created sql_mode Definer RESET MASTER; +START SLAVE; + +---> Test for BUG#20438 + +---> Preparing environment... +---> connection: master +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t2; + +---> Synchronizing slave with master... + +---> connection: master + +---> Creating objects... +CREATE TABLE t1(c INT); +CREATE TABLE t2(c INT); +/*!50003 CREATE TRIGGER t1_bi BEFORE INSERT ON t1 +FOR EACH ROW +INSERT INTO t2 VALUES(NEW.c * 10) */; + +---> Inserting value... +INSERT INTO t1 VALUES(1); + +---> Checking on master... +SELECT * FROM t1; +c +1 +SELECT * FROM t2; +c +10 + +---> Synchronizing slave with master... +---> connection: master + +---> Checking on slave... +SELECT * FROM t1; +c +1 +SELECT * FROM t2; +c +10 + +---> connection: master + +---> Cleaning up... +DROP TABLE t1; +DROP TABLE t2; diff --git a/mysql-test/r/rpl_view.result b/mysql-test/r/rpl_view.result index cf4c161b296..5a101defe38 100644 --- a/mysql-test/r/rpl_view.result +++ b/mysql-test/r/rpl_view.result @@ -54,3 +54,40 @@ slave-bin.000001 # Query 1 # use `test`; delete from v1 where a=2 slave-bin.000001 # Query 1 # use `test`; ALTER ALGORITHM=UNDEFINED DEFINER=root@localhost SQL SECURITY DEFINER VIEW v1 AS select a as b from t1 slave-bin.000001 # Query 1 # use `test`; drop view v1 slave-bin.000001 # Query 1 # use `test`; drop table t1 + +---> Test for BUG#20438 + +---> Preparing environment... +---> connection: master +DROP TABLE IF EXISTS t1; +DROP VIEW IF EXISTS v1; + +---> Synchronizing slave with master... + +---> connection: master + +---> Creating objects... +CREATE TABLE t1(c INT); +/*!50003 CREATE VIEW v1 AS SELECT * FROM t1 */; + +---> Inserting value... +INSERT INTO t1 VALUES(1); + +---> Checking on master... +SELECT * FROM t1; +c +1 + +---> Synchronizing slave with master... +---> connection: master + +---> Checking on slave... +SELECT * FROM t1; +c +1 + +---> connection: master + +---> Cleaning up... +DROP VIEW v1; +DROP TABLE t1; diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result index 994501767ba..7bdfa78066c 100644 --- a/mysql-test/r/show_check.result +++ b/mysql-test/r/show_check.result @@ -625,3 +625,7 @@ View Create View v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select sql_cache 1 AS `1` DROP PROCEDURE p1; DROP VIEW v1; +SHOW TABLES FROM no_such_database; +ERROR 42000: Unknown database 'no_such_database' +SHOW COLUMNS FROM no_such_table; +ERROR 42S02: Table 'test.no_such_table' doesn't exist diff --git a/mysql-test/r/sp-code.result b/mysql-test/r/sp-code.result index 8bdb9a724d7..4ae38861d29 100644 --- a/mysql-test/r/sp-code.result +++ b/mysql-test/r/sp-code.result @@ -155,11 +155,11 @@ Pos Instruction 0 stmt 9 "drop temporary table if exists sudoku..." 1 stmt 1 "create temporary table sudoku_work ( ..." 2 stmt 1 "create temporary table sudoku_schedul..." -3 stmt 95 "call sudoku_init(" +3 stmt 95 "call sudoku_init()" 4 jump_if_not 7(8) p_naive@0 5 stmt 4 "update sudoku_work set cnt = 0 where ..." 6 jump 8 -7 stmt 95 "call sudoku_count(" +7 stmt 95 "call sudoku_count()" 8 stmt 6 "insert into sudoku_schedule (row,col)..." 9 set v_scounter@2 0 10 set v_i@3 1 @@ -199,3 +199,10 @@ Pos Instruction 44 jump 14 45 stmt 9 "drop temporary table sudoku_work, sud..." drop procedure sudoku_solve; +DROP PROCEDURE IF EXISTS p1; +CREATE PROCEDURE p1() CREATE INDEX idx ON t1 (c1); +SHOW PROCEDURE CODE p1; +Pos Instruction +0 stmt 2 "CREATE INDEX idx ON t1 (c1)" +DROP PROCEDURE p1; +End of 5.0 tests. diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result index 924963017eb..da1fc58db57 100644 --- a/mysql-test/r/sp-error.result +++ b/mysql-test/r/sp-error.result @@ -1174,3 +1174,16 @@ drop procedure bug15091; drop function if exists bug16896; create aggregate function bug16896() returns int return 1; ERROR 42000: AGGREGATE is not supported for stored functions +DROP PROCEDURE IF EXISTS bug14702; +CREATE IF NOT EXISTS PROCEDURE bug14702() +BEGIN +END; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'IF NOT EXISTS PROCEDURE bug14702() +BEGIN +END' at line 1 +CREATE PROCEDURE IF NOT EXISTS bug14702() +BEGIN +END; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'IF NOT EXISTS bug14702() +BEGIN +END' at line 1 diff --git a/mysql-test/r/sp-security.result b/mysql-test/r/sp-security.result index a53b4c4d246..1198efc4f3b 100644 --- a/mysql-test/r/sp-security.result +++ b/mysql-test/r/sp-security.result @@ -451,3 +451,55 @@ SELECT Host,User,Password FROM mysql.user WHERE User='user19857'; Host User Password localhost user19857 *82DC221D557298F6CE9961037DB1C90604792F5C DROP USER user19857@localhost; +DROP TABLE IF EXISTS t1; +DROP VIEW IF EXISTS v1; +DROP FUNCTION IF EXISTS f_suid; +DROP PROCEDURE IF EXISTS p_suid; +DROP FUNCTION IF EXISTS f_evil; +DELETE FROM mysql.user WHERE user LIKE 'mysqltest\_%'; +DELETE FROM mysql.db WHERE user LIKE 'mysqltest\_%'; +DELETE FROM mysql.tables_priv WHERE user LIKE 'mysqltest\_%'; +DELETE FROM mysql.columns_priv WHERE user LIKE 'mysqltest\_%'; +FLUSH PRIVILEGES; +CREATE TABLE t1 (i INT); +CREATE FUNCTION f_suid(i INT) RETURNS INT SQL SECURITY DEFINER RETURN 0; +CREATE PROCEDURE p_suid(IN i INT) SQL SECURITY DEFINER SET @c:= 0; +CREATE USER mysqltest_u1@localhost; +GRANT EXECUTE ON test.* TO mysqltest_u1@localhost; +CREATE DEFINER=mysqltest_u1@localhost FUNCTION f_evil () RETURNS INT +SQL SECURITY INVOKER +BEGIN +SET @a:= CURRENT_USER(); +SET @b:= (SELECT COUNT(*) FROM t1); +RETURN @b; +END| +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT f_evil(); +SELECT COUNT(*) FROM t1; +ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 't1' +SELECT f_evil(); +ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 't1' +SELECT @a, @b; +@a @b +mysqltest_u1@localhost NULL +SELECT f_suid(f_evil()); +ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 't1' +SELECT @a, @b; +@a @b +mysqltest_u1@localhost NULL +CALL p_suid(f_evil()); +ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 't1' +SELECT @a, @b; +@a @b +mysqltest_u1@localhost NULL +SELECT * FROM v1; +ERROR 42000: SELECT command denied to user 'mysqltest_u1'@'localhost' for table 'v1' +SELECT @a, @b; +@a @b +mysqltest_u1@localhost NULL +DROP VIEW v1; +DROP FUNCTION f_evil; +DROP USER mysqltest_u1@localhost; +DROP PROCEDURE p_suid; +DROP FUNCTION f_suid; +DROP TABLE t1; +End of 5.0 tests. diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 7807b7b52ce..38594419e88 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -5057,6 +5057,171 @@ concat('data was: /', var1, '/') data was: /1/ drop table t3| drop procedure bug15217| +DROP PROCEDURE IF EXISTS bug21013 | +CREATE PROCEDURE bug21013(IN lim INT) +BEGIN +DECLARE i INT DEFAULT 0; +WHILE (i < lim) DO +SET @b = LOCATE(_latin1'b', @a, 1); +SET i = i + 1; +END WHILE; +END | +SET @a = _latin2"aaaaaaaaaa" | +CALL bug21013(10) | +DROP PROCEDURE bug21013 | +DROP DATABASE IF EXISTS mysqltest1| +DROP DATABASE IF EXISTS mysqltest2| +CREATE DATABASE mysqltest1 DEFAULT CHARACTER SET utf8| +CREATE DATABASE mysqltest2 DEFAULT CHARACTER SET utf8| +use mysqltest1| +CREATE FUNCTION bug16211_f1() RETURNS CHAR(10) +RETURN ""| +CREATE FUNCTION bug16211_f2() RETURNS CHAR(10) CHARSET koi8r +RETURN ""| +CREATE FUNCTION mysqltest2.bug16211_f3() RETURNS CHAR(10) +RETURN ""| +CREATE FUNCTION mysqltest2.bug16211_f4() RETURNS CHAR(10) CHARSET koi8r +RETURN ""| +SHOW CREATE FUNCTION bug16211_f1| +Function sql_mode Create Function +bug16211_f1 CREATE DEFINER=`root`@`localhost` FUNCTION `bug16211_f1`() RETURNS char(10) CHARSET utf8 +RETURN "" +SHOW CREATE FUNCTION bug16211_f2| +Function sql_mode Create Function +bug16211_f2 CREATE DEFINER=`root`@`localhost` FUNCTION `bug16211_f2`() RETURNS char(10) CHARSET koi8r +RETURN "" +SHOW CREATE FUNCTION mysqltest2.bug16211_f3| +Function sql_mode Create Function +bug16211_f3 CREATE DEFINER=`root`@`localhost` FUNCTION `bug16211_f3`() RETURNS char(10) CHARSET utf8 +RETURN "" +SHOW CREATE FUNCTION mysqltest2.bug16211_f4| +Function sql_mode Create Function +bug16211_f4 CREATE DEFINER=`root`@`localhost` FUNCTION `bug16211_f4`() RETURNS char(10) CHARSET koi8r +RETURN "" +SELECT dtd_identifier +FROM INFORMATION_SCHEMA.ROUTINES +WHERE ROUTINE_SCHEMA = "mysqltest1" AND ROUTINE_NAME = "bug16211_f1"| +dtd_identifier +char(10) CHARSET utf8 +SELECT dtd_identifier +FROM INFORMATION_SCHEMA.ROUTINES +WHERE ROUTINE_SCHEMA = "mysqltest1" AND ROUTINE_NAME = "bug16211_f2"| +dtd_identifier +char(10) CHARSET koi8r +SELECT dtd_identifier +FROM INFORMATION_SCHEMA.ROUTINES +WHERE ROUTINE_SCHEMA = "mysqltest2" AND ROUTINE_NAME = "bug16211_f3"| +dtd_identifier +char(10) CHARSET utf8 +SELECT dtd_identifier +FROM INFORMATION_SCHEMA.ROUTINES +WHERE ROUTINE_SCHEMA = "mysqltest2" AND ROUTINE_NAME = "bug16211_f4"| +dtd_identifier +char(10) CHARSET koi8r +SELECT CHARSET(bug16211_f1())| +CHARSET(bug16211_f1()) +utf8 +SELECT CHARSET(bug16211_f2())| +CHARSET(bug16211_f2()) +koi8r +SELECT CHARSET(mysqltest2.bug16211_f3())| +CHARSET(mysqltest2.bug16211_f3()) +utf8 +SELECT CHARSET(mysqltest2.bug16211_f4())| +CHARSET(mysqltest2.bug16211_f4()) +koi8r +ALTER DATABASE mysqltest1 CHARACTER SET cp1251| +ALTER DATABASE mysqltest2 CHARACTER SET cp1251| +SHOW CREATE FUNCTION bug16211_f1| +Function sql_mode Create Function +bug16211_f1 CREATE DEFINER=`root`@`localhost` FUNCTION `bug16211_f1`() RETURNS char(10) CHARSET utf8 +RETURN "" +SHOW CREATE FUNCTION bug16211_f2| +Function sql_mode Create Function +bug16211_f2 CREATE DEFINER=`root`@`localhost` FUNCTION `bug16211_f2`() RETURNS char(10) CHARSET koi8r +RETURN "" +SHOW CREATE FUNCTION mysqltest2.bug16211_f3| +Function sql_mode Create Function +bug16211_f3 CREATE DEFINER=`root`@`localhost` FUNCTION `bug16211_f3`() RETURNS char(10) CHARSET utf8 +RETURN "" +SHOW CREATE FUNCTION mysqltest2.bug16211_f4| +Function sql_mode Create Function +bug16211_f4 CREATE DEFINER=`root`@`localhost` FUNCTION `bug16211_f4`() RETURNS char(10) CHARSET koi8r +RETURN "" +SELECT dtd_identifier +FROM INFORMATION_SCHEMA.ROUTINES +WHERE ROUTINE_SCHEMA = "mysqltest1" AND ROUTINE_NAME = "bug16211_f1"| +dtd_identifier +char(10) CHARSET utf8 +SELECT dtd_identifier +FROM INFORMATION_SCHEMA.ROUTINES +WHERE ROUTINE_SCHEMA = "mysqltest1" AND ROUTINE_NAME = "bug16211_f2"| +dtd_identifier +char(10) CHARSET koi8r +SELECT dtd_identifier +FROM INFORMATION_SCHEMA.ROUTINES +WHERE ROUTINE_SCHEMA = "mysqltest2" AND ROUTINE_NAME = "bug16211_f3"| +dtd_identifier +char(10) CHARSET utf8 +SELECT dtd_identifier +FROM INFORMATION_SCHEMA.ROUTINES +WHERE ROUTINE_SCHEMA = "mysqltest2" AND ROUTINE_NAME = "bug16211_f4"| +dtd_identifier +char(10) CHARSET koi8r +SELECT CHARSET(bug16211_f1())| +CHARSET(bug16211_f1()) +utf8 +SELECT CHARSET(bug16211_f2())| +CHARSET(bug16211_f2()) +koi8r +SELECT CHARSET(mysqltest2.bug16211_f3())| +CHARSET(mysqltest2.bug16211_f3()) +utf8 +SELECT CHARSET(mysqltest2.bug16211_f4())| +CHARSET(mysqltest2.bug16211_f4()) +koi8r +use test| +DROP DATABASE mysqltest1| +DROP DATABASE mysqltest2| +DROP DATABASE IF EXISTS mysqltest1| +CREATE DATABASE mysqltest1 DEFAULT CHARACTER SET utf8| +use mysqltest1| +CREATE PROCEDURE bug16676_p1( +IN p1 CHAR(10), +INOUT p2 CHAR(10), +OUT p3 CHAR(10)) +BEGIN +SELECT CHARSET(p1), COLLATION(p1); +SELECT CHARSET(p2), COLLATION(p2); +SELECT CHARSET(p3), COLLATION(p3); +END| +CREATE PROCEDURE bug16676_p2( +IN p1 CHAR(10) CHARSET koi8r, +INOUT p2 CHAR(10) CHARSET cp1251, +OUT p3 CHAR(10) CHARSET greek) +BEGIN +SELECT CHARSET(p1), COLLATION(p1); +SELECT CHARSET(p2), COLLATION(p2); +SELECT CHARSET(p3), COLLATION(p3); +END| +SET @v2 = 'b'| +SET @v3 = 'c'| +CALL bug16676_p1('a', @v2, @v3)| +CHARSET(p1) COLLATION(p1) +utf8 utf8_general_ci +CHARSET(p2) COLLATION(p2) +utf8 utf8_general_ci +CHARSET(p3) COLLATION(p3) +utf8 utf8_general_ci +CALL bug16676_p2('a', @v2, @v3)| +CHARSET(p1) COLLATION(p1) +koi8r koi8r_general_ci +CHARSET(p2) COLLATION(p2) +cp1251 cp1251_general_ci +CHARSET(p3) COLLATION(p3) +greek greek_general_ci +use test| +DROP DATABASE mysqltest1| drop table if exists t3| drop database if exists mysqltest1| create table t3 (a int)| diff --git a/mysql-test/r/udf.result b/mysql-test/r/udf.result index 484c42c41bf..d71e7ef2ce7 100644 --- a/mysql-test/r/udf.result +++ b/mysql-test/r/udf.result @@ -93,6 +93,12 @@ NULL 0R FR DROP TABLE bug19904; +CREATE DEFINER=CURRENT_USER() FUNCTION should_not_parse +RETURNS STRING SONAME "should_not_parse.so"; +ERROR HY000: Incorrect usage of SONAME and DEFINER +CREATE DEFINER=someone@somewhere FUNCTION should_not_parse +RETURNS STRING SONAME "should_not_parse.so"; +ERROR HY000: Incorrect usage of SONAME and DEFINER End of 5.0 tests. DROP FUNCTION metaphon; DROP FUNCTION myfunc_double; diff --git a/mysql-test/t/im_daemon_life_cycle.imtest b/mysql-test/t/im_daemon_life_cycle.imtest index 3afc36935f8..fe2345a9987 100644 --- a/mysql-test/t/im_daemon_life_cycle.imtest +++ b/mysql-test/t/im_daemon_life_cycle.imtest @@ -7,21 +7,7 @@ ########################################################################### --source include/im_check_os.inc - -########################################################################### - -# Wait for mysqld1 (guarded instance) to start. - ---exec $MYSQL_TEST_DIR/t/wait_for_process.sh $IM_MYSQLD1_PATH_PID 30 started - -# Let IM detect that mysqld1 is online. This delay should be longer than -# monitoring interval. - ---sleep 3 - -# Check that start conditions are as expected. - -SHOW INSTANCES; +--source include/im_check_env.inc ########################################################################### diff --git a/mysql-test/t/im_life_cycle.imtest b/mysql-test/t/im_life_cycle.imtest index 2cbe53a7b28..35258396415 100644 --- a/mysql-test/t/im_life_cycle.imtest +++ b/mysql-test/t/im_life_cycle.imtest @@ -7,33 +7,7 @@ ########################################################################### --source include/im_check_os.inc - -########################################################################### -# -# 1.1.1. Check that Instance Manager is able: -# - to read definitions of two mysqld-instances; -# - to start the first instance; -# - to understand 'nonguarded' option and keep the second instance down; -# -########################################################################### - ---echo ---echo -------------------------------------------------------------------- ---echo -- 1.1.1. ---echo -------------------------------------------------------------------- - -# Wait for mysqld1 (guarded instance) to start. - ---exec $MYSQL_TEST_DIR/t/wait_for_process.sh $IM_MYSQLD1_PATH_PID 30 started - -# Let IM detect that mysqld1 is online. This delay should be longer than -# monitoring interval. - ---sleep 3 - -# Check that start conditions are as expected. - -SHOW INSTANCES; +--source include/im_check_env.inc ########################################################################### # @@ -54,9 +28,10 @@ START INSTANCE mysqld2; # FIXME: START INSTANCE should be synchronous. --exec $MYSQL_TEST_DIR/t/wait_for_process.sh $IM_MYSQLD2_PATH_PID 30 started -# FIXME: SHOW INSTANCES is not deterministic unless START INSTANCE is -# synchronous. Even waiting for mysqld to start by looking at its pid file is -# not enough, because IM may not detect that mysqld has started. +# FIXME: Result of SHOW INSTANCES here is not deterministic unless START +# INSTANCE is synchronous. Even waiting for mysqld to start by looking at +# its pid file is not enough, because it is unknown when IM detects that +# mysqld has started. # SHOW INSTANCES; --connect (mysql_con,localhost,root,,mysql,$IM_MYSQLD2_PORT,$IM_MYSQLD2_SOCK) @@ -86,9 +61,10 @@ STOP INSTANCE mysqld2; # FIXME: STOP INSTANCE should be synchronous. --exec $MYSQL_TEST_DIR/t/wait_for_process.sh $IM_MYSQLD2_PATH_PID 30 stopped -# FIXME: SHOW INSTANCES is not deterministic unless START INSTANCE is -# synchronous. Even waiting for mysqld to start by looking at its pid file is -# not enough, because IM may not detect that mysqld has started. +# FIXME: Result of SHOW INSTANCES here is not deterministic unless START +# INSTANCE is synchronous. Even waiting for mysqld to start by looking at +# its pid file is not enough, because it is unknown when IM detects that +# mysqld has started. # SHOW INSTANCES; ########################################################################### @@ -114,8 +90,8 @@ START INSTANCE mysqld1; ########################################################################### # -# 1.1.5. Check that Instance Manager reports correct errors for 'STOP INSTANCE' -# command: +# 1.1.5. Check that Instance Manager reports correct errors for +# 'STOP INSTANCE' command: # - if the client tries to start unregistered instance; # - if the client tries to start already stopped instance; # - if the client submits invalid arguments; @@ -146,12 +122,10 @@ STOP INSTANCE mysqld3; --echo -- 1.1.6. --echo -------------------------------------------------------------------- -SHOW INSTANCES; - --exec $MYSQL_TEST_DIR/t/kill_n_check.sh $IM_MYSQLD1_PATH_PID restarted 30 -# Give some time to IM to detect that mysqld was restarted. It should be longer -# than monitoring interval. +# Give some time to IM to detect that mysqld was restarted. It should be +# longer than monitoring interval. --sleep 3 @@ -172,16 +146,18 @@ START INSTANCE mysqld2; # FIXME: START INSTANCE should be synchronous. --exec $MYSQL_TEST_DIR/t/wait_for_process.sh $IM_MYSQLD2_PATH_PID 30 started -# FIXME: SHOW INSTANCES is not deterministic unless START INSTANCE is -# synchronous. Even waiting for mysqld to start by looking at its pid file is -# not enough, because IM may not detect that mysqld has started. +# FIXME: Result of SHOW INSTANCES here is not deterministic unless START +# INSTANCE is synchronous. Even waiting for mysqld to start by looking at +# its pid file is not enough, because it is unknown when IM detects that +# mysqld has started. # SHOW INSTANCES; --exec $MYSQL_TEST_DIR/t/kill_n_check.sh $IM_MYSQLD2_PATH_PID killed 10 -# FIXME: SHOW INSTANCES is not deterministic unless START INSTANCE is -# synchronous. Even waiting for mysqld to start by looking at its pid file is -# not enough, because IM may not detect that mysqld has started. +# FIXME: Result of SHOW INSTANCES here is not deterministic unless START +# INSTANCE is synchronous. Even waiting for mysqld to start by looking at +# its pid file is not enough, because it is unknown when IM detects that +# mysqld has started. # SHOW INSTANCES; ########################################################################### diff --git a/mysql-test/t/im_options_set.imtest b/mysql-test/t/im_options_set.imtest index a9b64861f99..76e209b6a66 100644 --- a/mysql-test/t/im_options_set.imtest +++ b/mysql-test/t/im_options_set.imtest @@ -39,32 +39,7 @@ ########################################################################### --source include/im_check_os.inc - -########################################################################### -# -# 0. Check starting conditions. -# -########################################################################### - -# - check the configuration file; - ---exec grep server_id $MYSQLTEST_VARDIR/im.cnf ; - -# - check the running instances. - ---connect (mysql1_con,localhost,root,,mysql,$IM_MYSQLD1_PORT,$IM_MYSQLD1_SOCK) - ---connection mysql1_con - -SHOW VARIABLES LIKE 'server_id'; - ---connection default - -# - check the internal cache. -# TODO: we should check only server_id option here. - -# SHOW INSTANCE OPTIONS mysqld1; -# SHOW INSTANCE OPTIONS mysqld2; +--source include/im_check_env.inc ########################################################################### # diff --git a/mysql-test/t/im_options_unset.imtest b/mysql-test/t/im_options_unset.imtest index 40629805d45..06f59e79ffe 100644 --- a/mysql-test/t/im_options_unset.imtest +++ b/mysql-test/t/im_options_unset.imtest @@ -46,32 +46,7 @@ ########################################################################### --source include/im_check_os.inc - -########################################################################### -# -# 0. Check starting conditions. -# -########################################################################### - -# - check the configuration file; - ---exec grep server_id $MYSQLTEST_VARDIR/im.cnf ; - -# - check the running instances. - ---connect (mysql1_con,localhost,root,,mysql,$IM_MYSQLD1_PORT,$IM_MYSQLD1_SOCK) - ---connection mysql1_con - -SHOW VARIABLES LIKE 'server_id'; - ---connection default - -# - check the internal cache. -# TODO: we should check only server_id option here. - -# SHOW INSTANCE OPTIONS mysqld1; -# SHOW INSTANCE OPTIONS mysqld2; +--source include/im_check_env.inc ########################################################################### # diff --git a/mysql-test/t/im_utils.imtest b/mysql-test/t/im_utils.imtest index 47902eeba52..4c05b342af5 100644 --- a/mysql-test/t/im_utils.imtest +++ b/mysql-test/t/im_utils.imtest @@ -7,36 +7,17 @@ ########################################################################### --source include/im_check_os.inc +--source include/im_check_env.inc ########################################################################### # -# Check starting conditions. This test case assumes that: -# - two mysqld-instances are registered; -# - the first instance is online; -# - the second instance is offline; +# Check 'SHOW INSTANCE OPTIONS' command. # - -# Wait for mysqld1 (guarded instance) to start. - ---exec $MYSQL_TEST_DIR/t/wait_for_process.sh $IM_MYSQLD1_PATH_PID 30 started - -# Let IM detect that mysqld1 is online. This delay should be longer than -# monitoring interval. - ---sleep 3 - -# Check that start conditions are as expected. - -SHOW INSTANCES; - -# -# Check 'SHOW INSTANCE OPTIONS' command: -# - check that options of both offline and online instances are accessible; -# - since configuration of an mysqld-instance contains directories, we should -# completely ignore the second column (values) in order to make the test -# case produce the same results on different installations; -# TODO: ignore values of only directory-specific options. +# Since configuration of an mysqld-instance contains directories, we should +# completely ignore the second column (values) in order to make the test +# case produce the same results on different installations; +# TODO: ignore values of only directory-specific options. # --replace_column 2 VALUE diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index 8eb383de57a..d0f31087c8f 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -491,6 +491,7 @@ deallocate prepare stmt; drop table t1, t2; # +# # Bug#19399 "Stored Procedures 'Lost Connection' when dropping/creating # tables" # Check that multi-delete tables are also cleaned up before re-execution. @@ -538,86 +539,6 @@ SELECT FOUND_ROWS(); deallocate prepare stmt; # -# Bug#8115: equality propagation and prepared statements -# - -create table t1 (a char(3) not null, b char(3) not null, - c char(3) not null, primary key (a, b, c)); -create table t2 like t1; - -# reduced query -prepare stmt from - "select t1.a from (t1 left outer join t2 on t2.a=1 and t1.b=t2.b) - where t1.a=1"; -execute stmt; -execute stmt; -execute stmt; - -# original query -prepare stmt from -"select t1.a, t1.b, t1.c, t2.a, t2.b, t2.c from -(t1 left outer join t2 on t2.a=? and t1.b=t2.b) -left outer join t2 t3 on t3.a=? where t1.a=?"; - -set @a:=1, @b:=1, @c:=1; - -execute stmt using @a, @b, @c; -execute stmt using @a, @b, @c; -execute stmt using @a, @b, @c; - -deallocate prepare stmt; - -drop table t1,t2; - - -# -# Bug#9383: INFORMATION_SCHEMA.COLUMNS, JOIN, Crash, prepared statement -# - -eval SET @aux= "SELECT COUNT(*) - FROM INFORMATION_SCHEMA.COLUMNS A, - INFORMATION_SCHEMA.COLUMNS B - WHERE A.TABLE_SCHEMA = B.TABLE_SCHEMA - AND A.TABLE_NAME = B.TABLE_NAME - AND A.COLUMN_NAME = B.COLUMN_NAME AND - A.TABLE_NAME = 'user'"; - -let $exec_loop_count= 3; -eval prepare my_stmt from @aux; -while ($exec_loop_count) -{ - eval execute my_stmt; - dec $exec_loop_count; -} -deallocate prepare my_stmt; - -# Test CALL in prepared mode -delimiter |; ---disable_warnings -drop procedure if exists p1| -drop table if exists t1| ---enable_warnings -create table t1 (id int)| -insert into t1 values(1)| -create procedure p1(a int, b int) -begin - declare c int; - select max(id)+1 into c from t1; - insert into t1 select a+b; - insert into t1 select a-b; - insert into t1 select a-c; -end| -set @a= 3, @b= 4| -prepare stmt from "call p1(?, ?)"| -execute stmt using @a, @b| -execute stmt using @a, @b| -select * from t1| -deallocate prepare stmt| -drop procedure p1| -drop table t1| -delimiter ;| - -# # Bug#9096 "select doesn't return all matched records if prepared statements # is used" # The bug was is bad co-operation of the optimizer's algorithm which determines @@ -692,35 +613,6 @@ deallocate prepare stmt; drop table t1, t2; # -# Bug#7306 LIMIT ?, ? and also WL#1785 " Prepared statements: implement -# support for placeholders in LIMIT clause." -# Add basic test coverage for the feature. -# -create table t1 (a int); -insert into t1 (a) values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10); -prepare stmt from "select * from t1 limit ?, ?"; -set @offset=0, @limit=1; -execute stmt using @offset, @limit; -select * from t1 limit 0, 1; -set @offset=3, @limit=2; -execute stmt using @offset, @limit; -select * from t1 limit 3, 2; -prepare stmt from "select * from t1 limit ?"; -execute stmt using @limit; ---error 1235 -prepare stmt from "select * from t1 where a in (select a from t1 limit ?)"; -prepare stmt from "select * from t1 union all select * from t1 limit ?, ?"; -set @offset=9; -set @limit=2; -execute stmt using @offset, @limit; -prepare stmt from "(select * from t1 limit ?, ?) union all - (select * from t1 limit ?, ?) order by a limit ?"; -execute stmt using @offset, @limit, @offset, @limit, @limit; - -drop table t1; -deallocate prepare stmt; - -# # Bug#11060 "Server crashes on calling stored procedure with INSERT SELECT # UNION SELECT" aka "Server crashes on re-execution of prepared INSERT ... # SELECT with UNION". @@ -837,22 +729,6 @@ select ??; select ? from t1; --enable_ps_protocol drop table t1; - -# -# Bug#12651 -# (Crash on a PS including a subquery which is a select from a simple view) -# -CREATE TABLE b12651_T1(a int) ENGINE=MYISAM; -CREATE TABLE b12651_T2(b int) ENGINE=MYISAM; -CREATE VIEW b12651_V1 as SELECT b FROM b12651_T2; - -PREPARE b12651 FROM 'SELECT 1 FROM b12651_T1 WHERE a IN (SELECT b FROM b12651_V1)'; -EXECUTE b12651; - -DROP VIEW b12651_V1; -DROP TABLE b12651_T1, b12651_T2; -DEALLOCATE PREPARE b12651; - # # Bug#9359 "Prepared statements take snapshot of system vars at PREPARE # time" @@ -1087,7 +963,172 @@ select @@max_prepared_stmt_count, @@prepared_stmt_count; set global max_prepared_stmt_count= @old_max_prepared_stmt_count; --enable_ps_protocol -# End of 4.1 tests +# +# Bug#19399 "Stored Procedures 'Lost Connection' when dropping/creating +# tables" +# Check that multi-delete tables are also cleaned up before re-execution. +# +--disable_warnings +drop table if exists t1; +create temporary table if not exists t1 (a1 int); +--enable_warnings +# exact delete syntax is essential +prepare stmt from "delete t1 from t1 where (cast(a1/3 as unsigned) * 3) = a1"; +drop temporary table t1; +create temporary table if not exists t1 (a1 int); +# the server crashed on the next statement without the fix +execute stmt; +drop temporary table t1; +create temporary table if not exists t1 (a1 int); +# the problem was in memory corruption: repeat the test just in case +execute stmt; +drop temporary table t1; +create temporary table if not exists t1 (a1 int); +execute stmt; +drop temporary table t1; +deallocate prepare stmt; + +--echo End of 4.1 tests +############################# 5.0 tests start ################################ +# +# +# Bug#6102 "Server crash with prepared statement and blank after +# function name" +# ensure that stored functions are cached when preparing a statement +# before we open tables +# +create table t1 (a varchar(20)); +insert into t1 values ('foo'); +--error 1305 +prepare stmt FROM 'SELECT char_length (a) FROM t1'; +drop table t1; + +# +# Bug#8115: equality propagation and prepared statements +# + +create table t1 (a char(3) not null, b char(3) not null, + c char(3) not null, primary key (a, b, c)); +create table t2 like t1; + +# reduced query +prepare stmt from + "select t1.a from (t1 left outer join t2 on t2.a=1 and t1.b=t2.b) + where t1.a=1"; +execute stmt; +execute stmt; +execute stmt; + +# original query +prepare stmt from +"select t1.a, t1.b, t1.c, t2.a, t2.b, t2.c from +(t1 left outer join t2 on t2.a=? and t1.b=t2.b) +left outer join t2 t3 on t3.a=? where t1.a=?"; + +set @a:=1, @b:=1, @c:=1; + +execute stmt using @a, @b, @c; +execute stmt using @a, @b, @c; +execute stmt using @a, @b, @c; + +deallocate prepare stmt; + +drop table t1,t2; + + +# +# Bug#9383: INFORMATION_SCHEMA.COLUMNS, JOIN, Crash, prepared statement +# + +eval SET @aux= "SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS A, + INFORMATION_SCHEMA.COLUMNS B + WHERE A.TABLE_SCHEMA = B.TABLE_SCHEMA + AND A.TABLE_NAME = B.TABLE_NAME + AND A.COLUMN_NAME = B.COLUMN_NAME AND + A.TABLE_NAME = 'user'"; + +let $exec_loop_count= 3; +eval prepare my_stmt from @aux; +while ($exec_loop_count) +{ + eval execute my_stmt; + dec $exec_loop_count; +} +deallocate prepare my_stmt; + +# Test CALL in prepared mode +delimiter |; +--disable_warnings +drop procedure if exists p1| +drop table if exists t1| +--enable_warnings +create table t1 (id int)| +insert into t1 values(1)| +create procedure p1(a int, b int) +begin + declare c int; + select max(id)+1 into c from t1; + insert into t1 select a+b; + insert into t1 select a-b; + insert into t1 select a-c; +end| +set @a= 3, @b= 4| +prepare stmt from "call p1(?, ?)"| +execute stmt using @a, @b| +execute stmt using @a, @b| +select * from t1| +deallocate prepare stmt| +drop procedure p1| +drop table t1| +delimiter ;| + + +# +# Bug#7306 LIMIT ?, ? and also WL#1785 " Prepared statements: implement +# support for placeholders in LIMIT clause." +# Add basic test coverage for the feature. +# +create table t1 (a int); +insert into t1 (a) values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10); +prepare stmt from "select * from t1 limit ?, ?"; +set @offset=0, @limit=1; +execute stmt using @offset, @limit; +select * from t1 limit 0, 1; +set @offset=3, @limit=2; +execute stmt using @offset, @limit; +select * from t1 limit 3, 2; +prepare stmt from "select * from t1 limit ?"; +execute stmt using @limit; +--error 1235 +prepare stmt from "select * from t1 where a in (select a from t1 limit ?)"; +prepare stmt from "select * from t1 union all select * from t1 limit ?, ?"; +set @offset=9; +set @limit=2; +execute stmt using @offset, @limit; +prepare stmt from "(select * from t1 limit ?, ?) union all + (select * from t1 limit ?, ?) order by a limit ?"; +execute stmt using @offset, @limit, @offset, @limit, @limit; + +drop table t1; +deallocate prepare stmt; + +# +# Bug#12651 +# (Crash on a PS including a subquery which is a select from a simple view) +# +CREATE TABLE b12651_T1(a int) ENGINE=MYISAM; +CREATE TABLE b12651_T2(b int) ENGINE=MYISAM; +CREATE VIEW b12651_V1 as SELECT b FROM b12651_T2; + +PREPARE b12651 FROM 'SELECT 1 FROM b12651_T1 WHERE a IN (SELECT b FROM b12651_V1)'; +EXECUTE b12651; + +DROP VIEW b12651_V1; +DROP TABLE b12651_T1, b12651_T2; +DEALLOCATE PREPARE b12651; + + # # Bug #14956: ROW_COUNT() returns incorrect result after EXECUTE of prepared diff --git a/mysql-test/t/rpl_sp.test b/mysql-test/t/rpl_sp.test index 8be17be3822..7479794eded 100644 --- a/mysql-test/t/rpl_sp.test +++ b/mysql-test/t/rpl_sp.test @@ -435,6 +435,86 @@ connection master; DROP PROCEDURE p1; + +# +# BUG#20438: CREATE statements for views, stored routines and triggers can be +# not replicable. +# + +--echo +--echo ---> Test for BUG#20438 + +# Prepare environment. + +--echo +--echo ---> Preparing environment... +--echo ---> connection: master +--connection master + +--disable_warnings +DROP PROCEDURE IF EXISTS p1; +DROP FUNCTION IF EXISTS f1; +--enable_warnings + +--echo +--echo ---> Synchronizing slave with master... + +--save_master_pos +--connection slave +--sync_with_master + +--echo +--echo ---> connection: master +--connection master + +# Test. + +--echo +--echo ---> Creating procedure... + +/*!50003 CREATE PROCEDURE p1() SET @a = 1 */; + +/*!50003 CREATE FUNCTION f1() RETURNS INT RETURN 0 */; + +--echo +--echo ---> Checking on master... + +SHOW CREATE PROCEDURE p1; +SHOW CREATE FUNCTION f1; + +--echo +--echo ---> Synchronizing slave with master... + +--save_master_pos +--connection slave +--sync_with_master + +--echo ---> connection: master + +--echo +--echo ---> Checking on slave... + +SHOW CREATE PROCEDURE p1; +SHOW CREATE FUNCTION f1; + +# Cleanup. + +--echo +--echo ---> connection: master +--connection master + +--echo +--echo ---> Cleaning up... + +DROP PROCEDURE p1; +DROP FUNCTION f1; + +--save_master_pos +--connection slave +--sync_with_master +--connection master + + # cleanup connection master; drop table t1; diff --git a/mysql-test/t/rpl_trigger.test b/mysql-test/t/rpl_trigger.test index 35f0a0b0a4b..3c8cbb97b31 100644 --- a/mysql-test/t/rpl_trigger.test +++ b/mysql-test/t/rpl_trigger.test @@ -331,6 +331,98 @@ SHOW TRIGGERS; RESET MASTER; +# Restart slave. + +connection slave; +START SLAVE; + + +# +# BUG#20438: CREATE statements for views, stored routines and triggers can be +# not replicable. +# + +--echo +--echo ---> Test for BUG#20438 + +# Prepare environment. + +--echo +--echo ---> Preparing environment... +--echo ---> connection: master +--connection master + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t2; +--enable_warnings + +--echo +--echo ---> Synchronizing slave with master... + +--save_master_pos +--connection slave +--sync_with_master + +--echo +--echo ---> connection: master +--connection master + +# Test. + +--echo +--echo ---> Creating objects... + +CREATE TABLE t1(c INT); +CREATE TABLE t2(c INT); + +/*!50003 CREATE TRIGGER t1_bi BEFORE INSERT ON t1 + FOR EACH ROW + INSERT INTO t2 VALUES(NEW.c * 10) */; + +--echo +--echo ---> Inserting value... + +INSERT INTO t1 VALUES(1); + +--echo +--echo ---> Checking on master... + +SELECT * FROM t1; +SELECT * FROM t2; + +--echo +--echo ---> Synchronizing slave with master... + +--save_master_pos +--connection slave +--sync_with_master + +--echo ---> connection: master + +--echo +--echo ---> Checking on slave... + +SELECT * FROM t1; +SELECT * FROM t2; + +# Cleanup. + +--echo +--echo ---> connection: master +--connection master + +--echo +--echo ---> Cleaning up... + +DROP TABLE t1; +DROP TABLE t2; + +--save_master_pos +--connection slave +--sync_with_master +--connection master + # # End of tests diff --git a/mysql-test/t/rpl_view.test b/mysql-test/t/rpl_view.test index 0a0c6a6dddb..d0990b4fbee 100644 --- a/mysql-test/t/rpl_view.test +++ b/mysql-test/t/rpl_view.test @@ -45,3 +45,87 @@ drop table t1; sync_slave_with_master; --replace_column 2 # 5 # show binlog events limit 1,100; + + + +# +# BUG#20438: CREATE statements for views, stored routines and triggers can be +# not replicable. +# + +--echo +--echo ---> Test for BUG#20438 + +# Prepare environment. + +--echo +--echo ---> Preparing environment... +--echo ---> connection: master +--connection master + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP VIEW IF EXISTS v1; +--enable_warnings + +--echo +--echo ---> Synchronizing slave with master... + +--save_master_pos +--connection slave +--sync_with_master + +--echo +--echo ---> connection: master +--connection master + +# Test. + +--echo +--echo ---> Creating objects... + +CREATE TABLE t1(c INT); + +/*!50003 CREATE VIEW v1 AS SELECT * FROM t1 */; + +--echo +--echo ---> Inserting value... + +INSERT INTO t1 VALUES(1); + +--echo +--echo ---> Checking on master... + +SELECT * FROM t1; + +--echo +--echo ---> Synchronizing slave with master... + +--save_master_pos +--connection slave +--sync_with_master + +--echo ---> connection: master + +--echo +--echo ---> Checking on slave... + +SELECT * FROM t1; + +# Cleanup. + +--echo +--echo ---> connection: master +--connection master + +--echo +--echo ---> Cleaning up... + +DROP VIEW v1; +DROP TABLE t1; + +--save_master_pos +--connection slave +--sync_with_master +--connection master + diff --git a/mysql-test/t/show_check.test b/mysql-test/t/show_check.test index 6937cbe949d..65a81545c87 100644 --- a/mysql-test/t/show_check.test +++ b/mysql-test/t/show_check.test @@ -495,4 +495,15 @@ SHOW CREATE VIEW v1; DROP PROCEDURE p1; DROP VIEW v1; + +# +# Check that SHOW TABLES and SHOW COLUMNS give a error if there is no +# referenced database and table respectively. +# +--error ER_BAD_DB_ERROR +SHOW TABLES FROM no_such_database; +--error ER_NO_SUCH_TABLE +SHOW COLUMNS FROM no_such_table; + + # End of 5.0 tests. diff --git a/mysql-test/t/sp-code.test b/mysql-test/t/sp-code.test index 0a26ea644f6..72efa831059 100644 --- a/mysql-test/t/sp-code.test +++ b/mysql-test/t/sp-code.test @@ -190,3 +190,25 @@ delimiter ;// show procedure code sudoku_solve; drop procedure sudoku_solve; + + +# +# Bug#19207: Final parenthesis omitted for CREATE INDEX in Stored +# Procedure +# +# Wrong criteria was used to distinguish the case when there was no +# lookahead performed in the parser. Bug affected only statements +# ending in one-character token without any optional tail, like CREATE +# INDEX and CALL. +# +--disable_warnings +DROP PROCEDURE IF EXISTS p1; +--enable_warnings + +CREATE PROCEDURE p1() CREATE INDEX idx ON t1 (c1); +SHOW PROCEDURE CODE p1; + +DROP PROCEDURE p1; + + +--echo End of 5.0 tests. diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test index a4ab5d98922..2abb923efbb 100644 --- a/mysql-test/t/sp-error.test +++ b/mysql-test/t/sp-error.test @@ -1707,6 +1707,28 @@ create aggregate function bug16896() returns int return 1; # +# BUG#14702: misleading error message when syntax error in CREATE +# PROCEDURE +# +# Misleading error message was given when IF NOT EXISTS was used in +# CREATE PROCEDURE. +# +--disable_warnings +DROP PROCEDURE IF EXISTS bug14702; +--enable_warnings + +--error ER_PARSE_ERROR +CREATE IF NOT EXISTS PROCEDURE bug14702() +BEGIN +END; + +--error ER_PARSE_ERROR +CREATE PROCEDURE IF NOT EXISTS bug14702() +BEGIN +END; + + +# # BUG#NNNN: New bug synopsis # #--disable_warnings diff --git a/mysql-test/t/sp-security.test b/mysql-test/t/sp-security.test index d323b180216..a5d509f29b7 100644 --- a/mysql-test/t/sp-security.test +++ b/mysql-test/t/sp-security.test @@ -790,4 +790,80 @@ SELECT Host,User,Password FROM mysql.user WHERE User='user19857'; DROP USER user19857@localhost; -# End of 5.0 bugs. +--disconnect con1root +--connection default + + +# +# BUG#18630: Arguments of suid routine calculated in wrong security +# context +# +# Arguments of suid routines were calculated in definer's security +# context instead of caller's context thus creating security hole. +# +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP VIEW IF EXISTS v1; +DROP FUNCTION IF EXISTS f_suid; +DROP PROCEDURE IF EXISTS p_suid; +DROP FUNCTION IF EXISTS f_evil; +--enable_warnings +DELETE FROM mysql.user WHERE user LIKE 'mysqltest\_%'; +DELETE FROM mysql.db WHERE user LIKE 'mysqltest\_%'; +DELETE FROM mysql.tables_priv WHERE user LIKE 'mysqltest\_%'; +DELETE FROM mysql.columns_priv WHERE user LIKE 'mysqltest\_%'; +FLUSH PRIVILEGES; + +CREATE TABLE t1 (i INT); +CREATE FUNCTION f_suid(i INT) RETURNS INT SQL SECURITY DEFINER RETURN 0; +CREATE PROCEDURE p_suid(IN i INT) SQL SECURITY DEFINER SET @c:= 0; + +CREATE USER mysqltest_u1@localhost; +# Thanks to this grant statement privileges of anonymous users on +# 'test' database are not applicable for mysqltest_u1@localhost. +GRANT EXECUTE ON test.* TO mysqltest_u1@localhost; + +delimiter |; +CREATE DEFINER=mysqltest_u1@localhost FUNCTION f_evil () RETURNS INT + SQL SECURITY INVOKER +BEGIN + SET @a:= CURRENT_USER(); + SET @b:= (SELECT COUNT(*) FROM t1); + RETURN @b; +END| +delimiter ;| + +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT f_evil(); + +connect (conn1, localhost, mysqltest_u1,,); + +--error ER_TABLEACCESS_DENIED_ERROR +SELECT COUNT(*) FROM t1; + +--error ER_TABLEACCESS_DENIED_ERROR +SELECT f_evil(); +SELECT @a, @b; + +--error ER_TABLEACCESS_DENIED_ERROR +SELECT f_suid(f_evil()); +SELECT @a, @b; + +--error ER_TABLEACCESS_DENIED_ERROR +CALL p_suid(f_evil()); +SELECT @a, @b; + +--error ER_TABLEACCESS_DENIED_ERROR +SELECT * FROM v1; +SELECT @a, @b; + +disconnect conn1; +connection default; + +DROP VIEW v1; +DROP FUNCTION f_evil; +DROP USER mysqltest_u1@localhost; +DROP PROCEDURE p_suid; +DROP FUNCTION f_suid; +DROP TABLE t1; + +--echo End of 5.0 tests. diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 9a0003bab9c..5b34ad06984 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -5962,7 +5962,190 @@ call bug15217()| drop table t3| drop procedure bug15217| + +# +# BUG#21013: Performance Degrades when importing data that uses +# Trigger and Stored Procedure +# +# This is a performance and memory leak test. Run with large number +# passed to bug21013() procedure. +# +--disable_warnings +DROP PROCEDURE IF EXISTS bug21013 | +--enable_warnings + +CREATE PROCEDURE bug21013(IN lim INT) +BEGIN + DECLARE i INT DEFAULT 0; + WHILE (i < lim) DO + SET @b = LOCATE(_latin1'b', @a, 1); + SET i = i + 1; + END WHILE; +END | + +SET @a = _latin2"aaaaaaaaaa" | +CALL bug21013(10) | + +DROP PROCEDURE bug21013 | + + +# +# BUG#16211: Stored function return type for strings is ignored +# + +# Prepare: create database with fixed, pre-defined character set. + +--disable_warnings +DROP DATABASE IF EXISTS mysqltest1| +DROP DATABASE IF EXISTS mysqltest2| +--enable_warnings + +CREATE DATABASE mysqltest1 DEFAULT CHARACTER SET utf8| +CREATE DATABASE mysqltest2 DEFAULT CHARACTER SET utf8| + +# Test case: + +use mysqltest1| + +# - Create two stored functions -- with and without explicit CHARSET-clause +# for return value; + +CREATE FUNCTION bug16211_f1() RETURNS CHAR(10) + RETURN ""| + +CREATE FUNCTION bug16211_f2() RETURNS CHAR(10) CHARSET koi8r + RETURN ""| + +CREATE FUNCTION mysqltest2.bug16211_f3() RETURNS CHAR(10) + RETURN ""| + +CREATE FUNCTION mysqltest2.bug16211_f4() RETURNS CHAR(10) CHARSET koi8r + RETURN ""| + +# - Check that CHARSET-clause is specified for the second function; + +SHOW CREATE FUNCTION bug16211_f1| +SHOW CREATE FUNCTION bug16211_f2| + +SHOW CREATE FUNCTION mysqltest2.bug16211_f3| +SHOW CREATE FUNCTION mysqltest2.bug16211_f4| + +SELECT dtd_identifier +FROM INFORMATION_SCHEMA.ROUTINES +WHERE ROUTINE_SCHEMA = "mysqltest1" AND ROUTINE_NAME = "bug16211_f1"| + +SELECT dtd_identifier +FROM INFORMATION_SCHEMA.ROUTINES +WHERE ROUTINE_SCHEMA = "mysqltest1" AND ROUTINE_NAME = "bug16211_f2"| + +SELECT dtd_identifier +FROM INFORMATION_SCHEMA.ROUTINES +WHERE ROUTINE_SCHEMA = "mysqltest2" AND ROUTINE_NAME = "bug16211_f3"| + +SELECT dtd_identifier +FROM INFORMATION_SCHEMA.ROUTINES +WHERE ROUTINE_SCHEMA = "mysqltest2" AND ROUTINE_NAME = "bug16211_f4"| + +SELECT CHARSET(bug16211_f1())| +SELECT CHARSET(bug16211_f2())| + +SELECT CHARSET(mysqltest2.bug16211_f3())| +SELECT CHARSET(mysqltest2.bug16211_f4())| + +# - Alter database character set. + +ALTER DATABASE mysqltest1 CHARACTER SET cp1251| +ALTER DATABASE mysqltest2 CHARACTER SET cp1251| + +# - Check that CHARSET-clause has not changed. + +SHOW CREATE FUNCTION bug16211_f1| +SHOW CREATE FUNCTION bug16211_f2| + +SHOW CREATE FUNCTION mysqltest2.bug16211_f3| +SHOW CREATE FUNCTION mysqltest2.bug16211_f4| + +SELECT dtd_identifier +FROM INFORMATION_SCHEMA.ROUTINES +WHERE ROUTINE_SCHEMA = "mysqltest1" AND ROUTINE_NAME = "bug16211_f1"| + +SELECT dtd_identifier +FROM INFORMATION_SCHEMA.ROUTINES +WHERE ROUTINE_SCHEMA = "mysqltest1" AND ROUTINE_NAME = "bug16211_f2"| + +SELECT dtd_identifier +FROM INFORMATION_SCHEMA.ROUTINES +WHERE ROUTINE_SCHEMA = "mysqltest2" AND ROUTINE_NAME = "bug16211_f3"| + +SELECT dtd_identifier +FROM INFORMATION_SCHEMA.ROUTINES +WHERE ROUTINE_SCHEMA = "mysqltest2" AND ROUTINE_NAME = "bug16211_f4"| + +SELECT CHARSET(bug16211_f1())| +SELECT CHARSET(bug16211_f2())| + +SELECT CHARSET(mysqltest2.bug16211_f3())| +SELECT CHARSET(mysqltest2.bug16211_f4())| + +# Cleanup. + +use test| + +DROP DATABASE mysqltest1| +DROP DATABASE mysqltest2| + + # +# BUG#16676: Database CHARSET not used for stored procedures +# + +# Prepare: create database with fixed, pre-defined character set. + +--disable_warnings +DROP DATABASE IF EXISTS mysqltest1| +--enable_warnings + +CREATE DATABASE mysqltest1 DEFAULT CHARACTER SET utf8| + +# Test case: + +use mysqltest1| + +# - Create two stored procedures -- with and without explicit CHARSET-clause; + +CREATE PROCEDURE bug16676_p1( + IN p1 CHAR(10), + INOUT p2 CHAR(10), + OUT p3 CHAR(10)) +BEGIN + SELECT CHARSET(p1), COLLATION(p1); + SELECT CHARSET(p2), COLLATION(p2); + SELECT CHARSET(p3), COLLATION(p3); +END| + +CREATE PROCEDURE bug16676_p2( + IN p1 CHAR(10) CHARSET koi8r, + INOUT p2 CHAR(10) CHARSET cp1251, + OUT p3 CHAR(10) CHARSET greek) +BEGIN + SELECT CHARSET(p1), COLLATION(p1); + SELECT CHARSET(p2), COLLATION(p2); + SELECT CHARSET(p3), COLLATION(p3); +END| + +# - Call procedures. + +SET @v2 = 'b'| +SET @v3 = 'c'| + +CALL bug16676_p1('a', @v2, @v3)| +CALL bug16676_p2('a', @v2, @v3)| + +# Cleanup. + +use test| + +DROP DATABASE mysqltest1| # Bug#21002 "Derived table not selecting from a "real" table fails in JOINs" # # A regression caused by the fix for Bug#18444: for derived tables we should diff --git a/mysql-test/t/udf.test b/mysql-test/t/udf.test index e0c2493c616..80c73948744 100644 --- a/mysql-test/t/udf.test +++ b/mysql-test/t/udf.test @@ -109,6 +109,18 @@ SELECT myfunc_double(n) AS f FROM bug19904; SELECT metaphon(v) AS f FROM bug19904; DROP TABLE bug19904; +# +# Bug#21269: DEFINER-clause is allowed for UDF-functions +# + +--error ER_WRONG_USAGE +CREATE DEFINER=CURRENT_USER() FUNCTION should_not_parse +RETURNS STRING SONAME "should_not_parse.so"; + +--error ER_WRONG_USAGE +CREATE DEFINER=someone@somewhere FUNCTION should_not_parse +RETURNS STRING SONAME "should_not_parse.so"; + --echo End of 5.0 tests. # diff --git a/sql-common/client.c b/sql-common/client.c index 45cd8befdf0..6819caddf35 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -584,7 +584,7 @@ err: *****************************************************************************/ ulong -net_safe_read(MYSQL *mysql) +cli_safe_read(MYSQL *mysql) { NET *net= &mysql->net; ulong len=0; @@ -627,6 +627,16 @@ net_safe_read(MYSQL *mysql) } else set_mysql_error(mysql, CR_UNKNOWN_ERROR, unknown_sqlstate); + /* + Cover a protocol design error: error packet does not + contain the server status. Therefore, the client has no way + to find out whether there are more result sets of + a multiple-result-set statement pending. Luckily, in 5.0 an + error always aborts execution of a statement, wherever it is + a multi-statement or a stored procedure, so it should be + safe to unconditionally turn off the flag here. + */ + mysql->server_status&= ~SERVER_MORE_RESULTS_EXISTS; DBUG_PRINT("error",("Got error: %d/%s (%s)", net->last_errno, net->sqlstate, net->last_error)); @@ -653,7 +663,7 @@ cli_advanced_command(MYSQL *mysql, enum enum_server_command command, NET *net= &mysql->net; my_bool result= 1; init_sigpipe_variables - DBUG_ENTER("cli_advanced_command"); + DBUG_ENTER("cli_advanced_command"); /* Don't give sigpipe errors if the client doesn't want them */ set_sigpipe(mysql); @@ -663,7 +673,8 @@ cli_advanced_command(MYSQL *mysql, enum enum_server_command command, if (mysql_reconnect(mysql)) DBUG_RETURN(1); } - if (mysql->status != MYSQL_STATUS_READY) + if (mysql->status != MYSQL_STATUS_READY || + mysql->server_status & SERVER_MORE_RESULTS_EXISTS) { DBUG_PRINT("error",("state: %d", mysql->status)); set_mysql_error(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); @@ -702,7 +713,7 @@ cli_advanced_command(MYSQL *mysql, enum enum_server_command command, } result=0; if (!skip_check) - result= ((mysql->packet_length=net_safe_read(mysql)) == packet_error ? + result= ((mysql->packet_length=cli_safe_read(mysql)) == packet_error ? 1 : 0); end: reset_sigpipe(mysql); @@ -754,7 +765,7 @@ static void cli_flush_use_result(MYSQL *mysql) for (;;) { ulong pkt_len; - if ((pkt_len=net_safe_read(mysql)) == packet_error) + if ((pkt_len=cli_safe_read(mysql)) == packet_error) break; if (pkt_len <= 8 && mysql->net.read_pos[0] == 254) { @@ -1276,7 +1287,7 @@ MYSQL_DATA *cli_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, NET *net = &mysql->net; DBUG_ENTER("cli_read_rows"); - if ((pkt_len= net_safe_read(mysql)) == packet_error) + if ((pkt_len= cli_safe_read(mysql)) == packet_error) DBUG_RETURN(0); if (!(result=(MYSQL_DATA*) my_malloc(sizeof(MYSQL_DATA), MYF(MY_WME | MY_ZEROFILL)))) @@ -1341,7 +1352,7 @@ MYSQL_DATA *cli_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, } } cur->data[field]=to; /* End of last field */ - if ((pkt_len=net_safe_read(mysql)) == packet_error) + if ((pkt_len=cli_safe_read(mysql)) == packet_error) { free_rows(result); DBUG_RETURN(0); @@ -1373,7 +1384,7 @@ read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row, ulong *lengths) uchar *pos, *prev_pos, *end_pos; NET *net= &mysql->net; - if ((pkt_len=net_safe_read(mysql)) == packet_error) + if ((pkt_len=cli_safe_read(mysql)) == packet_error) return -1; if (pkt_len <= 8 && net->read_pos[0] == 254) { @@ -1650,23 +1661,23 @@ static MYSQL_RES *cli_use_result(MYSQL *mysql); static MYSQL_METHODS client_methods= { - cli_read_query_result, - cli_advanced_command, - cli_read_rows, - cli_use_result, - cli_fetch_lengths, - cli_flush_use_result + cli_read_query_result, /* read_query_result */ + cli_advanced_command, /* advanced_command */ + cli_read_rows, /* read_rows */ + cli_use_result, /* use_result */ + cli_fetch_lengths, /* fetch_lengths */ + cli_flush_use_result /* flush_use_result */ #ifndef MYSQL_SERVER - ,cli_list_fields, - cli_read_prepare_result, - cli_stmt_execute, - cli_read_binary_rows, - cli_unbuffered_fetch, - NULL, - cli_read_statistics, - cli_read_query_result, - cli_read_change_user_result, - cli_read_binary_rows + ,cli_list_fields, /* list_fields */ + cli_read_prepare_result, /* read_prepare_result */ + cli_stmt_execute, /* stmt_execute */ + cli_read_binary_rows, /* read_binary_rows */ + cli_unbuffered_fetch, /* unbuffered_fetch */ + NULL, /* free_embedded_thd */ + cli_read_statistics, /* read_statistics */ + cli_read_query_result, /* next_result */ + cli_read_change_user_result, /* read_change_user_result */ + cli_read_binary_rows /* read_rows_from_cursor */ #endif }; @@ -2029,7 +2040,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, Part 1: Connection established, read and parse first packet */ - if ((pkt_length=net_safe_read(mysql)) == packet_error) + if ((pkt_length=cli_safe_read(mysql)) == packet_error) goto error; /* Check if version of protocol matches current one */ @@ -2253,7 +2264,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, OK-packet, or re-request scrambled password. */ - if ((pkt_length=net_safe_read(mysql)) == packet_error) + if ((pkt_length=cli_safe_read(mysql)) == packet_error) goto error; if (pkt_length == 1 && net->read_pos[0] == 254 && @@ -2270,7 +2281,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, goto error; } /* Read what server thinks about out new auth message report */ - if (net_safe_read(mysql) == packet_error) + if (cli_safe_read(mysql) == packet_error) goto error; } @@ -2594,7 +2605,7 @@ static my_bool cli_read_query_result(MYSQL *mysql) */ mysql = mysql->last_used_con; - if ((length = net_safe_read(mysql)) == packet_error) + if ((length = cli_safe_read(mysql)) == packet_error) DBUG_RETURN(1); free_old_query(mysql); /* Free old result */ #ifdef MYSQL_CLIENT /* Avoid warn of unused labels*/ @@ -2629,7 +2640,7 @@ get_info: if (field_count == NULL_LENGTH) /* LOAD DATA LOCAL INFILE */ { int error=handle_local_infile(mysql,(char*) pos); - if ((length=net_safe_read(mysql)) == packet_error || error) + if ((length= cli_safe_read(mysql)) == packet_error || error) DBUG_RETURN(1); goto get_info; /* Get info packet */ } diff --git a/sql/item.cc b/sql/item.cc index ad8b79182d4..a6d64f7ab9d 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1424,7 +1424,8 @@ bool agg_item_charsets(DTCollation &coll, const char *fname, In case we're in statement prepare, create conversion item in its memory: it will be reused on each execute. */ - arena= thd->activate_stmt_arena_if_needed(&backup); + arena= thd->is_stmt_prepare() ? thd->activate_stmt_arena_if_needed(&backup) + : NULL; for (i= 0, arg= args; i < nargs; i++, arg+= item_sep) { @@ -1459,7 +1460,7 @@ bool agg_item_charsets(DTCollation &coll, const char *fname, been created in prepare. In this case register the change for rollback. */ - if (arena && arena->is_conventional()) + if (arena) *arg= conv; else thd->change_item_tree(arg, conv); diff --git a/sql/item_func.cc b/sql/item_func.cc index 1d906b300b6..a4c786c2351 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -4830,7 +4830,9 @@ Item_func_sp::execute_impl(THD *thd, Field *return_value_fld) { bool err_status= TRUE; Sub_statement_state statement_state; - Security_context *save_security_ctx= thd->security_ctx, *save_ctx_func; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + Security_context *save_security_ctx= thd->security_ctx; +#endif DBUG_ENTER("Item_func_sp::execute_impl"); @@ -4841,7 +4843,7 @@ Item_func_sp::execute_impl(THD *thd, Field *return_value_fld) thd->security_ctx= context->security_ctx; } #endif - if (find_and_check_access(thd, EXECUTE_ACL, &save_ctx_func)) + if (find_and_check_access(thd)) goto error; /* @@ -4853,13 +4855,11 @@ Item_func_sp::execute_impl(THD *thd, Field *return_value_fld) err_status= m_sp->execute_function(thd, args, arg_count, return_value_fld); thd->restore_sub_statement_state(&statement_state); -#ifndef NO_EMBEDDED_ACCESS_CHECKS - sp_restore_security_context(thd, save_ctx_func); error: +#ifndef NO_EMBEDDED_ACCESS_CHECKS thd->security_ctx= save_security_ctx; -#else -error: #endif + DBUG_RETURN(err_status); } @@ -4976,70 +4976,38 @@ Item_func_sp::tmp_table_field(TABLE *t_arg) SYNOPSIS find_and_check_access() thd thread handler - want_access requested access - save backup of security context RETURN FALSE Access granted TRUE Requested access can't be granted or function doesn't exists - In this case security context is not changed and *save = 0 NOTES Checks if requested access to function can be granted to user. If function isn't found yet, it searches function first. If function can't be found or user don't have requested access error is raised. - If security context sp_ctx is provided and access can be granted then - switch back to previous context isn't performed. - In case of access error or if context is not provided then - find_and_check_access() switches back to previous security context. */ bool -Item_func_sp::find_and_check_access(THD *thd, ulong want_access, - Security_context **save) +Item_func_sp::find_and_check_access(THD *thd) { - bool res= TRUE; - - *save= 0; // Safety if error if (! m_sp && ! (m_sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, m_name, &thd->sp_func_cache, TRUE))) { my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION", m_name->m_qname.str); - goto error; + return TRUE; } #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (check_routine_access(thd, want_access, - m_sp->m_db.str, m_sp->m_name.str, 0, FALSE)) - goto error; - - sp_change_security_context(thd, m_sp, save); - /* - If we changed context to run as another user, we need to check the - access right for the new context again as someone may have deleted - this person the right to use the procedure - - TODO: - Cache if the definer has the right to use the object on the first - usage and only reset the cache if someone does a GRANT statement - that 'may' affect this. - */ - if (*save && - check_routine_access(thd, want_access, + if (check_routine_access(thd, EXECUTE_ACL, m_sp->m_db.str, m_sp->m_name.str, 0, FALSE)) - { - sp_restore_security_context(thd, *save); - *save= 0; // Safety - goto error; - } + return TRUE; #endif - res= FALSE; // no error -error: - return res; + return FALSE; } + bool Item_func_sp::fix_fields(THD *thd, Item **ref) { @@ -5050,19 +5018,23 @@ Item_func_sp::fix_fields(THD *thd, Item **ref) { /* Here we check privileges of the stored routine only during view - creation, in order to validate the view. A runtime check is perfomed - in Item_func_sp::execute(), and this method is not called during - context analysis. We do not need to restore the security context - changed in find_and_check_access because all view structures created - in CREATE VIEW are not used for execution. Notice, that during view - creation we do not infer into stored routine bodies and do not check - privileges of its statements, which would probably be a good idea - especially if the view has SQL SECURITY DEFINER and the used stored - procedure has SQL SECURITY DEFINER + creation, in order to validate the view. A runtime check is + perfomed in Item_func_sp::execute(), and this method is not + called during context analysis. Notice, that during view + creation we do not infer into stored routine bodies and do not + check privileges of its statements, which would probably be a + good idea especially if the view has SQL SECURITY DEFINER and + the used stored procedure has SQL SECURITY DEFINER. */ - Security_context *save_ctx; - if (!(res= find_and_check_access(thd, EXECUTE_ACL, &save_ctx))) - sp_restore_security_context(thd, save_ctx); + res= find_and_check_access(thd); +#ifndef NO_EMBEDDED_ACCESS_CHECKS + Security_context *save_secutiry_ctx; + if (!res && !(res= set_routine_security_ctx(thd, m_sp, false, + &save_secutiry_ctx))) + { + sp_restore_security_context(thd, save_secutiry_ctx); + } +#endif /* ! NO_EMBEDDED_ACCESS_CHECKS */ } return res; } diff --git a/sql/item_func.h b/sql/item_func.h index c54fc701c53..4b43a9e5d27 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1468,8 +1468,7 @@ public: { context= (Name_resolution_context *)cntx; return FALSE; } void fix_length_and_dec(); - bool find_and_check_access(THD * thd, ulong want_access, - Security_context **backup); + bool find_and_check_access(THD * thd); virtual enum Functype functype() const { return FUNC_SP; } bool fix_fields(THD *thd, Item **ref); diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 089bc965c8c..4d89c8fd2f1 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -865,8 +865,6 @@ bool mysqld_show_create_db(THD *thd, char *dbname, HA_CREATE_INFO *create); void mysqld_list_processes(THD *thd,const char *user,bool verbose); int mysqld_show_status(THD *thd); int mysqld_show_variables(THD *thd,const char *wild); -int mysql_find_files(THD *thd,List<char> *files, const char *db, - const char *path, const char *wild, bool dir); bool mysqld_show_storage_engines(THD *thd); bool mysqld_show_privileges(THD *thd); bool mysqld_show_column_types(THD *thd); @@ -1138,7 +1136,10 @@ uint check_word(TYPELIB *lib, const char *val, const char *end, bool is_keyword(const char *name, uint len); #define MY_DB_OPT_FILE "db.opt" +bool check_db_dir_existence(const char *db_name); bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create); +bool load_db_opt_by_name(THD *thd, const char *db_name, + HA_CREATE_INFO *db_create_info); bool my_dbopt_init(void); void my_dbopt_cleanup(void); void my_dbopt_free(void); diff --git a/sql/protocol.cc b/sql/protocol.cc index 650bd8fc58f..f4efb8004ee 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -322,7 +322,7 @@ static char eof_buff[1]= { (char) 254 }; /* Marker for end of fields */ 254 Marker (1 byte) warning_count Stored in 2 bytes; New in 4.1 protocol status_flag Stored in 2 bytes; - For flags like SERVER_STATUS_MORE_RESULTS + For flags like SERVER_MORE_RESULTS_EXISTS Note that the warning count will not be sent if 'no_flush' is set as we don't want to report the warning count until all data is sent to the diff --git a/sql/slave.cc b/sql/slave.cc index 90e95e812bd..8bcaa6be102 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -3051,7 +3051,7 @@ static ulong read_event(MYSQL* mysql, MASTER_INFO *mi, bool* suppress_warnings) return packet_error; #endif - len = net_safe_read(mysql); + len = cli_safe_read(mysql); if (len == packet_error || (long) len < 1) { if (mysql_errno(mysql) == ER_NET_READ_INTERRUPTED) diff --git a/sql/sp.cc b/sql/sp.cc index a7078da2f50..b7bf049cb1d 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -495,6 +495,13 @@ sp_returns_type(THD *thd, String &result, sp_head *sp) table.s = &table.share_not_to_be_used; field= sp->create_result_field(0, 0, &table); field->sql_type(result); + + if (field->has_charset()) + { + result.append(STRING_WITH_LEN(" CHARSET ")); + result.append(field->charset()->csname); + } + delete field; } @@ -626,7 +633,10 @@ db_create_routine(THD *thd, int type, sp_head *sp) log_query.append(STRING_WITH_LEN("CREATE ")); append_definer(thd, &log_query, &thd->lex->definer->user, &thd->lex->definer->host); - log_query.append(thd->lex->stmt_definition_begin); + log_query.append(thd->lex->stmt_definition_begin, + (char *)sp->m_body_begin - + thd->lex->stmt_definition_begin + + sp->m_body.length); /* Such a statement can always go directly to binlog, no trans cache */ Query_log_event qinfo(thd, log_query.c_ptr(), log_query.length(), 0, @@ -974,6 +984,11 @@ sp_find_routine(THD *thd, int type, sp_name *name, sp_cache **cp, sp_head *new_sp; const char *returns= ""; char definer[USER_HOST_BUFF_SIZE]; + + /* + String buffer for RETURNS data type must have system charset; + 64 -- size of "returns" column of mysql.proc. + */ String retstr(64); DBUG_PRINT("info", ("found: 0x%lx", (ulong)sp)); diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 9965c48935a..eec6e0fc3cd 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -470,7 +470,7 @@ sp_head::init(LEX *lex) lex->trg_table_fields.empty(); my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8); m_param_begin= m_param_end= m_body_begin= 0; - m_qname.str= m_db.str= m_name.str= m_params.str= + m_qname.str= m_db.str= m_name.str= m_params.str= m_body.str= m_defstr.str= 0; m_qname.length= m_db.length= m_name.length= m_params.length= m_body.length= m_defstr.length= 0; @@ -478,29 +478,42 @@ sp_head::init(LEX *lex) DBUG_VOID_RETURN; } + +void +sp_head::init_sp_name(THD *thd, sp_name *spname) +{ + DBUG_ENTER("sp_head::init_sp_name"); + + /* Must be initialized in the parser. */ + + DBUG_ASSERT(spname && spname->m_db.str && spname->m_db.length); + + /* We have to copy strings to get them into the right memroot. */ + + m_db.length= spname->m_db.length; + m_db.str= strmake_root(thd->mem_root, spname->m_db.str, spname->m_db.length); + + m_name.length= spname->m_name.length; + m_name.str= strmake_root(thd->mem_root, spname->m_name.str, + spname->m_name.length); + + if (spname->m_qname.length == 0) + spname->init_qname(thd); + + m_qname.length= spname->m_qname.length; + m_qname.str= strmake_root(thd->mem_root, spname->m_qname.str, + m_qname.length); +} + + void -sp_head::init_strings(THD *thd, LEX *lex, sp_name *name) +sp_head::init_strings(THD *thd, LEX *lex) { DBUG_ENTER("sp_head::init_strings"); uchar *endp; /* Used to trim the end */ /* During parsing, we must use thd->mem_root */ MEM_ROOT *root= thd->mem_root; - DBUG_ASSERT(name); - /* Must be initialized in the parser */ - DBUG_ASSERT(name->m_db.str && name->m_db.length); - - /* We have to copy strings to get them into the right memroot */ - m_db.length= name->m_db.length; - m_db.str= strmake_root(root, name->m_db.str, name->m_db.length); - m_name.length= name->m_name.length; - m_name.str= strmake_root(root, name->m_name.str, name->m_name.length); - - if (name->m_qname.length == 0) - name->init_qname(thd); - m_qname.length= name->m_qname.length; - m_qname.str= strmake_root(root, name->m_qname.str, m_qname.length); - if (m_param_begin && m_param_end) { m_params.length= m_param_end - m_param_begin; @@ -514,10 +527,7 @@ sp_head::init_strings(THD *thd, LEX *lex, sp_name *name) Trim "garbage" at the end. This is sometimes needed with the "/ * ! VERSION... * /" wrapper in dump files. */ - while (m_body_begin < endp && - (endp[-1] <= ' ' || endp[-1] == '*' || - endp[-1] == '/' || endp[-1] == ';')) - endp-= 1; + endp= skip_rear_comments(m_body_begin, endp); m_body.length= endp - m_body_begin; m_body.str= strmake_root(root, (char *)m_body_begin, m_body.length); @@ -1097,6 +1107,7 @@ sp_head::execute(THD *thd) thd->restore_active_arena(&execute_arena, &backup_arena); + thd->spcont->pop_all_cursors(); // To avoid memory leaks after an error /* Restore all saved */ old_packet.swap(thd->packet); @@ -1158,6 +1169,161 @@ sp_head::execute(THD *thd) m_first_instance->m_first_free_instance->m_recursion_level == m_recursion_level + 1)); m_first_instance->m_first_free_instance= this; + + DBUG_RETURN(err_status); +} + + +#ifndef NO_EMBEDDED_ACCESS_CHECKS +/* + set_routine_security_ctx() changes routine security context, and + checks if there is an EXECUTE privilege in new context. If there is + no EXECUTE privilege, it changes the context back and returns a + error. + + SYNOPSIS + set_routine_security_ctx() + thd thread handle + sp stored routine to change the context for + is_proc TRUE is procedure, FALSE if function + save_ctx pointer to an old security context + + RETURN + TRUE if there was a error, and the context wasn't changed. + FALSE if the context was changed. +*/ + +bool +set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc, + Security_context **save_ctx) +{ + *save_ctx= 0; + if (sp_change_security_context(thd, sp, save_ctx)) + return TRUE; + + /* + If we changed context to run as another user, we need to check the + access right for the new context again as someone may have revoked + the right to use the procedure from this user. + + TODO: + Cache if the definer has the right to use the object on the + first usage and only reset the cache if someone does a GRANT + statement that 'may' affect this. + */ + if (*save_ctx && + check_routine_access(thd, EXECUTE_ACL, + sp->m_db.str, sp->m_name.str, is_proc, FALSE)) + { + sp_restore_security_context(thd, *save_ctx); + *save_ctx= 0; + return TRUE; + } + + return FALSE; +} +#endif // ! NO_EMBEDDED_ACCESS_CHECKS + + +/* + Execute a trigger: + - changes security context for triggers + - switch to new memroot + - call sp_head::execute + - restore old memroot + - restores security context + + SYNOPSIS + sp_head::execute_trigger() + thd Thread handle + db database name + table table name + grant_info GRANT_INFO structure to be filled with + information about definer's privileges + on subject table + + RETURN + FALSE on success + TRUE on error +*/ + +bool +sp_head::execute_trigger(THD *thd, const char *db, const char *table, + GRANT_INFO *grant_info) +{ + sp_rcontext *octx = thd->spcont; + sp_rcontext *nctx = NULL; + bool err_status= FALSE; + MEM_ROOT call_mem_root; + Query_arena call_arena(&call_mem_root, Query_arena::INITIALIZED_FOR_SP); + Query_arena backup_arena; + + DBUG_ENTER("sp_head::execute_trigger"); + DBUG_PRINT("info", ("trigger %s", m_name.str)); + +#ifndef NO_EMBEDDED_ACCESS_CHECKS + Security_context *save_ctx; + if (sp_change_security_context(thd, this, &save_ctx)) + DBUG_RETURN(TRUE); + + /* + NOTE: TRIGGER_ACL should be used here. + */ + if (check_global_access(thd, SUPER_ACL)) + { + sp_restore_security_context(thd, save_ctx); + DBUG_RETURN(TRUE); + } + + /* + Fetch information about table-level privileges to GRANT_INFO + structure for subject table. Check of privileges that will use it + and information about column-level privileges will happen in + Item_trigger_field::fix_fields(). + */ + fill_effective_table_privileges(thd, grant_info, db, table); +#endif // NO_EMBEDDED_ACCESS_CHECKS + + /* + Prepare arena and memroot for objects which lifetime is whole + duration of trigger call (sp_rcontext, it's tables and items, + sp_cursor and Item_cache holders for case expressions). We can't + use caller's arena/memroot for those objects because in this case + some fixed amount of memory will be consumed for each trigger + invocation and so statements which involve lot of them will hog + memory. + + TODO: we should create sp_rcontext once per command and reuse it + on subsequent executions of a trigger. + */ + init_sql_alloc(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0); + thd->set_n_backup_active_arena(&call_arena, &backup_arena); + + if (!(nctx= new sp_rcontext(m_pcont, 0, octx)) || + nctx->init(thd)) + { + err_status= TRUE; + goto err_with_cleanup; + } + +#ifndef DBUG_OFF + nctx->sp= this; +#endif + + thd->spcont= nctx; + + err_status= execute(thd); + +err_with_cleanup: + thd->restore_active_arena(&call_arena, &backup_arena); +#ifndef NO_EMBEDDED_ACCESS_CHECKS + sp_restore_security_context(thd, save_ctx); +#endif // NO_EMBEDDED_ACCESS_CHECKS + delete nctx; + call_arena.free_items(); + free_root(&call_mem_root, MYF(0)); + thd->spcont= octx; + DBUG_RETURN(err_status); } @@ -1165,8 +1331,12 @@ sp_head::execute(THD *thd) /* Execute a function: - evaluate parameters + - changes security context for SUID routines + - switch to new memroot - call sp_head::execute + - restore old memroot - evaluate the return value + - restores security context SYNOPSIS sp_head::execute_function() @@ -1293,6 +1463,15 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, } thd->spcont= nctx; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + Security_context *save_security_ctx; + if (set_routine_security_ctx(thd, this, FALSE, &save_security_ctx)) + { + err_status= TRUE; + goto err_with_cleanup; + } +#endif + binlog_save_options= thd->options; if (need_binlog_call) { @@ -1333,7 +1512,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, reset_dynamic(&thd->user_var_events); } - if (m_type == TYPE_ENUM_FUNCTION && !err_status) + if (!err_status) { /* We need result only in function but not in trigger */ @@ -1344,8 +1523,9 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, } } - - nctx->pop_all_cursors(); // To avoid memory leaks after an error +#ifndef NO_EMBEDDED_ACCESS_CHECKS + sp_restore_security_context(thd, save_security_ctx); +#endif err_with_cleanup: delete nctx; @@ -1368,8 +1548,10 @@ err_with_cleanup: The function does the following steps: - Set all parameters + - changes security context for SUID routines - call sp_head::execute - copy back values of INOUT and OUT parameters + - restores security context RETURN FALSE on success @@ -1490,6 +1672,12 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) thd->spcont= nctx; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + Security_context *save_security_ctx= 0; + if (!err_status) + err_status= set_routine_security_ctx(thd, this, TRUE, &save_security_ctx); +#endif + if (!err_status) err_status= execute(thd); @@ -1534,10 +1722,14 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) } } +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (save_security_ctx) + sp_restore_security_context(thd, save_security_ctx); +#endif + if (!save_spcont) delete octx; - nctx->pop_all_cursors(); // To avoid memory leaks after an error delete nctx; thd->spcont= save_spcont; @@ -1674,14 +1866,18 @@ sp_head::fill_field_definition(THD *thd, LEX *lex, enum enum_field_types field_type, create_field *field_def) { + HA_CREATE_INFO sp_db_info; LEX_STRING cmt = { 0, 0 }; uint unused1= 0; int unused2= 0; + load_db_opt_by_name(thd, m_db.str, &sp_db_info); + if (field_def->init(thd, (char*) "", field_type, lex->length, lex->dec, lex->type, (Item*) 0, (Item*) 0, &cmt, 0, &lex->interval_list, - (lex->charset ? lex->charset : default_charset_info), + (lex->charset ? lex->charset : + sp_db_info.default_table_charset), lex->uint_geom_type)) return TRUE; diff --git a/sql/sp_head.h b/sql/sp_head.h index 073cca2cd12..4cd34bc9e20 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -193,9 +193,13 @@ public: void init(LEX *lex); + /* Copy sp name from parser. */ + void + init_sp_name(THD *thd, sp_name *spname); + // Initialize strings after parsing header void - init_strings(THD *thd, LEX *lex, sp_name *name); + init_strings(THD *thd, LEX *lex); int create(THD *thd); @@ -207,6 +211,10 @@ public: destroy(); bool + execute_trigger(THD *thd, const char *db, const char *table, + GRANT_INFO *grant_onfo); + + bool execute_function(THD *thd, Item **args, uint argcount, Field *return_fld); bool @@ -1149,6 +1157,10 @@ sp_change_security_context(THD *thd, sp_head *sp, Security_context **backup); void sp_restore_security_context(THD *thd, Security_context *backup); + +bool +set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc, + Security_context **save_ctx); #endif /* NO_EMBEDDED_ACCESS_CHECKS */ TABLE_LIST * diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 902539dfdec..81508e4e9be 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -295,7 +295,6 @@ static bool write_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create) create Where to store the read options DESCRIPTION - For now, only default-character-set is read. RETURN VALUES 0 File found @@ -384,6 +383,50 @@ err1: /* + Retrieve database options by name. Load database options file or fetch from + cache. + + SYNOPSIS + load_db_opt_by_name() + db_name Database name + db_create_info Where to store the database options + + DESCRIPTION + load_db_opt_by_name() is a shortcut for load_db_opt(). + + NOTE + Although load_db_opt_by_name() (and load_db_opt()) returns status of + the operation, it is useless usually and should be ignored. The problem + is that there are 1) system databases ("mysql") and 2) virtual + databases ("information_schema"), which do not contain options file. + So, load_db_opt[_by_name]() returns FALSE for these databases, but this + is not an error. + + load_db_opt[_by_name]() clears db_create_info structure in any case, so + even on failure it contains valid data. So, common use case is just + call load_db_opt[_by_name]() without checking return value and use + db_create_info right after that. + + RETURN VALUES (read NOTE!) + FALSE Success + TRUE Failed to retrieve options +*/ + +bool load_db_opt_by_name(THD *thd, const char *db_name, + HA_CREATE_INFO *db_create_info) +{ + char db_opt_path[FN_REFLEN]; + + strxnmov(db_opt_path, sizeof (db_opt_path) - 1, mysql_data_home, "/", + db_name, "/", MY_DB_OPT_FILE, NullS); + + unpack_filename(db_opt_path, db_opt_path); + + return load_db_opt(thd, db_opt_path, db_create_info); +} + + +/* Create a database SYNOPSIS @@ -1126,8 +1169,6 @@ bool mysql_change_db(THD *thd, const char *name, bool no_access_check) { int path_length, db_length; char *db_name; - char path[FN_REFLEN]; - HA_CREATE_INFO create; bool system_db= 0; #ifndef NO_EMBEDDED_ACCESS_CHECKS ulong db_access; @@ -1196,16 +1237,14 @@ bool mysql_change_db(THD *thd, const char *name, bool no_access_check) } } #endif - (void) sprintf(path,"%s/%s", mysql_data_home, db_name); - path_length= unpack_dirname(path, path); // Convert if not UNIX - if (path_length && path[path_length-1] == FN_LIBCHAR) - path[path_length-1]= '\0'; // remove ending '\' - if (my_access(path,F_OK)) + + if (check_db_dir_existence(db_name)) { my_error(ER_BAD_DB_ERROR, MYF(0), db_name); my_free(db_name, MYF(0)); DBUG_RETURN(1); } + end: x_free(thd->db); DBUG_ASSERT(db_name == NULL || db_name[0] != '\0'); @@ -1221,8 +1260,10 @@ end: } else { - strmov(path+unpack_dirname(path,path), MY_DB_OPT_FILE); - load_db_opt(thd, path, &create); + HA_CREATE_INFO create; + + load_db_opt_by_name(thd, db_name, &create); + thd->db_charset= create.default_table_charset ? create.default_table_charset : thd->variables.collation_server; @@ -1230,3 +1271,36 @@ end: } DBUG_RETURN(0); } + + +/* + Check if there is directory for the database name. + + SYNOPSIS + check_db_dir_existence() + db_name database name + + RETURN VALUES + FALSE There is directory for the specified database name. + TRUE The directory does not exist. +*/ + +bool check_db_dir_existence(const char *db_name) +{ + char db_dir_path[FN_REFLEN]; + uint db_dir_path_len; + + strxnmov(db_dir_path, sizeof (db_dir_path) - 1, mysql_data_home, "/", + db_name, NullS); + + db_dir_path_len= unpack_dirname(db_dir_path, db_dir_path); + + /* Remove trailing '/' or '\' if exists. */ + + if (db_dir_path_len && db_dir_path[db_dir_path_len - 1] == FN_LIBCHAR) + db_dir_path[db_dir_path_len - 1]= 0; + + /* Check access. */ + + return my_access(db_dir_path, F_OK); +} diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 7d4dca15608..bb66fde79fe 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -126,6 +126,7 @@ void lex_start(THD *thd, uchar *buf,uint length) lex->param_list.empty(); lex->view_list.empty(); lex->prepared_stmt_params.empty(); + lex->auxiliary_table_list.empty(); lex->unit.next= lex->unit.master= lex->unit.link_next= lex->unit.return_to= 0; lex->unit.prev= lex->unit.link_prev= 0; @@ -1053,6 +1054,30 @@ int MYSQLlex(void *arg, void *yythd) } } + +/* + Skip comment in the end of statement. + + SYNOPSIS + skip_rear_comments() + begin pointer to the beginning of statement + end pointer to the end of statement + + DESCRIPTION + The function is intended to trim comments at the end of the statement. + + RETURN + Pointer to the last non-comment symbol of the statement. +*/ + +uchar *skip_rear_comments(uchar *begin, uchar *end) +{ + while (begin < end && (end[-1] <= ' ' || end[-1] == '*' || + end[-1] == '/' || end[-1] == ';')) + end-= 1; + return end; +} + /* st_select_lex structures initialisations */ diff --git a/sql/sql_lex.h b/sql/sql_lex.h index e5b087fc72a..d7438a37d7e 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1115,4 +1115,4 @@ extern void lex_free(void); extern void lex_start(THD *thd, uchar *buf,uint length); extern void lex_end(LEX *lex); extern int MYSQLlex(void *arg, void *yythd); - +extern uchar *skip_rear_comments(uchar *begin, uchar *end); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 28ed3e25d57..786da04aff9 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -4413,9 +4413,6 @@ end_with_restore_list: } else { -#ifndef NO_EMBEDDED_ACCESS_CHECKS - Security_context *save_ctx; -#endif ha_rows select_limit; /* bits that should be cleared in thd->server_status */ uint bits_to_be_cleared= 0; @@ -4457,21 +4454,11 @@ end_with_restore_list: #ifndef NO_EMBEDDED_ACCESS_CHECKS if (check_routine_access(thd, EXECUTE_ACL, - sp->m_db.str, sp->m_name.str, TRUE, 0) || - sp_change_security_context(thd, sp, &save_ctx)) + sp->m_db.str, sp->m_name.str, TRUE, FALSE)) { thd->net.no_send_ok= nsok; goto error; } - if (save_ctx && - check_routine_access(thd, EXECUTE_ACL, - sp->m_db.str, sp->m_name.str, TRUE, 0)) - { - thd->net.no_send_ok= nsok; - sp_restore_security_context(thd, save_ctx); - goto error; - } - #endif select_limit= thd->variables.select_limit; thd->variables.select_limit= HA_POS_ERROR; @@ -4495,9 +4482,6 @@ end_with_restore_list: thd->total_warn_count= 0; thd->variables.select_limit= select_limit; -#ifndef NO_EMBEDDED_ACCESS_CHECKS - sp_restore_security_context(thd, save_ctx); -#endif thd->net.no_send_ok= nsok; thd->server_status&= ~bits_to_be_cleared; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index ffe54310843..6d35c441368 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -2128,29 +2128,21 @@ void reinit_stmt_before_use(THD *thd, LEX *lex) they have their own table list). */ for (TABLE_LIST *tables= lex->query_tables; - tables; - tables= tables->next_global) + tables; + tables= tables->next_global) + { + tables->reinit_before_use(thd); + } + /* + Cleanup of the special case of DELETE t1, t2 FROM t1, t2, t3 ... + (multi-delete). We do a full clean up, although at the moment all we + need to clean in the tables of MULTI-DELETE list is 'table' member. + */ + for (TABLE_LIST *tables= (TABLE_LIST*) lex->auxiliary_table_list.first; + tables; + tables= tables->next_global) { - /* - Reset old pointers to TABLEs: they are not valid since the tables - were closed in the end of previous prepare or execute call. - */ tables->reinit_before_use(thd); - - /* Reset is_schema_table_processed value(needed for I_S tables */ - tables->is_schema_table_processed= FALSE; - - TABLE_LIST *embedded; /* The table at the current level of nesting. */ - TABLE_LIST *embedding= tables; /* The parent nested table reference. */ - do - { - embedded= embedding; - if (embedded->prep_on_expr) - embedded->on_expr= embedded->prep_on_expr->copy_andor_structure(thd); - embedding= embedded->embedding; - } - while (embedding && - embedding->nested_join->join_list.head() == embedded); } lex->current_select= &lex->select_lex; @@ -2165,7 +2157,7 @@ void reinit_stmt_before_use(THD *thd, LEX *lex) } lex->allow_sum_func= 0; lex->in_sum_func= NULL; - DBUG_VOID_RETURN; + DBUG_VOID_RETURN; } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index cabb04c5f16..805b6c597e6 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -250,9 +250,35 @@ bool mysqld_show_column_types(THD *thd) } -int -mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path, - const char *wild, bool dir) +/* + find_files() - find files in a given directory. + + SYNOPSIS + find_files() + thd thread handler + files put found files in this list + db database name to set in TABLE_LIST structure + path path to database + wild filter for found files + dir read databases in path if TRUE, read .frm files in + database otherwise + + RETURN + FIND_FILES_OK success + FIND_FILES_OOM out of memory error + FIND_FILES_DIR no such directory, or directory can't be read +*/ + +enum find_files_result { + FIND_FILES_OK, + FIND_FILES_OOM, + FIND_FILES_DIR +}; + +static +find_files_result +find_files(THD *thd, List<char> *files, const char *db, + const char *path, const char *wild, bool dir) { uint i; char *ext; @@ -262,7 +288,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path, uint col_access=thd->col_access; #endif TABLE_LIST table_list; - DBUG_ENTER("mysql_find_files"); + DBUG_ENTER("find_files"); if (wild && !wild[0]) wild=0; @@ -275,7 +301,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path, my_error(ER_BAD_DB_ERROR, MYF(ME_BELL+ME_WAITTANG), db); else my_error(ER_CANT_READ_DIR, MYF(ME_BELL+ME_WAITTANG), path, my_errno); - DBUG_RETURN(-1); + DBUG_RETURN(FIND_FILES_DIR); } for (i=0 ; i < (uint) dirp->number_off_files ; i++) @@ -337,7 +363,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path, if (files->push_back(thd->strdup(file->name))) { my_dirend(dirp); - DBUG_RETURN(-1); + DBUG_RETURN(FIND_FILES_OOM); } } DBUG_PRINT("info",("found: %d files", files->elements)); @@ -345,7 +371,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path, VOID(ha_find_files(thd,db,path,wild,dir,files)); - DBUG_RETURN(0); + DBUG_RETURN(FIND_FILES_OK); } @@ -439,13 +465,11 @@ bool mysqld_show_create_db(THD *thd, char *dbname, { Security_context *sctx= thd->security_ctx; int length; - char path[FN_REFLEN]; char buff[2048]; String buffer(buff, sizeof(buff), system_charset_info); #ifndef NO_EMBEDDED_ACCESS_CHECKS uint db_access; #endif - bool found_libchar; HA_CREATE_INFO create; uint create_options = create_info ? create_info->options : 0; Protocol *protocol=thd->protocol; @@ -480,23 +504,13 @@ bool mysqld_show_create_db(THD *thd, char *dbname, } else { - (void) sprintf(path,"%s/%s",mysql_data_home, dbname); - length=unpack_dirname(path,path); // Convert if not unix - found_libchar= 0; - if (length && path[length-1] == FN_LIBCHAR) - { - found_libchar= 1; - path[length-1]=0; // remove ending '\' - } - if (access(path,F_OK)) + if (check_db_dir_existence(dbname)) { my_error(ER_BAD_DB_ERROR, MYF(0), dbname); DBUG_RETURN(TRUE); } - if (found_libchar) - path[length-1]= FN_LIBCHAR; - strmov(path+length, MY_DB_OPT_FILE); - load_db_opt(thd, path, &create); + + load_db_opt_by_name(thd, dbname, &create); } List<Item> field_list; field_list.push_back(new Item_empty_string("Database",NAME_LEN)); @@ -2000,8 +2014,8 @@ enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table) wild string otherwise it's db name; RETURN - 1 error - 0 success + zero success + non-zero error */ int make_db_list(THD *thd, List<char> *files, @@ -2027,8 +2041,8 @@ int make_db_list(THD *thd, List<char> *files, if (files->push_back(thd->strdup(information_schema_name.str))) return 1; } - return mysql_find_files(thd, files, NullS, mysql_data_home, - idx_field_vals->db_value, 1); + return (find_files(thd, files, NullS, mysql_data_home, + idx_field_vals->db_value, 1) != FIND_FILES_OK); } /* @@ -2055,7 +2069,8 @@ int make_db_list(THD *thd, List<char> *files, if (files->push_back(thd->strdup(information_schema_name.str))) return 1; *with_i_schema= 1; - return mysql_find_files(thd, files, NullS, mysql_data_home, NullS, 1); + return (find_files(thd, files, NullS, + mysql_data_home, NullS, 1) != FIND_FILES_OK); } @@ -2204,9 +2219,28 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) strxmov(path, mysql_data_home, "/", base_name, NullS); end= path + (len= unpack_dirname(path,path)); len= FN_LEN - len; - if (mysql_find_files(thd, &files, base_name, - path, idx_field_vals.table_value, 0)) - goto err; + find_files_result res= find_files(thd, &files, base_name, + path, idx_field_vals.table_value, 0); + if (res != FIND_FILES_OK) + { + /* + Downgrade errors about problems with database directory to + warnings if this is not a 'SHOW' command. Another thread + may have dropped database, and we may still have a name + for that directory. + */ + if (res == FIND_FILES_DIR && lex->orig_sql_command == SQLCOM_END) + { + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + thd->net.last_errno, thd->net.last_error); + thd->clear_error(); + continue; + } + else + { + goto err; + } + } if (lower_case_table_names) orig_base_name= thd->strdup(base_name); } @@ -2319,8 +2353,11 @@ bool store_schema_shemata(THD* thd, TABLE *table, const char *db_name, int fill_schema_shemata(THD *thd, TABLE_LIST *tables, COND *cond) { - char path[FN_REFLEN]; - bool found_libchar; + /* + TODO: fill_schema_shemata() is called when new client is connected. + Returning error status in this case leads to client hangup. + */ + INDEX_FIELD_VALUES idx_field_vals; List<char> files; char *file_name; @@ -2352,20 +2389,9 @@ int fill_schema_shemata(THD *thd, TABLE_LIST *tables, COND *cond) (grant_option && !check_grant_db(thd, file_name))) #endif { - strxmov(path, mysql_data_home, "/", file_name, NullS); - length=unpack_dirname(path,path); // Convert if not unix - found_libchar= 0; - if (length && path[length-1] == FN_LIBCHAR) - { - found_libchar= 1; - path[length-1]=0; // remove ending '\' - } + load_db_opt_by_name(thd, file_name, &create); - if (found_libchar) - path[length-1]= FN_LIBCHAR; - strmov(path+length, MY_DB_OPT_FILE); - load_db_opt(thd, path, &create); - if (store_schema_shemata(thd, table, file_name, + if (store_schema_shemata(thd, table, file_name, create.default_table_charset)) DBUG_RETURN(1); } @@ -3969,7 +3995,12 @@ bool get_schema_tables_result(JOIN *join) if (table_list->schema_table->fill_table(thd, table_list, tab->select_cond)) + { result= 1; + join->error= 1; + table_list->is_schema_table_processed= TRUE; + break; + } table_list->is_schema_table_processed= TRUE; } } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 4772d64ad0a..e065a32c2e8 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1631,10 +1631,9 @@ bool mysql_create_table(THD *thd,const char *db, const char *table_name, if (!create_info->default_table_charset) { HA_CREATE_INFO db_info; - char path[FN_REFLEN]; - /* Abuse build_table_path() to build the path to the db.opt file */ - build_table_path(path, sizeof(path), db, MY_DB_OPT_FILE, ""); - load_db_opt(thd, path, &db_info); + + load_db_opt_by_name(thd, db, &db_info); + create_info->default_table_charset= db_info.default_table_charset; } diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 66a16f16d8c..e806dd4a3f3 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -295,7 +295,10 @@ end: append_definer(thd, &log_query, &definer_user, &definer_host); } - log_query.append(thd->lex->stmt_definition_begin); + log_query.append(thd->lex->stmt_definition_begin, + (char *)thd->lex->sphead->m_body_begin - + thd->lex->stmt_definition_begin + + thd->lex->sphead->m_body.length); } /* Such a statement can always go directly to binlog, no trans cache. */ @@ -1503,40 +1506,11 @@ bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event, old_field= table->field; } -#ifndef NO_EMBEDDED_ACCESS_CHECKS - Security_context *save_ctx; - - if (sp_change_security_context(thd, sp_trigger, &save_ctx)) - return TRUE; - - /* - NOTE: TRIGGER_ACL should be used below. - */ - - if (check_global_access(thd, SUPER_ACL)) - { - sp_restore_security_context(thd, save_ctx); - return TRUE; - } - - /* - Fetch information about table-level privileges to GRANT_INFO structure for - subject table. Check of privileges that will use it and information about - column-level privileges will happen in Item_trigger_field::fix_fields(). - */ - - fill_effective_table_privileges(thd, - &subject_table_grants[event][time_type], - table->s->db, table->s->table_name); -#endif // NO_EMBEDDED_ACCESS_CHECKS - thd->reset_sub_statement_state(&statement_state, SUB_STMT_TRIGGER); - err_status= sp_trigger->execute_function(thd, 0, 0, 0); + err_status= sp_trigger->execute_trigger + (thd, table->s->db, table->s->table_name, + &subject_table_grants[event][time_type]); thd->restore_sub_statement_state(&statement_state); - -#ifndef NO_EMBEDDED_ACCESS_CHECKS - sp_restore_security_context(thd, save_ctx); -#endif // NO_EMBEDDED_ACCESS_CHECKS } return err_status; diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 1561ade78af..80cb8970049 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -671,8 +671,10 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, view->query.str= (char*)str.ptr(); view->query.length= str.length()-1; // we do not need last \0 view->source.str= thd->query + thd->lex->create_view_select_start; - view->source.length= (thd->query_length - - thd->lex->create_view_select_start); + view->source.length= (char *)skip_rear_comments((uchar *)view->source.str, + (uchar *)thd->query + + thd->query_length) - + view->source.str; view->file_version= 1; view->calc_md5(md5); view->md5.str= md5; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 425945f525e..675c54c0fbc 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1256,6 +1256,17 @@ create_function_tail: RETURNS_SYM udf_type UDF_SONAME_SYM TEXT_STRING_sys { LEX *lex=Lex; + if (lex->definer != NULL) + { + /* + DEFINER is a concept meaningful when interpreting SQL code. + UDF functions are compiled. + Using DEFINER with UDF has therefore no semantic, + and is considered a parsing error. + */ + my_error(ER_WRONG_USAGE, MYF(0), "SONAME", "DEFINER"); + YYABORT; + } lex->sql_command = SQLCOM_CREATE_FUNCTION; lex->udf.name = lex->spname->m_name; lex->udf.returns=(Item_result) $2; @@ -1285,6 +1296,7 @@ create_function_tail: sp= new sp_head(); sp->reset_thd_mem_root(YYTHD); sp->init(lex); + sp->init_sp_name(YYTHD, lex->spname); sp->m_type= TYPE_ENUM_FUNCTION; lex->sphead= sp; @@ -1339,7 +1351,7 @@ create_function_tail: YYABORT; lex->sql_command= SQLCOM_CREATE_SPFUNCTION; - sp->init_strings(YYTHD, lex, lex->spname); + sp->init_strings(YYTHD, lex); if (!(sp->m_flags & sp_head::HAS_RETURN)) { my_error(ER_SP_NORETURN, MYF(0), sp->m_qname.str); @@ -1911,9 +1923,12 @@ sp_proc_stmt: sp_instr_stmt *i=new sp_instr_stmt(sp->instructions(), lex->spcont, lex); - /* Extract the query statement from the tokenizer: - The end is either lex->tok_end or tok->ptr. */ - if (lex->ptr - lex->tok_end > 1) + /* + Extract the query statement from the tokenizer. The + end is either lex->ptr, if there was no lookahead, + lex->tok_end otherwise. + */ + if (yychar == YYEMPTY) i->m_query.length= lex->ptr - sp->m_tmp_query; else i->m_query.length= lex->tok_end - sp->m_tmp_query; @@ -7822,7 +7837,12 @@ option_type_value: lex))) YYABORT; - if (lex->ptr - lex->tok_end > 1) + /* + Extract the query statement from the tokenizer. The + end is either lex->ptr, if there was no lookahead, + lex->tok_end otherwise. + */ + if (yychar == YYEMPTY) qbuff.length= lex->ptr - sp->m_tmp_query; else qbuff.length= lex->tok_end - sp->m_tmp_query; @@ -9092,6 +9112,7 @@ trigger_tail: YYABORT; sp->reset_thd_mem_root(YYTHD); sp->init(lex); + sp->init_sp_name(YYTHD, $3); lex->stmt_definition_begin= $2; lex->ident.str= $7; @@ -9120,7 +9141,7 @@ trigger_tail: sp_head *sp= lex->sphead; lex->sql_command= SQLCOM_CREATE_TRIGGER; - sp->init_strings(YYTHD, lex, $3); + sp->init_strings(YYTHD, lex); /* Restore flag if it was cleared above */ if (sp->m_old_cmq) YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES; @@ -9168,13 +9189,14 @@ sp_tail: my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "PROCEDURE"); YYABORT; } - + lex->stmt_definition_begin= $2; - + /* Order is important here: new - reset - init */ sp= new sp_head(); sp->reset_thd_mem_root(YYTHD); sp->init(lex); + sp->init_sp_name(YYTHD, $3); sp->m_type= TYPE_ENUM_PROCEDURE; lex->sphead= sp; @@ -9212,7 +9234,7 @@ sp_tail: LEX *lex= Lex; sp_head *sp= lex->sphead; - sp->init_strings(YYTHD, lex, $3); + sp->init_strings(YYTHD, lex); lex->sql_command= SQLCOM_CREATE_PROCEDURE; /* Restore flag if it was cleared above */ if (sp->m_old_cmq) diff --git a/sql/table.cc b/sql/table.cc index e50b401df5f..b4c08729a79 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -2992,13 +2992,27 @@ Field_iterator_table_ref::get_natural_column_ref() st_table_list::reinit_before_use() */ -void st_table_list::reinit_before_use(THD * /* thd */) +void st_table_list::reinit_before_use(THD *thd) { /* Reset old pointers to TABLEs: they are not valid since the tables were closed in the end of previous prepare or execute call. */ table= 0; + /* Reset is_schema_table_processed value(needed for I_S tables */ + is_schema_table_processed= FALSE; + + TABLE_LIST *embedded; /* The table at the current level of nesting. */ + TABLE_LIST *embedding= this; /* The parent nested table reference. */ + do + { + embedded= embedding; + if (embedded->prep_on_expr) + embedded->on_expr= embedded->prep_on_expr->copy_andor_structure(thd); + embedding= embedded->embedding; + } + while (embedding && + embedding->nested_join->join_list.head() == embedded); } diff --git a/sql/table.h b/sql/table.h index eb34867c390..5a0cf1fdc12 100644 --- a/sql/table.h +++ b/sql/table.h @@ -677,6 +677,10 @@ typedef struct st_table_list private: bool prep_check_option(THD *thd, uint8 check_opt_type); bool prep_where(THD *thd, Item **conds, bool no_where_clause); + /* + Cleanup for re-execution in a prepared statement or a stored + procedure. + */ } TABLE_LIST; class Item; diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 5ee63cb8738..e4b2374a698 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -50,7 +50,6 @@ static unsigned int opt_port; static my_bool tty_password= 0, opt_silent= 0; static MYSQL *mysql= 0; -static char query[MAX_TEST_QUERY_LENGTH]; static char current_db[]= "client_test_db"; static unsigned int test_count= 0; static unsigned int opt_count= 0; @@ -270,6 +269,7 @@ mysql_simple_prepare(MYSQL *mysql, const char *query) static void client_connect(ulong flag) { int rc; + static char query[MAX_TEST_QUERY_LENGTH]; myheader_r("client_connect"); if (!opt_silent) @@ -327,6 +327,8 @@ static void client_connect(ulong flag) static void client_disconnect() { + static char query[MAX_TEST_QUERY_LENGTH]; + myheader_r("client_disconnect"); if (mysql) @@ -658,6 +660,7 @@ int my_stmt_result(const char *buff) static void verify_col_data(const char *table, const char *col, const char *exp_data) { + static char query[MAX_TEST_QUERY_LENGTH]; MYSQL_RES *result; MYSQL_ROW row; int rc, field= 1; @@ -1363,6 +1366,7 @@ static void test_prepare_insert_update() for (cur_query= testcase; *cur_query; cur_query++) { + char query[MAX_TEST_QUERY_LENGTH]; printf("\nRunning query: %s", *cur_query); strmov(query, *cur_query); stmt= mysql_simple_prepare(mysql, query); @@ -1397,6 +1401,7 @@ static void test_prepare_simple() { MYSQL_STMT *stmt; int rc; + char query[MAX_TEST_QUERY_LENGTH]; myheader("test_prepare_simple"); @@ -1467,6 +1472,7 @@ static void test_prepare_field_result() MYSQL_STMT *stmt; MYSQL_RES *result; int rc; + char query[MAX_TEST_QUERY_LENGTH]; myheader("test_prepare_field_result"); @@ -1518,6 +1524,7 @@ static void test_prepare_syntax() { MYSQL_STMT *stmt; int rc; + char query[MAX_TEST_QUERY_LENGTH]; myheader("test_prepare_syntax"); @@ -1559,6 +1566,7 @@ static void test_prepare() my_bool is_null[7]; char llbuf[22]; MYSQL_BIND bind[7]; + char query[MAX_TEST_QUERY_LENGTH]; myheader("test_prepare"); @@ -1732,6 +1740,7 @@ static void test_double_compare() MYSQL_RES *result; MYSQL_BIND bind[3]; ulong length[3]; + char query[MAX_TEST_QUERY_LENGTH]; myheader("test_double_compare"); @@ -1814,6 +1823,7 @@ static void test_null() uint nData; MYSQL_BIND bind[2]; my_bool is_null[2]; + char query[MAX_TEST_QUERY_LENGTH]; myheader("test_null"); @@ -1960,6 +1970,7 @@ static void test_ps_null_param() /* Execute several queries, all returning NULL in result. */ for(cur_query= queries; *cur_query; cur_query++) { + char query[MAX_TEST_QUERY_LENGTH]; strmov(query, *cur_query); stmt= mysql_simple_prepare(mysql, query); check_stmt(stmt); @@ -1991,6 +2002,7 @@ static void test_fetch_null() MYSQL_BIND bind[11]; ulong length[11]; my_bool is_null[11]; + char query[MAX_TEST_QUERY_LENGTH]; myheader("test_fetch_null"); @@ -2219,6 +2231,7 @@ static void test_select() int nData= 1; MYSQL_BIND bind[2]; ulong length[2]; + char query[MAX_TEST_QUERY_LENGTH]; myheader("test_select"); @@ -2290,6 +2303,7 @@ static void test_ps_conj_select() int32 int_data; char str_data[32]; unsigned long str_length; + char query[MAX_TEST_QUERY_LENGTH]; myheader("test_ps_conj_select"); rc= mysql_query(mysql, "drop table if exists t1"); @@ -2347,6 +2361,7 @@ static void test_bug1115() MYSQL_BIND bind[1]; ulong length[1]; char szData[11]; + char query[MAX_TEST_QUERY_LENGTH]; myheader("test_bug1115"); @@ -2458,6 +2473,7 @@ static void test_bug1180() MYSQL_BIND bind[1]; ulong length[1]; char szData[11]; + char query[MAX_TEST_QUERY_LENGTH]; myheader("test_select_bug"); @@ -2548,6 +2564,7 @@ static void test_bug1644() int num; my_bool isnull; int rc, i; + char query[MAX_TEST_QUERY_LENGTH]; myheader("test_bug1644"); @@ -2647,6 +2664,7 @@ static void test_select_show() { MYSQL_STMT *stmt; int rc; + char query[MAX_TEST_QUERY_LENGTH]; myheader("test_select_show"); @@ -2715,6 +2733,7 @@ static void test_simple_update() MYSQL_RES *result; MYSQL_BIND bind[2]; ulong length[2]; + char query[MAX_TEST_QUERY_LENGTH]; myheader("test_simple_update"); @@ -2792,6 +2811,7 @@ static void test_long_data() char *data= NullS; MYSQL_RES *result; MYSQL_BIND bind[3]; + char query[MAX_TEST_QUERY_LENGTH]; myheader("test_long_data"); @@ -2878,6 +2898,7 @@ static void test_long_data_str() MYSQL_RES *result; MYSQL_BIND bind[2]; my_bool is_null[2]; + char query[MAX_TEST_QUERY_LENGTH]; myheader("test_long_data_str"); @@ -2970,6 +2991,7 @@ static void test_long_data_str1() MYSQL_RES *result; MYSQL_BIND bind[2]; MYSQL_FIELD *field; + char query[MAX_TEST_QUERY_LENGTH]; myheader("test_long_data_str1"); @@ -3125,6 +3147,7 @@ static void test_long_data_bin() long length; MYSQL_RES *result; MYSQL_BIND bind[2]; + char query[MAX_TEST_QUERY_LENGTH]; myheader("test_long_data_bin"); @@ -3204,6 +3227,7 @@ static void test_simple_delete() MYSQL_RES *result; MYSQL_BIND bind[2]; ulong length[2]; + char query[MAX_TEST_QUERY_LENGTH]; myheader("test_simple_delete"); @@ -3286,6 +3310,7 @@ static void test_update() MYSQL_RES *result; MYSQL_BIND bind[2]; ulong length[2]; + char query[MAX_TEST_QUERY_LENGTH]; myheader("test_update"); @@ -3382,6 +3407,7 @@ static void test_prepare_noparam() MYSQL_STMT *stmt; int rc; MYSQL_RES *result; + char query[MAX_TEST_QUERY_LENGTH]; myheader("test_prepare_noparam"); @@ -4238,6 +4264,7 @@ static void test_prepare_ext() short sData= 10; longlong bData= 20; MYSQL_BIND bind[6]; + char query[MAX_TEST_QUERY_LENGTH]; myheader("test_prepare_ext"); rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_prepare_ext"); @@ -4625,6 +4652,7 @@ static void test_stmt_close() MYSQL_RES *result; unsigned int count; int rc; + char query[MAX_TEST_QUERY_LENGTH]; myheader("test_stmt_close"); @@ -5271,6 +5299,7 @@ static void test_manual_sample() ulonglong affected_rows; MYSQL_BIND bind[3]; my_bool is_null; + char query[MAX_TEST_QUERY_LENGTH]; myheader("test_manual_sample"); @@ -5625,6 +5654,7 @@ static void test_prepare_multi_statements() { MYSQL *mysql_local; MYSQL_STMT *stmt; + char query[MAX_TEST_QUERY_LENGTH]; myheader("test_prepare_multi_statements"); if (!(mysql_local= mysql_init(NULL))) @@ -5842,6 +5872,7 @@ static void test_store_result2() int nData; ulong length; MYSQL_BIND bind[1]; + char query[MAX_TEST_QUERY_LENGTH]; myheader("test_store_result2"); @@ -7121,6 +7152,7 @@ static void test_set_option() static void test_prepare_grant() { int rc; + char query[MAX_TEST_QUERY_LENGTH]; myheader("test_prepare_grant"); @@ -8593,6 +8625,7 @@ static void test_sqlmode() MYSQL_BIND bind[2]; char c1[5], c2[5]; int rc; + char query[MAX_TEST_QUERY_LENGTH]; myheader("test_sqlmode"); @@ -8736,6 +8769,7 @@ static void test_ts() ulong length; int rc, field_count; char name; + char query[MAX_TEST_QUERY_LENGTH]; myheader("test_ts"); @@ -15027,6 +15061,65 @@ static void test_bug20152() } } +/* Bug#15752 "Lost connection to MySQL server when calling a SP from C API" */ + +static void test_bug15752() +{ + MYSQL mysql_local; + int rc, i; + const int ITERATION_COUNT= 100; + char *query= "CALL p1()"; + + myheader("test_bug15752"); + + rc= mysql_query(mysql, "drop procedure if exists p1"); + myquery(rc); + rc= mysql_query(mysql, "create procedure p1() select 1"); + myquery(rc); + + mysql_init(&mysql_local); + if (! mysql_real_connect(&mysql_local, opt_host, opt_user, + opt_password, current_db, opt_port, + opt_unix_socket, + CLIENT_MULTI_STATEMENTS|CLIENT_MULTI_RESULTS)) + { + printf("Unable connect to MySQL server: %s\n", mysql_error(&mysql_local)); + DIE_UNLESS(0); + } + rc= mysql_real_query(&mysql_local, query, strlen(query)); + myquery(rc); + mysql_free_result(mysql_store_result(&mysql_local)); + + rc= mysql_real_query(&mysql_local, query, strlen(query)); + DIE_UNLESS(rc && mysql_errno(&mysql_local) == CR_COMMANDS_OUT_OF_SYNC); + + if (! opt_silent) + printf("Got error (as expected): %s\n", mysql_error(&mysql_local)); + + /* Check some other commands too */ + + DIE_UNLESS(mysql_next_result(&mysql_local) == 0); + mysql_free_result(mysql_store_result(&mysql_local)); + DIE_UNLESS(mysql_next_result(&mysql_local) == -1); + + /* The second problem is not reproducible: add the test case */ + for (i = 0; i < ITERATION_COUNT; i++) + { + if (mysql_real_query(&mysql_local, query, strlen(query))) + { + printf("\ni=%d %s failed: %s\n", i, query, mysql_error(&mysql_local)); + break; + } + mysql_free_result(mysql_store_result(&mysql_local)); + DIE_UNLESS(mysql_next_result(&mysql_local) == 0); + mysql_free_result(mysql_store_result(&mysql_local)); + DIE_UNLESS(mysql_next_result(&mysql_local) == -1); + + } + mysql_close(&mysql_local); + rc= mysql_query(mysql, "drop procedure p1"); + myquery(rc); +} /* Bug#21206: memory corruption when too many cursors are opened at once @@ -15342,7 +15435,8 @@ static struct my_tests_st my_tests[]= { { "test_bug20152", test_bug20152 }, { "test_bug14169", test_bug14169 }, { "test_bug17667", test_bug17667 }, - { "test_bug19671", test_bug19671}, + { "test_bug19671", test_bug19671 }, + { "test_bug15752", test_bug15752 }, { "test_bug21206", test_bug21206}, { 0, 0 } }; |