From 0fd784928c340968967a104dd0191837549ee8a3 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Feb 2006 13:00:32 +0100 Subject: [PATCH 1/4] Fixed BUG#16896: Stored function: unused AGGREGATE-clause in CREATE FUNCTION Check if AGGREGATE was given with a stored (non-UDF) function, and return error in that case. Also made udf_example/udf_test work again, by adding a missing *_init() function. (_init() functions required unless --allow_suspicious_udfs is given to the server, since March 2005 - it seems udf_example wasn't updated at the time.) mysql-test/r/sp-error.result: Updated results for BUG#16896. mysql-test/t/sp-error.test: Added test case for BUG#16896. sql/share/errmsg.txt: New error message: ER_SP_NO_AGGREGATE sql/sql_yacc.yy: Check if AGGREGATE was used when creating a stored function (i.e. not an UDF). sql/udf_example.cc: Added myfunc_int_init() function to make it work when the server is running without --allow_suspicious_udfs. --- mysql-test/r/sp-error.result | 3 +++ mysql-test/t/sp-error.test | 12 ++++++++++++ sql/share/errmsg.txt | 2 ++ sql/sql_yacc.yy | 10 ++++++++++ sql/udf_example.cc | 9 +++++++++ 5 files changed, 36 insertions(+) diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result index 2766dca5845..c0e02cbeb6f 100644 --- a/mysql-test/r/sp-error.result +++ b/mysql-test/r/sp-error.result @@ -1176,3 +1176,6 @@ end| call bug15091(); ERROR 42S02: Unknown table 'c' in field list 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 diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test index e1839b4b98f..c4745537de3 100644 --- a/mysql-test/t/sp-error.test +++ b/mysql-test/t/sp-error.test @@ -1703,6 +1703,17 @@ call bug15091(); drop procedure bug15091; +# +# BUG#16896: Stored function: unused AGGREGATE-clause in CREATE FUNCTION +# +--disable_warnings +drop function if exists bug16896; +--enable_warnings + +--error ER_SP_NO_AGGREGATE +create aggregate function bug16896() returns int return 1; + + # # BUG#NNNN: New bug synopsis # @@ -1710,3 +1721,4 @@ drop procedure bug15091; #drop procedure if exists bugNNNN| #--enable_warnings #create procedure bugNNNN... + diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 8017ba3ef9f..1315de6528a 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5607,3 +5607,5 @@ ER_SP_PROC_TABLE_CORRUPT eng "Failed to load routine %s. The table mysql.proc is missing, corrupt, or contains bad data (internal code %d)" ER_SP_WRONG_NAME 42000 eng "Incorrect routine name '%-.64s'" +ER_SP_NO_AGGREGATE 42000 + eng "AGGREGATE is not supported for stored functions" diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index ebb97cde3e4..4f4ec5e92de 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1326,6 +1326,16 @@ create_function_tail: LEX *lex= Lex; sp_head *sp; + /* + First check if AGGREGATE was used, in that case it's a + syntax error. + */ + if (lex->udf.type == UDFTYPE_AGGREGATE) + { + my_error(ER_SP_NO_AGGREGATE, MYF(0)); + YYABORT; + } + if (lex->sphead) { my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "FUNCTION"); diff --git a/sql/udf_example.cc b/sql/udf_example.cc index a186b4fbf6c..35833e63fab 100644 --- a/sql/udf_example.cc +++ b/sql/udf_example.cc @@ -144,6 +144,7 @@ char *metaphon(UDF_INIT *initid, UDF_ARGS *args, char *result, my_bool myfunc_double_init(UDF_INIT *, UDF_ARGS *args, char *message); double myfunc_double(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error); +my_bool myfunc_int_init(UDF_INIT *initid, UDF_ARGS *args, char *message); longlong myfunc_int(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error); my_bool sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message); @@ -597,6 +598,14 @@ longlong myfunc_int(UDF_INIT *initid, UDF_ARGS *args, char *is_null, return val; } +/* + At least one of _init/_deinit is needed unless the server is started + with --allow_suspicious_udfs. +*/ +my_bool myfunc_int_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + return 0; +} /* Simple example of how to get a sequences starting from the first argument From 4628da295f421042f9aa3b24e307f5d20133528b Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 15 Feb 2006 17:28:34 +0100 Subject: [PATCH 2/4] Additional tests for nested handlers added to sp.test. A follow-up to BUG#15011 (already fixed). mysql-test/r/sp.result: Updated results for new handler tests. mysql-test/t/sp.test: Additional tests for nested handlers. --- mysql-test/r/sp.result | 335 ++++++++++++++++++++++++++++++++++- mysql-test/t/sp.test | 393 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 725 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index bec2f049bc4..61c977f1a5c 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -1413,8 +1413,6 @@ select `foo` ()| 5 drop function `foo`| drop function if exists t1max| -Warnings: -Note 1305 FUNCTION t1max does not exist create function t1max() returns int begin declare x int; @@ -1470,6 +1468,339 @@ zip 3 foo 1 drop table t3| drop function getcount| +drop table if exists t3| +drop procedure if exists h_ee| +drop procedure if exists h_es| +drop procedure if exists h_en| +drop procedure if exists h_ew| +drop procedure if exists h_ex| +drop procedure if exists h_se| +drop procedure if exists h_ss| +drop procedure if exists h_sn| +drop procedure if exists h_sw| +drop procedure if exists h_sx| +drop procedure if exists h_ne| +drop procedure if exists h_ns| +drop procedure if exists h_nn| +drop procedure if exists h_we| +drop procedure if exists h_ws| +drop procedure if exists h_ww| +drop procedure if exists h_xe| +drop procedure if exists h_xs| +drop procedure if exists h_xx| +create table t3 (a smallint primary key)| +insert into t3 (a) values (1)| +create procedure h_ee() +deterministic +begin +declare continue handler for 1062 -- ER_DUP_ENTRY +select 'Outer (bad)' as 'h_ee'; +begin +declare continue handler for 1062 -- ER_DUP_ENTRY +select 'Inner (good)' as 'h_ee'; +insert into t3 values (1); +end; +end| +create procedure h_es() +deterministic +begin +declare continue handler for 1062 -- ER_DUP_ENTRY +select 'Outer (good)' as 'h_es'; +begin +-- integrity constraint violation +declare continue handler for sqlstate '23000' + select 'Inner (bad)' as 'h_es'; +insert into t3 values (1); +end; +end| +create procedure h_en() +deterministic +begin +declare continue handler for 1329 -- ER_SP_FETCH_NO_DATA +select 'Outer (good)' as 'h_en'; +begin +declare x int; +declare continue handler for sqlstate '02000' -- no data +select 'Inner (bad)' as 'h_en'; +select a into x from t3 where a = 42; +end; +end| +create procedure h_ew() +deterministic +begin +declare continue handler for 1264 -- ER_WARN_DATA_OUT_OF_RANGE +select 'Outer (good)' as 'h_ew'; +begin +declare continue handler for sqlwarning +select 'Inner (bad)' as 'h_ew'; +insert into t3 values (123456789012); +end; +delete from t3; +insert into t3 values (1); +end| +create procedure h_ex() +deterministic +begin +declare continue handler for 1062 -- ER_DUP_ENTRY +select 'Outer (good)' as 'h_ex'; +begin +declare continue handler for sqlexception +select 'Inner (bad)' as 'h_ex'; +insert into t3 values (1); +end; +end| +create procedure h_se() +deterministic +begin +-- integrity constraint violation +declare continue handler for sqlstate '23000' +select 'Outer (bad)' as 'h_se'; +begin +declare continue handler for 1062 -- ER_DUP_ENTRY +select 'Inner (good)' as 'h_se'; +insert into t3 values (1); +end; +end| +create procedure h_ss() +deterministic +begin +-- integrity constraint violation +declare continue handler for sqlstate '23000' +select 'Outer (bad)' as 'h_ss'; +begin +-- integrity constraint violation +declare continue handler for sqlstate '23000' +select 'Inner (good)' as 'h_ss'; +insert into t3 values (1); +end; +end| +create procedure h_sn() +deterministic +begin +-- Note: '02000' is more specific than NOT FOUND ; +-- there might be other not found states +declare continue handler for sqlstate '02000' -- no data +select 'Outer (good)' as 'h_sn'; +begin +declare x int; +declare continue handler for not found +select 'Inner (bad)' as 'h_sn'; +select a into x from t3 where a = 42; +end; +end| +create procedure h_sw() +deterministic +begin +-- data exception - numeric value out of range +declare continue handler for sqlstate '22003' + select 'Outer (good)' as 'h_sw'; +begin +declare continue handler for sqlwarning +select 'Inner (bad)' as 'h_sw'; +insert into t3 values (123456789012); +end; +delete from t3; +insert into t3 values (1); +end| +create procedure h_sx() +deterministic +begin +-- integrity constraint violation +declare continue handler for sqlstate '23000' +select 'Outer (good)' as 'h_sx'; +begin +declare continue handler for sqlexception +select 'Inner (bad)' as 'h_sx'; +insert into t3 values (1); +end; +end| +create procedure h_ne() +deterministic +begin +declare continue handler for not found +select 'Outer (bad)' as 'h_ne'; +begin +declare x int; +declare continue handler for 1329 -- ER_SP_FETCH_NO_DATA +select 'Inner (good)' as 'h_ne'; +select a into x from t3 where a = 42; +end; +end| +create procedure h_ns() +deterministic +begin +declare continue handler for not found +select 'Outer (bad)' as 'h_ns'; +begin +declare x int; +declare continue handler for sqlstate '02000' -- no data +select 'Inner (good)' as 'h_ns'; +select a into x from t3 where a = 42; +end; +end| +create procedure h_nn() +deterministic +begin +declare continue handler for not found +select 'Outer (bad)' as 'h_nn'; +begin +declare x int; +declare continue handler for not found +select 'Inner (good)' as 'h_nn'; +select a into x from t3 where a = 42; +end; +end| +create procedure h_we() +deterministic +begin +declare continue handler for sqlwarning +select 'Outer (bad)' as 'h_we'; +begin +declare continue handler for 1264 -- ER_WARN_DATA_OUT_OF_RANGE +select 'Inner (good)' as 'h_we'; +insert into t3 values (123456789012); +end; +delete from t3; +insert into t3 values (1); +end| +create procedure h_ws() +deterministic +begin +declare continue handler for sqlwarning +select 'Outer (bad)' as 'h_ws'; +begin +-- data exception - numeric value out of range +declare continue handler for sqlstate '22003' + select 'Inner (good)' as 'h_ws'; +insert into t3 values (123456789012); +end; +delete from t3; +insert into t3 values (1); +end| +create procedure h_ww() +deterministic +begin +declare continue handler for sqlwarning +select 'Outer (bad)' as 'h_ww'; +begin +declare continue handler for sqlwarning +select 'Inner (good)' as 'h_ww'; +insert into t3 values (123456789012); +end; +delete from t3; +insert into t3 values (1); +end| +create procedure h_xe() +deterministic +begin +declare continue handler for sqlexception +select 'Outer (bad)' as 'h_xe'; +begin +declare continue handler for 1062 -- ER_DUP_ENTRY +select 'Inner (good)' as 'h_xe'; +insert into t3 values (1); +end; +end| +create procedure h_xs() +deterministic +begin +declare continue handler for sqlexception +select 'Outer (bad)' as 'h_xs'; +begin +-- integrity constraint violation +declare continue handler for sqlstate '23000' + select 'Inner (good)' as 'h_xs'; +insert into t3 values (1); +end; +end| +create procedure h_xx() +deterministic +begin +declare continue handler for sqlexception +select 'Outer (bad)' as 'h_xx'; +begin +declare continue handler for sqlexception +select 'Inner (good)' as 'h_xx'; +insert into t3 values (1); +end; +end| +call h_ee()| +h_ee +Inner (good) +call h_es()| +h_es +Outer (good) +call h_en()| +h_en +Outer (good) +call h_ew()| +h_ew +Outer (good) +call h_ex()| +h_ex +Outer (good) +call h_se()| +h_se +Inner (good) +call h_ss()| +h_ss +Inner (good) +call h_sn()| +h_sn +Outer (good) +call h_sw()| +h_sw +Outer (good) +call h_sx()| +h_sx +Outer (good) +call h_ne()| +h_ne +Inner (good) +call h_ns()| +h_ns +Inner (good) +call h_nn()| +h_nn +Inner (good) +call h_we()| +h_we +Inner (good) +call h_ws()| +h_ws +Inner (good) +call h_ww()| +h_ww +Inner (good) +call h_xe()| +h_xe +Inner (good) +call h_xs()| +h_xs +Inner (good) +call h_xx()| +h_xx +Inner (good) +drop table t3| +drop procedure h_ee| +drop procedure h_es| +drop procedure h_en| +drop procedure h_ew| +drop procedure h_ex| +drop procedure h_se| +drop procedure h_ss| +drop procedure h_sn| +drop procedure h_sw| +drop procedure h_sx| +drop procedure h_ne| +drop procedure h_ns| +drop procedure h_nn| +drop procedure h_we| +drop procedure h_ws| +drop procedure h_ww| +drop procedure h_xe| +drop procedure h_xs| +drop procedure h_xx| drop procedure if exists bug822| create procedure bug822(a_id char(16), a_data int) begin diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 53c15ffd05b..bc0b6f654b8 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -1660,7 +1660,7 @@ drop function `foo`| # Implicit LOCK/UNLOCK TABLES for table access in functions # ---disable_warning +--disable_warnings drop function if exists t1max| --enable_warnings create function t1max() returns int @@ -1704,6 +1704,397 @@ drop table t3| drop function getcount| +# Test cases for different combinations of condition handlers in nested +# begin-end blocks in stored procedures. +# +# Note that the standard specifies that the most specific handler should +# be triggered even if it's an outer handler masked by a less specific +# handler in an inner block. +# Note also that '02000' is more specific than NOT FOUND; there might be +# other '02xxx' states, even if we currently do not issue them in any +# situation (e.g. '02001'). +# +# The combinations we test are these: +# +# Inner +# errcode sqlstate not found sqlwarning sqlexception +# Outer +------------+------------+------------+------------+------------+ +#errcode | h_ee (i) | h_es (o) | h_en (o) | h_ew (o) | h_ex (o) | +#sqlstate | h_se (i) | h_ss (i) | h_sn (o) | h_sw (o) | h_sx (o) | +#not found | h_ne (i) | h_ns (i) | h_nn (i) | | | +#sqlwarning | h_we (i) | h_ws (i) | | h_ww (i) | | +#sqlexception | h_xe (i) | h_xs (i) | | | h_xx (i) | +# +------------+---------------------------------------------------+ +# +# (i) means that the inner handler is the one that should be invoked, +# (o) means that the outer handler should be invoked. +# +# ('not found', 'sqlwarning' and 'sqlexception' are mutually exclusive, hence +# no tests for those combinations.) +# + +--disable_warnings +drop table if exists t3| +drop procedure if exists h_ee| +drop procedure if exists h_es| +drop procedure if exists h_en| +drop procedure if exists h_ew| +drop procedure if exists h_ex| +drop procedure if exists h_se| +drop procedure if exists h_ss| +drop procedure if exists h_sn| +drop procedure if exists h_sw| +drop procedure if exists h_sx| +drop procedure if exists h_ne| +drop procedure if exists h_ns| +drop procedure if exists h_nn| +drop procedure if exists h_we| +drop procedure if exists h_ws| +drop procedure if exists h_ww| +drop procedure if exists h_xe| +drop procedure if exists h_xs| +drop procedure if exists h_xx| +--enable_warnings + +# smallint - to get out of range warnings +# primary key - to get constraint errors +create table t3 (a smallint primary key)| + +insert into t3 (a) values (1)| + +create procedure h_ee() + deterministic +begin + declare continue handler for 1062 -- ER_DUP_ENTRY + select 'Outer (bad)' as 'h_ee'; + + begin + declare continue handler for 1062 -- ER_DUP_ENTRY + select 'Inner (good)' as 'h_ee'; + + insert into t3 values (1); + end; +end| + +create procedure h_es() + deterministic +begin + declare continue handler for 1062 -- ER_DUP_ENTRY + select 'Outer (good)' as 'h_es'; + + begin + -- integrity constraint violation + declare continue handler for sqlstate '23000' + select 'Inner (bad)' as 'h_es'; + + insert into t3 values (1); + end; +end| + +create procedure h_en() + deterministic +begin + declare continue handler for 1329 -- ER_SP_FETCH_NO_DATA + select 'Outer (good)' as 'h_en'; + + begin + declare x int; + declare continue handler for sqlstate '02000' -- no data + select 'Inner (bad)' as 'h_en'; + + select a into x from t3 where a = 42; + end; +end| + +create procedure h_ew() + deterministic +begin + declare continue handler for 1264 -- ER_WARN_DATA_OUT_OF_RANGE + select 'Outer (good)' as 'h_ew'; + + begin + declare continue handler for sqlwarning + select 'Inner (bad)' as 'h_ew'; + + insert into t3 values (123456789012); + end; + delete from t3; + insert into t3 values (1); +end| + +create procedure h_ex() + deterministic +begin + declare continue handler for 1062 -- ER_DUP_ENTRY + select 'Outer (good)' as 'h_ex'; + + begin + declare continue handler for sqlexception + select 'Inner (bad)' as 'h_ex'; + + insert into t3 values (1); + end; +end| + +create procedure h_se() + deterministic +begin + -- integrity constraint violation + declare continue handler for sqlstate '23000' + select 'Outer (bad)' as 'h_se'; + + begin + declare continue handler for 1062 -- ER_DUP_ENTRY + select 'Inner (good)' as 'h_se'; + + insert into t3 values (1); + end; +end| + +create procedure h_ss() + deterministic +begin + -- integrity constraint violation + declare continue handler for sqlstate '23000' + select 'Outer (bad)' as 'h_ss'; + + begin + -- integrity constraint violation + declare continue handler for sqlstate '23000' + select 'Inner (good)' as 'h_ss'; + + insert into t3 values (1); + end; +end| + +create procedure h_sn() + deterministic +begin + -- Note: '02000' is more specific than NOT FOUND ; + -- there might be other not found states + declare continue handler for sqlstate '02000' -- no data + select 'Outer (good)' as 'h_sn'; + + begin + declare x int; + declare continue handler for not found + select 'Inner (bad)' as 'h_sn'; + + select a into x from t3 where a = 42; + end; +end| + +create procedure h_sw() + deterministic +begin + -- data exception - numeric value out of range + declare continue handler for sqlstate '22003' + select 'Outer (good)' as 'h_sw'; + + begin + declare continue handler for sqlwarning + select 'Inner (bad)' as 'h_sw'; + + insert into t3 values (123456789012); + end; + delete from t3; + insert into t3 values (1); +end| + +create procedure h_sx() + deterministic +begin + -- integrity constraint violation + declare continue handler for sqlstate '23000' + select 'Outer (good)' as 'h_sx'; + + begin + declare continue handler for sqlexception + select 'Inner (bad)' as 'h_sx'; + + insert into t3 values (1); + end; +end| + +create procedure h_ne() + deterministic +begin + declare continue handler for not found + select 'Outer (bad)' as 'h_ne'; + + begin + declare x int; + declare continue handler for 1329 -- ER_SP_FETCH_NO_DATA + select 'Inner (good)' as 'h_ne'; + + select a into x from t3 where a = 42; + end; +end| + +create procedure h_ns() + deterministic +begin + declare continue handler for not found + select 'Outer (bad)' as 'h_ns'; + + begin + declare x int; + declare continue handler for sqlstate '02000' -- no data + select 'Inner (good)' as 'h_ns'; + + select a into x from t3 where a = 42; + end; +end| + +create procedure h_nn() + deterministic +begin + declare continue handler for not found + select 'Outer (bad)' as 'h_nn'; + + begin + declare x int; + declare continue handler for not found + select 'Inner (good)' as 'h_nn'; + + select a into x from t3 where a = 42; + end; +end| + +create procedure h_we() + deterministic +begin + declare continue handler for sqlwarning + select 'Outer (bad)' as 'h_we'; + + begin + declare continue handler for 1264 -- ER_WARN_DATA_OUT_OF_RANGE + select 'Inner (good)' as 'h_we'; + + insert into t3 values (123456789012); + end; + delete from t3; + insert into t3 values (1); +end| + +create procedure h_ws() + deterministic +begin + declare continue handler for sqlwarning + select 'Outer (bad)' as 'h_ws'; + + begin + -- data exception - numeric value out of range + declare continue handler for sqlstate '22003' + select 'Inner (good)' as 'h_ws'; + + insert into t3 values (123456789012); + end; + delete from t3; + insert into t3 values (1); +end| + +create procedure h_ww() + deterministic +begin + declare continue handler for sqlwarning + select 'Outer (bad)' as 'h_ww'; + + begin + declare continue handler for sqlwarning + select 'Inner (good)' as 'h_ww'; + + insert into t3 values (123456789012); + end; + delete from t3; + insert into t3 values (1); +end| + +create procedure h_xe() + deterministic +begin + declare continue handler for sqlexception + select 'Outer (bad)' as 'h_xe'; + + begin + declare continue handler for 1062 -- ER_DUP_ENTRY + select 'Inner (good)' as 'h_xe'; + + insert into t3 values (1); + end; +end| + +create procedure h_xs() + deterministic +begin + declare continue handler for sqlexception + select 'Outer (bad)' as 'h_xs'; + + begin + -- integrity constraint violation + declare continue handler for sqlstate '23000' + select 'Inner (good)' as 'h_xs'; + + insert into t3 values (1); + end; +end| + +create procedure h_xx() + deterministic +begin + declare continue handler for sqlexception + select 'Outer (bad)' as 'h_xx'; + + begin + declare continue handler for sqlexception + select 'Inner (good)' as 'h_xx'; + + insert into t3 values (1); + end; +end| + +call h_ee()| +call h_es()| +call h_en()| +call h_ew()| +call h_ex()| +call h_se()| +call h_ss()| +call h_sn()| +call h_sw()| +call h_sx()| +call h_ne()| +call h_ns()| +call h_nn()| +call h_we()| +call h_ws()| +call h_ww()| +call h_xe()| +call h_xs()| +call h_xx()| + +drop table t3| +drop procedure h_ee| +drop procedure h_es| +drop procedure h_en| +drop procedure h_ew| +drop procedure h_ex| +drop procedure h_se| +drop procedure h_ss| +drop procedure h_sn| +drop procedure h_sw| +drop procedure h_sx| +drop procedure h_ne| +drop procedure h_ns| +drop procedure h_nn| +drop procedure h_we| +drop procedure h_ws| +drop procedure h_ww| +drop procedure h_xe| +drop procedure h_xs| +drop procedure h_xx| + + # # Test cases for old bugs # From e1c8d9c9b4f4d7592f088e38a8374ab844cd51ee Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 16 Feb 2006 16:19:24 +0300 Subject: [PATCH 3/4] Fix for bug #16593 "Deadlock or crash in stress test for case where trigger starts trigger". In short, the deadlock/crash happened when execution of statement, which used stored functions or activated triggers, coincided with alteration of the tables used by these functions or triggers (in highly concurrent environment). Bug was caused by the incorrect handling of tables from prelocked set in open_tables() functions in situations when refresh happened. This fix replaces old smart but not very robust way of handling tables after refresh (which was closing only old tables), with new one which simply closes all tables opened so far and restarts open_tables(). Also fixed handling of temporary tables in close_tables_for_reopen(). No test case present since bug manifests itself only in concurrent environment. sql/mysql_priv.h: In order to handle correctly case when table list completely consists from tables from prelocked set close_tables_for_reopen() have to accept table list as in/out parameter. sql/sql_base.cc: open_tables(): Removed part of comment which was out of date. Changed handling of case when refresh happens during opening of tables, now instead of having code which decides for each table if it should be closed we simply close all tables. Old code also incorrectly handled tables from prelocked set in this situation which resulted in bug #16593 "Deadlock or crash in stress test for case where triggers starting trigger". close_tables_for_reopen(): Now we correctly handle the case when table list completely consists from tables from prelocked set. Also now we simply close all tables instead leaving temporary tables non-closed (such approach allows easily handle correctly tables from prelocked set). sql/sql_prepare.cc: In order to handle correctly case when table list completely consists from tables from prelocked set close_tables_for_reopen() have to accept table list as in/out parameter. sql/sql_update.cc: In order to handle correctly case when table list completely consists from tables from prelocked set close_tables_for_reopen() have to accept table list as in/out parameter. --- sql/mysql_priv.h | 2 +- sql/sql_base.cc | 84 +++++++++++++++------------------------------- sql/sql_prepare.cc | 2 +- sql/sql_update.cc | 4 +-- 4 files changed, 31 insertions(+), 61 deletions(-) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index ca2cda0f7c6..6908f2b9049 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -981,7 +981,7 @@ void free_io_cache(TABLE *entry); void intern_close_table(TABLE *entry); bool close_thread_table(THD *thd, TABLE **table_ptr); void close_temporary_tables(THD *thd); -void close_tables_for_reopen(THD *thd, TABLE_LIST *tables); +void close_tables_for_reopen(THD *thd, TABLE_LIST **tables); TABLE_LIST *find_table_in_list(TABLE_LIST *table, uint offset_to_list, const char *db_name, diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 9d1720cc527..37d4c80a0d0 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1982,22 +1982,11 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) statement for which table list for prelocking is already built, let us cache routines and try to build such table list. - NOTE: We can't delay prelocking until we will met some sub-statement - which really uses tables, since this will imply that we have to restore - its table list to be able execute it in some other context. - And current views implementation assumes that view tables are added to - global table list only once during PS preparing/first SP execution. - Also locking at earlier stage is probably faster altough may decrease - concurrency a bit. - NOTE: We will mark statement as requiring prelocking only if we will have non empty table list. But this does not guarantee that in prelocked mode we will have some locked tables, because queries which use only derived/information schema tables and views possible. Thus "counter" may be still zero for prelocked statement... - - NOTE: The above notes may be out of date. Please wait for psergey to - document new prelocked behavior. */ if (!thd->prelocked_mode && !thd->lex->requires_prelocking() && @@ -2083,48 +2072,23 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) if (refresh) // Refresh in progress { - /* close all 'old' tables used by this thread */ - pthread_mutex_lock(&LOCK_open); - // if query_id is not reset, we will get an error - // re-opening a temp table - thd->version=refresh_version; - TABLE **prev_table= &thd->open_tables; - bool found=0; - for (TABLE_LIST *tmp= *start; tmp; tmp= tmp->next_global) - { - /* Close normal (not temporary) changed tables */ - if (tmp->table && ! tmp->table->s->tmp_table) - { - if (tmp->table->s->version != refresh_version || - ! tmp->table->db_stat) - { - VOID(hash_delete(&open_cache,(byte*) tmp->table)); - tmp->table=0; - found=1; - } - else - { - *prev_table= tmp->table; // Relink open list - prev_table= &tmp->table->next; - } - } - } - *prev_table=0; - pthread_mutex_unlock(&LOCK_open); - if (found) - VOID(pthread_cond_broadcast(&COND_refresh)); // Signal to refresh /* - Let us prepare for recalculation of set of prelocked tables. - First we pretend that we have finished calculation which we - were doing currently. Then we restore list of tables to be - opened and set of used routines to the state in which they were - before first open_tables() call for this statement (i.e. before - we have calculated current set of tables for prelocking). + We have met name-locked or old version of table. Now we have + to close all tables which are not up to date. We also have to + throw away set of prelocked tables (and thus close tables from + this set that were open by now) since it possible that one of + tables which determined its content was changed. + + Instead of implementing complex/non-robust logic mentioned + above we simply close and then reopen all tables. + + In order to prepare for recalculation of set of prelocked tables + we pretend that we have finished calculation which we were doing + currently. */ if (query_tables_last_own) thd->lex->mark_as_requiring_prelocking(query_tables_last_own); - thd->lex->chop_off_not_own_tables(); - sp_remove_not_own_routines(thd->lex); + close_tables_for_reopen(thd, start); goto restart; } result= -1; // Fatal error @@ -2335,7 +2299,7 @@ int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables) break; if (!need_reopen) DBUG_RETURN(-1); - close_tables_for_reopen(thd, tables); + close_tables_for_reopen(thd, &tables); } DBUG_RETURN(0); } @@ -2372,7 +2336,7 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables) break; if (!need_reopen) DBUG_RETURN(-1); - close_tables_for_reopen(thd, tables); + close_tables_for_reopen(thd, &tables); } if (mysql_handle_derived(thd->lex, &mysql_derived_prepare) || (thd->fill_derived_tables() && @@ -2600,18 +2564,24 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) SYNOPSIS close_tables_for_reopen() - thd Thread context - tables List of tables which we were trying to open and lock + thd in Thread context + tables in/out List of tables which we were trying to open and lock */ -void close_tables_for_reopen(THD *thd, TABLE_LIST *tables) +void close_tables_for_reopen(THD *thd, TABLE_LIST **tables) { + /* + If table list consists only from tables from prelocking set, table list + for new attempt should be empty, so we have to update list's root pointer. + */ + if (thd->lex->first_not_own_table() == *tables) + *tables= 0; thd->lex->chop_off_not_own_tables(); sp_remove_not_own_routines(thd->lex); - for (TABLE_LIST *tmp= tables; tmp; tmp= tmp->next_global) - if (tmp->table && !tmp->table->s->tmp_table) - tmp->table= 0; + for (TABLE_LIST *tmp= *tables; tmp; tmp= tmp->next_global) + tmp->table= 0; + mark_used_tables_as_free_for_reuse(thd, thd->temporary_tables); close_thread_tables(thd); } diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 68f033f78fe..b0c4cef6e87 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1135,7 +1135,7 @@ static int mysql_test_update(Prepared_statement *stmt, break; if (!need_reopen) goto error; - close_tables_for_reopen(thd, table_list); + close_tables_for_reopen(thd, &table_list); } /* diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 2ff8a4bc244..324623a61ca 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -158,7 +158,7 @@ int mysql_update(THD *thd, break; if (!need_reopen) DBUG_RETURN(1); - close_tables_for_reopen(thd, table_list); + close_tables_for_reopen(thd, &table_list); } if (mysql_handle_derived(thd->lex, &mysql_derived_prepare) || @@ -813,7 +813,7 @@ reopen_tables: for (TABLE_LIST *tbl= table_list; tbl; tbl= tbl->next_global) tbl->cleanup_items(); - close_tables_for_reopen(thd, table_list); + close_tables_for_reopen(thd, &table_list); goto reopen_tables; } From 85e54a08c4fc468ab89f8df27bc981027cb22e6c Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 22 Feb 2006 13:44:04 +0300 Subject: [PATCH 4/4] Fix for BUG#7787: Stored procedures: improper warning for "grant execute" statement. The problem was that error flag was not reset. mysql-test/r/sp-security.result: Results for test case for BUG#7787. mysql-test/t/sp-security.test: A test case for BUG#7787. sql/sp.cc: Reset errors after sp_find_routine(). --- mysql-test/r/sp-security.result | 9 +++++++++ mysql-test/t/sp-security.test | 22 ++++++++++++++++++++++ sql/sp.cc | 11 +++++++---- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/sp-security.result b/mysql-test/r/sp-security.result index ff729e87f97..d8abc387677 100644 --- a/mysql-test/r/sp-security.result +++ b/mysql-test/r/sp-security.result @@ -314,3 +314,12 @@ select * from db_bug14533.t1; ERROR 42000: SELECT command denied to user 'user_bug14533'@'localhost' for table 't1' drop user user_bug14533@localhost; drop database db_bug14533; +CREATE DATABASE db_bug7787; +use db_bug7787; +CREATE PROCEDURE p1() +SHOW INNODB STATUS; +Warnings: +Warning 1287 'SHOW INNODB STATUS' is deprecated; use 'SHOW ENGINE INNODB STATUS' instead +GRANT EXECUTE ON PROCEDURE p1 TO user_bug7787@localhost; +DROP DATABASE db_bug7787; +use test; diff --git a/mysql-test/t/sp-security.test b/mysql-test/t/sp-security.test index 90160780618..19f94a32d9c 100644 --- a/mysql-test/t/sp-security.test +++ b/mysql-test/t/sp-security.test @@ -525,4 +525,26 @@ disconnect user_bug14533; drop user user_bug14533@localhost; drop database db_bug14533; + +# +# BUG#7787: Stored procedures: improper warning for "grant execute" statement +# + +# Prepare. + +CREATE DATABASE db_bug7787; +use db_bug7787; + +# Test. + +CREATE PROCEDURE p1() + SHOW INNODB STATUS; + +GRANT EXECUTE ON PROCEDURE p1 TO user_bug7787@localhost; + +# Cleanup. + +DROP DATABASE db_bug7787; +use test; + # End of 5.0 bugs. diff --git a/sql/sp.cc b/sql/sp.cc index fe249141fea..ce0282bf810 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -1012,6 +1012,7 @@ sp_exist_routines(THD *thd, TABLE_LIST *routines, bool any, bool no_error) { TABLE_LIST *routine; bool result= 0; + bool sp_object_found; DBUG_ENTER("sp_exists_routine"); for (routine= routines; routine; routine= routine->next_global) { @@ -1024,10 +1025,12 @@ sp_exist_routines(THD *thd, TABLE_LIST *routines, bool any, bool no_error) lex_name.str= thd->strmake(routine->table_name, lex_name.length); name= new sp_name(lex_db, lex_name); name->init_qname(thd); - if (sp_find_routine(thd, TYPE_ENUM_PROCEDURE, name, - &thd->sp_proc_cache, FALSE) != NULL || - sp_find_routine(thd, TYPE_ENUM_FUNCTION, name, - &thd->sp_func_cache, FALSE) != NULL) + sp_object_found= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, name, + &thd->sp_proc_cache, FALSE) != NULL || + sp_find_routine(thd, TYPE_ENUM_FUNCTION, name, + &thd->sp_func_cache, FALSE) != NULL; + mysql_reset_errors(thd, TRUE); + if (sp_object_found) { if (any) DBUG_RETURN(1);