From 17fc288b3054514f139b32c89c62905d374968e4 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Fri, 21 Jul 2017 13:53:58 +0300 Subject: [PATCH 1/6] MDEV-13352: Server crashes in st_join_table::remove_duplicates Do not run the window function computation step when the select produces no rows (zero_result_cause!=NULL). This may cause reads from uninitialized memory. We still need to run the window function computation step when the output includes just one row (for example SELECT MAX(col), RANK() OVER (...) FROM t1 WHERE 1=0). This fix also resolves an issue with queries with window functions producing an output row where should be none, like in SELECT ROW_NUMBER() FROM t1 WHERE 1=0. Updated a few test results in the existing tests to reflect this. --- mysql-test/r/win.result | 12 ++++++++++-- mysql-test/r/win_insert_select.result | 4 ---- mysql-test/t/win.test | 11 +++++++++++ sql/sql_select.cc | 8 +++++++- 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/win.result b/mysql-test/r/win.result index 7f8777ed491..862d711bce7 100644 --- a/mysql-test/r/win.result +++ b/mysql-test/r/win.result @@ -2762,10 +2762,8 @@ CREATE TABLE t1 (i INT); INSERT INTO t1 VALUES (3), (1), (2); SELECT i, ROW_NUMBER() OVER () FROM t1 WHERE 1 = 2; i ROW_NUMBER() OVER () -NULL 1 SELECT i, COUNT(*) OVER () FROM t1 WHERE 1 = 2; i COUNT(*) OVER () -NULL 1 DROP TABLE t1; # # MDEV-12051: window function in query with implicit grouping @@ -3141,3 +3139,13 @@ sum(i) over (order by i) interval(sum(i) over (order by i), 10, 20) 33 2 63 2 drop table t1; +# +# MDEV-13352: Server crashes in st_join_table::remove_duplicates +# +CREATE TABLE t1 (i INT); +INSERT INTO t1 VALUES (1),(2); +SELECT DISTINCT ROW_NUMBER() OVER(), i FROM t1 WHERE 0; +ROW_NUMBER() OVER() i +SELECT ROW_NUMBER() OVER(), i FROM t1 WHERE 0; +ROW_NUMBER() OVER() i +DROP TABLE t1; diff --git a/mysql-test/r/win_insert_select.result b/mysql-test/r/win_insert_select.result index c86576df6ae..93545e6de30 100644 --- a/mysql-test/r/win_insert_select.result +++ b/mysql-test/r/win_insert_select.result @@ -11,10 +11,6 @@ c1 c2 4 manual_insert_2 11 should repeat 4 times [11-14] 12 should repeat 4 times [11-14] -13 should repeat 4 times [11-14] -14 should repeat 4 times [11-14] -0 should_have_0 -2 should_have_2 DELETE FROM t1; EXECUTE populate_table; INSERT INTO t1 diff --git a/mysql-test/t/win.test b/mysql-test/t/win.test index aff717e3151..1db2354a442 100644 --- a/mysql-test/t/win.test +++ b/mysql-test/t/win.test @@ -1923,3 +1923,14 @@ insert into t1 values (1),(2),(10),(20),(30); select sum(i) over (order by i), interval(sum(i) over (order by i), 10, 20) from t1; drop table t1; + +--echo # +--echo # MDEV-13352: Server crashes in st_join_table::remove_duplicates +--echo # +CREATE TABLE t1 (i INT); +INSERT INTO t1 VALUES (1),(2); +SELECT DISTINCT ROW_NUMBER() OVER(), i FROM t1 WHERE 0; +SELECT ROW_NUMBER() OVER(), i FROM t1 WHERE 0; +DROP TABLE t1; + + diff --git a/sql/sql_select.cc b/sql/sql_select.cc index e8e399868db..90f5e11dd16 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -3398,8 +3398,14 @@ void JOIN::exec_inner() if (zero_result_cause) { - if (select_lex->have_window_funcs()) + if (select_lex->have_window_funcs() && send_row_on_empty_set()) { + /* + The query produces just one row but it has window functions. + + The only way to compute the value of window function(s) is to + run the entire window function computation step (there is no shortcut). + */ const_tables= table_count; first_select= sub_select_postjoin_aggr; } From e2afdb1ee430cb9d030aeeedc85eb903cda5e5d1 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Fri, 21 Jul 2017 19:06:01 +0300 Subject: [PATCH 2/6] MDEV-13344: Server crashes in in AGGR_OP::put_record on subquery Add a testcase --- mysql-test/r/win.result | 11 +++++++++++ mysql-test/t/win.test | 9 +++++++++ 2 files changed, 20 insertions(+) diff --git a/mysql-test/r/win.result b/mysql-test/r/win.result index 862d711bce7..71521e747ab 100644 --- a/mysql-test/r/win.result +++ b/mysql-test/r/win.result @@ -3149,3 +3149,14 @@ ROW_NUMBER() OVER() i SELECT ROW_NUMBER() OVER(), i FROM t1 WHERE 0; ROW_NUMBER() OVER() i DROP TABLE t1; +# +# MDEV-13344: Server crashes in in AGGR_OP::put_record on subquery +# with window function and constant table +# (Testcase only) +# +CREATE TABLE t1 (c CHAR(8)) ENGINE=MyISAM; +INSERT IGNORE INTO t1 VALUES ('foo'); +SELECT ('bar',1) IN ( SELECT c, ROW_NUMBER() OVER (PARTITION BY c) FROM t1); +('bar',1) IN ( SELECT c, ROW_NUMBER() OVER (PARTITION BY c) FROM t1) +0 +DROP TABLE t1; diff --git a/mysql-test/t/win.test b/mysql-test/t/win.test index 1db2354a442..be7049af8b4 100644 --- a/mysql-test/t/win.test +++ b/mysql-test/t/win.test @@ -1933,4 +1933,13 @@ SELECT DISTINCT ROW_NUMBER() OVER(), i FROM t1 WHERE 0; SELECT ROW_NUMBER() OVER(), i FROM t1 WHERE 0; DROP TABLE t1; +--echo # +--echo # MDEV-13344: Server crashes in in AGGR_OP::put_record on subquery +--echo # with window function and constant table +--echo # (Testcase only) +--echo # +CREATE TABLE t1 (c CHAR(8)) ENGINE=MyISAM; +INSERT IGNORE INTO t1 VALUES ('foo'); +SELECT ('bar',1) IN ( SELECT c, ROW_NUMBER() OVER (PARTITION BY c) FROM t1); +DROP TABLE t1; From 2a1035b004dfabcf3a7113be632b0030721a44d6 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Fri, 21 Jul 2017 20:09:19 +0300 Subject: [PATCH 3/6] MDEV-13351: Server crashes in st_select_lex::set_explain_type upon UNION with window function Make st_select_lex::set_explain_type() take into account that JOIN_TABs it is traversing may be also post-join aggregation JOIN_TABs (which have pos_in_table_list=NULL, etc). --- mysql-test/r/win.result | 13 +++++++++++++ mysql-test/t/win.test | 11 +++++++++++ sql/sql_lex.cc | 12 +++++++++--- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/win.result b/mysql-test/r/win.result index 71521e747ab..c6707bd51bc 100644 --- a/mysql-test/r/win.result +++ b/mysql-test/r/win.result @@ -3160,3 +3160,16 @@ SELECT ('bar',1) IN ( SELECT c, ROW_NUMBER() OVER (PARTITION BY c) FROM t1); ('bar',1) IN ( SELECT c, ROW_NUMBER() OVER (PARTITION BY c) FROM t1) 0 DROP TABLE t1; +# +# MDEV-13351: Server crashes in st_select_lex::set_explain_type upon UNION with window function +# +CREATE TABLE t1 (i INT); +INSERT INTO t1 VALUES (1),(2); +SELECT Nth_value(i,1) OVER() FROM t1 +UNION ALL +( SELECT Nth_value(i,2) OVER() FROM t1 LIMIT 0 ) +; +Nth_value(i,1) OVER() +1 +1 +DROP TABLE t1; diff --git a/mysql-test/t/win.test b/mysql-test/t/win.test index be7049af8b4..77ca755378d 100644 --- a/mysql-test/t/win.test +++ b/mysql-test/t/win.test @@ -1943,3 +1943,14 @@ INSERT IGNORE INTO t1 VALUES ('foo'); SELECT ('bar',1) IN ( SELECT c, ROW_NUMBER() OVER (PARTITION BY c) FROM t1); DROP TABLE t1; +--echo # +--echo # MDEV-13351: Server crashes in st_select_lex::set_explain_type upon UNION with window function +--echo # +CREATE TABLE t1 (i INT); +INSERT INTO t1 VALUES (1),(2); +SELECT Nth_value(i,1) OVER() FROM t1 +UNION ALL +( SELECT Nth_value(i,2) OVER() FROM t1 LIMIT 0 ) +; +DROP TABLE t1; + diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 21d7637ec06..f9330bc4375 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -4395,10 +4395,16 @@ void st_select_lex::set_explain_type(bool on_the_fly) if (join) { bool uses_cte= false; - for (JOIN_TAB *tab= first_explain_order_tab(join); tab; - tab= next_explain_order_tab(join, tab)) + for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_BUSH_ROOTS, + WITH_CONST_TABLES); + tab; + tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS)) { - if (tab->table && tab->table->pos_in_table_list->with) + /* + pos_in_table_list=NULL for e.g. post-join aggregation JOIN_TABs. + */ + if (tab->table && tab->table->pos_in_table_list && + tab->table->pos_in_table_list->with) { uses_cte= true; break; From 8c0129dc324d7ef1aa1642bd297c521f20285d25 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Fri, 28 Jul 2017 12:30:30 +0400 Subject: [PATCH 4/6] MDEV-13396 Unexpected "alter routine comand defined" during CREATE OR REPLACE PROCEDURE --- mysql-test/r/sp-security.result | 18 ++++++++++++++++++ mysql-test/t/sp-security.test | 27 +++++++++++++++++++++++++++ sql/sql_parse.cc | 2 +- 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/sp-security.result b/mysql-test/r/sp-security.result index cd5e6a16967..882072ff7c7 100644 --- a/mysql-test/r/sp-security.result +++ b/mysql-test/r/sp-security.result @@ -755,3 +755,21 @@ GRANT EXECUTE ON PROCEDURE `test`.`sp1` TO 'root'@'localhost' GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION drop procedure sp1; set password=''; +# +# MDEV-13396 Unexpected "alter routine comand defined" during CREATE OR REPLACE PROCEDURE +# +CREATE DATABASE u1; +CREATE PROCEDURE u1.p1() BEGIN SELECT 1; END; $$ +CREATE FUNCTION u1.f1() RETURNS INT BEGIN RETURN 1; END; $$ +CREATE USER u1@localhost; +GRANT CREATE ROUTINE ON u1.* TO u1@localhost; +GRANT ALTER ROUTINE ON FUNCTION u1.f1 TO u1@localhost; +GRANT ALTER ROUTINE ON PROCEDURE u1.p1 TO u1@localhost; +connect u1, localhost, u1,,; +USE u1; +CREATE OR REPLACE FUNCTION f1() RETURNS INT BEGIN RETURN 2; END; $$ +CREATE OR REPLACE PROCEDURE p1() BEGIN SELECT 1; END; $$ +disconnect u1; +connection default; +DROP DATABASE u1; +DROP USER u1@localhost; diff --git a/mysql-test/t/sp-security.test b/mysql-test/t/sp-security.test index f719cbff11c..059a5dd0fa8 100644 --- a/mysql-test/t/sp-security.test +++ b/mysql-test/t/sp-security.test @@ -1023,3 +1023,30 @@ grant execute on procedure sp1 to current_user() identified by 'barfoo'; show grants; drop procedure sp1; set password=''; + +--echo # +--echo # MDEV-13396 Unexpected "alter routine comand defined" during CREATE OR REPLACE PROCEDURE +--echo # + +CREATE DATABASE u1; +DELIMITER $$; +CREATE PROCEDURE u1.p1() BEGIN SELECT 1; END; $$ +CREATE FUNCTION u1.f1() RETURNS INT BEGIN RETURN 1; END; $$ +DELIMITER ;$$ + +CREATE USER u1@localhost; +GRANT CREATE ROUTINE ON u1.* TO u1@localhost; +GRANT ALTER ROUTINE ON FUNCTION u1.f1 TO u1@localhost; +GRANT ALTER ROUTINE ON PROCEDURE u1.p1 TO u1@localhost; + +connect (u1, localhost, u1,,); +USE u1; +DELIMITER $$; +CREATE OR REPLACE FUNCTION f1() RETURNS INT BEGIN RETURN 2; END; $$ +CREATE OR REPLACE PROCEDURE p1() BEGIN SELECT 1; END; $$ +DELIMITER ;$$ + +disconnect u1; +connection default; +DROP DATABASE u1; +DROP USER u1@localhost; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index ac742d79681..4f0610cffcb 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5660,7 +5660,7 @@ end_with_restore_list: { if (check_routine_access(thd, ALTER_PROC_ACL, lex->spname->m_db.str, lex->spname->m_name.str, - lex->sql_command == SQLCOM_DROP_PROCEDURE, 0)) + lex->sql_command == SQLCOM_CREATE_PROCEDURE, 0)) goto error; } From 4937474f862010c90f76bf879a70c1edf17e7c85 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Mon, 31 Jul 2017 17:34:59 +0400 Subject: [PATCH 5/6] MDEV-13414 Fix the SP code to avoid excessive use of strlen --- sql/field.cc | 19 +++ sql/field.h | 15 +++ sql/sp.cc | 298 +++++++++++++++++++++----------------------- sql/sp.h | 28 +++-- sql/sp_head.cc | 21 ---- sql/sp_head.h | 7 +- sql/sql_class.cc | 30 +++++ sql/sql_lex.h | 1 + sql/sql_show.cc | 98 +++++++-------- sql/sql_string.h | 1 + sql/structs.h | 2 + sql/wsrep_mysqld.cc | 17 ++- 12 files changed, 281 insertions(+), 256 deletions(-) diff --git a/sql/field.cc b/sql/field.cc index 6320121d1c3..fbb2ad79ba9 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -10773,3 +10773,22 @@ void Field::register_field_in_read_map() } bitmap_set_bit(table->read_set, field_index); } + + +bool Field::val_str_nopad(MEM_ROOT *mem_root, LEX_CSTRING *to) +{ + StringBuffer str; + bool rc= false; + THD *thd= get_thd(); + sql_mode_t sql_mode_backup= thd->variables.sql_mode; + thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH; + + val_str(&str); + if (!(to->length= str.length())) + *to= empty_clex_str; + else if ((rc= !(to->str= strmake_root(mem_root, str.ptr(), str.length())))) + to->length= 0; + + thd->variables.sql_mode= sql_mode_backup; + return rc; +} diff --git a/sql/field.h b/sql/field.h index 208941b3daa..cb8ade55090 100644 --- a/sql/field.h +++ b/sql/field.h @@ -839,6 +839,21 @@ public: */ virtual String *val_str(String*,String *)=0; String *val_int_as_str(String *val_buffer, bool unsigned_flag); + /* + Return the field value as a LEX_CSTRING, without padding to full length + (MODE_PAD_CHAR_TO_FULL_LENGTH is temporarily suppressed during the call). + + In case of an empty value, to[0] is assigned to empty_clex_string, + memory is not allocated. + In case of a non-empty value, the memory is allocated on mem_root. + In case of a memory allocation failure, to[0] is assigned to {NULL,0}. + + @param [IN] mem_root store non-empty values here + @param [OUT to return the string here + @retval false (success) + @retval true (EOM) + */ + bool val_str_nopad(MEM_ROOT *mem_root, LEX_CSTRING *to); fast_field_copier get_fast_field_copier(const Field *from); /* str_needs_quotes() returns TRUE if the value returned by val_str() needs diff --git a/sql/sp.cc b/sql/sp.cc index c80078ebe19..2059c1ac508 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -40,10 +40,12 @@ static int db_load_routine(THD *thd, stored_procedure_type type, const sp_name *name, sp_head **sphp, - sql_mode_t sql_mode, const char *params, const char *returns, - const char *body, const st_sp_chistics &chistics, - LEX_CSTRING *definer_user_name, - LEX_CSTRING *definer_host_name, + sql_mode_t sql_mode, + const LEX_CSTRING ¶ms, + const LEX_CSTRING &returns, + const LEX_CSTRING &body, + const st_sp_chistics &chistics, + const AUTHID &definer, longlong created, longlong modified, Stored_program_creation_ctx *creation_ctx); @@ -214,15 +216,16 @@ bool load_charset(MEM_ROOT *mem_root, CHARSET_INFO *dflt_cs, CHARSET_INFO **cs) { - String cs_name; + LEX_CSTRING cs_name; - if (get_field(mem_root, field, &cs_name)) + if (field->val_str_nopad(mem_root, &cs_name)) { *cs= dflt_cs; return TRUE; } - *cs= get_charset_by_csname(cs_name.c_ptr(), MY_CS_PRIMARY, MYF(0)); + DBUG_ASSERT(cs_name.str[cs_name.length] == 0); + *cs= get_charset_by_csname(cs_name.str, MY_CS_PRIMARY, MYF(0)); if (*cs == NULL) { @@ -240,15 +243,16 @@ bool load_collation(MEM_ROOT *mem_root, CHARSET_INFO *dflt_cl, CHARSET_INFO **cl) { - String cl_name; + LEX_CSTRING cl_name; - if (get_field(mem_root, field, &cl_name)) + if (field->val_str_nopad(mem_root, &cl_name)) { *cl= dflt_cl; return TRUE; } - *cl= get_charset_by_name(cl_name.c_ptr(), MYF(0)); + DBUG_ASSERT(cl_name.str[cl_name.length] == 0); + *cl= get_charset_by_name(cl_name.str, MYF(0)); if (*cl == NULL) { @@ -503,6 +507,62 @@ db_find_routine_aux(THD *thd, stored_procedure_type type, } +bool st_sp_chistics::read_from_mysql_proc_row(THD *thd, TABLE *table) +{ + LEX_CSTRING str; + + if (table->field[MYSQL_PROC_FIELD_ACCESS]->val_str_nopad(thd->mem_root, + &str)) + return true; + + switch (str.str[0]) { + case 'N': + daccess= SP_NO_SQL; + break; + case 'C': + daccess= SP_CONTAINS_SQL; + break; + case 'R': + daccess= SP_READS_SQL_DATA; + break; + case 'M': + daccess= SP_MODIFIES_SQL_DATA; + break; + default: + daccess= SP_DEFAULT_ACCESS_MAPPING; + } + + if (table->field[MYSQL_PROC_FIELD_DETERMINISTIC]->val_str_nopad(thd->mem_root, + &str)) + return true; + detistic= str.str[0] == 'N' ? false : true; + + if (table->field[MYSQL_PROC_FIELD_SECURITY_TYPE]->val_str_nopad(thd->mem_root, + &str)) + return true; + suid= str.str[0] == 'I' ? SP_IS_NOT_SUID : SP_IS_SUID; + + if (table->field[MYSQL_PROC_FIELD_COMMENT]->val_str_nopad(thd->mem_root, + &comment)) + return true; + + return false; +} + + +bool AUTHID::read_from_mysql_proc_row(THD *thd, TABLE *table) +{ + LEX_CSTRING str; + if (table->field[MYSQL_PROC_FIELD_DEFINER]->val_str_nopad(thd->mem_root, + &str)) + return true; + parse(str.str, str.length); + if (user.str[user.length]) + ((char *) user.str)[user.length]= '\0'; // 0-terminate if was truncated + return false; +} + + /** Find routine definition in mysql.proc table and create corresponding sp_head object for it. @@ -528,24 +588,16 @@ db_find_routine(THD *thd, stored_procedure_type type, const sp_name *name, sp_head **sphp) { TABLE *table; - const char *params, *returns, *body; + LEX_CSTRING params, returns, body; int ret; - const char *definer; longlong created; longlong modified; Sp_chistics chistics; - char *ptr; - uint length; - char buff[65]; - String str(buff, sizeof(buff), &my_charset_bin); bool saved_time_zone_used= thd->time_zone_used; sql_mode_t sql_mode, saved_mode= thd->variables.sql_mode; Open_tables_backup open_tables_state_backup; Stored_program_creation_ctx *creation_ctx; - char definer_user_name_holder[USERNAME_LENGTH + 1]; - LEX_CSTRING definer_user_name= { definer_user_name_holder, USERNAME_LENGTH }; - char definer_host_name_holder[HOSTNAME_LENGTH + 1]; - LEX_CSTRING definer_host_name= { definer_host_name_holder, HOSTNAME_LENGTH }; + AUTHID definer; DBUG_ENTER("db_find_routine"); DBUG_PRINT("enter", ("type: %d name: %.*s", @@ -567,106 +619,43 @@ db_find_routine(THD *thd, stored_procedure_type type, const sp_name *name, goto done; } - if ((ptr= get_field(thd->mem_root, - table->field[MYSQL_PROC_FIELD_ACCESS])) == NULL) + if (chistics.read_from_mysql_proc_row(thd, table) || + definer.read_from_mysql_proc_row(thd, table)) { ret= SP_GET_FIELD_FAILED; goto done; } - switch (ptr[0]) { - case 'N': - chistics.daccess= SP_NO_SQL; - break; - case 'C': - chistics.daccess= SP_CONTAINS_SQL; - break; - case 'R': - chistics.daccess= SP_READS_SQL_DATA; - break; - case 'M': - chistics.daccess= SP_MODIFIES_SQL_DATA; - break; - default: - chistics.daccess= SP_DEFAULT_ACCESS_MAPPING; - } - - if ((ptr= get_field(thd->mem_root, - table->field[MYSQL_PROC_FIELD_DETERMINISTIC])) == NULL) - { - ret= SP_GET_FIELD_FAILED; - goto done; - } - chistics.detistic= (ptr[0] == 'N' ? FALSE : TRUE); - - if ((ptr= get_field(thd->mem_root, - table->field[MYSQL_PROC_FIELD_SECURITY_TYPE])) == NULL) - { - ret= SP_GET_FIELD_FAILED; - goto done; - } - chistics.suid= (ptr[0] == 'I' ? SP_IS_NOT_SUID : SP_IS_SUID); - - if ((params= get_field(thd->mem_root, - table->field[MYSQL_PROC_FIELD_PARAM_LIST])) == NULL) - { - params= ""; - } + table->field[MYSQL_PROC_FIELD_PARAM_LIST]->val_str_nopad(thd->mem_root, + ¶ms); if (type == TYPE_ENUM_PROCEDURE) - returns= ""; - else if ((returns= get_field(thd->mem_root, - table->field[MYSQL_PROC_FIELD_RETURNS])) == NULL) + returns= empty_clex_str; + else if (table->field[MYSQL_PROC_FIELD_RETURNS]->val_str_nopad(thd->mem_root, + &returns)) { ret= SP_GET_FIELD_FAILED; goto done; } - if ((body= get_field(thd->mem_root, - table->field[MYSQL_PROC_FIELD_BODY])) == NULL) + if (table->field[MYSQL_PROC_FIELD_BODY]->val_str_nopad(thd->mem_root, + &body)) { ret= SP_GET_FIELD_FAILED; goto done; } // Get additional information - if ((definer= get_field(thd->mem_root, - table->field[MYSQL_PROC_FIELD_DEFINER])) == NULL) - { - ret= SP_GET_FIELD_FAILED; - goto done; - } - modified= table->field[MYSQL_PROC_FIELD_MODIFIED]->val_int(); created= table->field[MYSQL_PROC_FIELD_CREATED]->val_int(); - sql_mode= (ulong) table->field[MYSQL_PROC_FIELD_SQL_MODE]->val_int(); - table->field[MYSQL_PROC_FIELD_COMMENT]->val_str(&str, &str); - - ptr= 0; - if ((length= str.length())) - ptr= thd->strmake(str.ptr(), length); - chistics.comment.str= ptr; - chistics.comment.length= length; - creation_ctx= Stored_routine_creation_ctx::load_from_db(thd, name, table); close_system_tables(thd, &open_tables_state_backup); table= 0; - /* It's ok to cast to char* here as the pointers are to local buffers */ - if (parse_user(definer, strlen(definer), - (char*) definer_user_name.str, &definer_user_name.length, - (char*) definer_host_name.str, &definer_host_name.length) && - definer_user_name.length && !definer_host_name.length) - { - // 'user@' -> 'user@%' - definer_host_name= host_not_specified; - } - ret= db_load_routine(thd, type, name, sphp, - sql_mode, params, returns, body, chistics, - &definer_user_name, &definer_host_name, + sql_mode, params, returns, body, chistics, definer, created, modified, creation_ctx); done: /* @@ -811,10 +800,12 @@ Bad_db_error_handler::handle_condition(THD *thd, static int db_load_routine(THD *thd, stored_procedure_type type, const sp_name *name, sp_head **sphp, - sql_mode_t sql_mode, const char *params, const char *returns, - const char *body, const st_sp_chistics &chistics, - LEX_CSTRING *definer_user_name, - LEX_CSTRING *definer_host_name, + sql_mode_t sql_mode, + const LEX_CSTRING ¶ms, + const LEX_CSTRING &returns, + const LEX_CSTRING &body, + const st_sp_chistics &chistics, + const AUTHID &definer, longlong created, longlong modified, Stored_program_creation_ctx *creation_ctx) { @@ -843,13 +834,9 @@ db_load_routine(THD *thd, stored_procedure_type type, if (!show_create_sp(thd, &defstr, type, - NULL, 0, - name->m_name.str, name->m_name.length, - params, strlen(params), - returns, strlen(returns), - body, strlen(body), - chistics, definer_user_name, definer_host_name, - sql_mode)) + null_clex_str, name->m_name, + params, returns, body, + chistics, definer, sql_mode)) { ret= SP_INTERNAL_ERROR; goto end; @@ -900,7 +887,7 @@ db_load_routine(THD *thd, stored_procedure_type type, goto end; } - (*sphp)->set_definer(definer_user_name, definer_host_name); + (*sphp)->set_definer(&definer.user, &definer.host); (*sphp)->set_info(created, modified, chistics, sql_mode); (*sphp)->set_creation_ctx(creation_ctx); (*sphp)->optimize(); @@ -1041,6 +1028,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) DBUG_PRINT("enter", ("type: %d name: %.*s", (int) type, (int) sp->m_name.length, sp->m_name.str)); + LEX_CSTRING returns= empty_clex_str; String retstr(64); retstr.set_charset(system_charset_info); @@ -1099,7 +1087,10 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) // Setting retstr as it is used for logging. if (sp->m_type == TYPE_ENUM_FUNCTION) + { sp_returns_type(thd, retstr, sp); + returns= retstr.lex_cstring(); + } goto log; } else @@ -1177,6 +1168,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) if (sp->m_type == TYPE_ENUM_FUNCTION) { sp_returns_type(thd, retstr, sp); + returns= retstr.lex_cstring(); store_failed= store_failed || table->field[MYSQL_PROC_FIELD_RETURNS]-> @@ -1282,16 +1274,12 @@ log: log_query.set_charset(system_charset_info); if (!show_create_sp(thd, &log_query, - sp->m_type, - (sp->m_explicit_name ? sp->m_db.str : NULL), - (sp->m_explicit_name ? sp->m_db.length : 0), - sp->m_name.str, sp->m_name.length, - sp->m_params.str, sp->m_params.length, - retstr.ptr(), retstr.length(), - sp->m_body.str, sp->m_body.length, - sp->chistics(), &(thd->lex->definer->user), - &(thd->lex->definer->host), - saved_mode)) + sp->m_type, + sp->m_explicit_name ? sp->m_db : null_clex_str, + sp->m_name, + sp->m_params, returns, sp->m_body, + sp->chistics(), thd->lex->definer[0], + saved_mode)) { my_error(ER_OUT_OF_RESOURCES, MYF(0)); goto done; @@ -1745,7 +1733,7 @@ sp_find_routine(THD *thd, stored_procedure_type type, const sp_name *name, { ulong level; sp_head *new_sp; - const char *returns= ""; + LEX_CSTRING returns= empty_clex_str; /* String buffer for RETURNS data type must have system charset; @@ -1785,12 +1773,12 @@ sp_find_routine(THD *thd, stored_procedure_type type, const sp_name *name, if (type == TYPE_ENUM_FUNCTION) { sp_returns_type(thd, retstr, sp); - returns= retstr.ptr(); + returns= retstr.lex_cstring(); } if (db_load_routine(thd, type, name, &new_sp, - sp->m_sql_mode, sp->m_params.str, returns, - sp->m_body.str, sp->chistics(), - &sp->m_definer.user, &sp->m_definer.host, + sp->m_sql_mode, sp->m_params, returns, + sp->m_body, sp->chistics(), + sp->m_definer, sp->m_created, sp->m_modified, sp->get_creation_ctx()) == SP_OK) { @@ -2193,19 +2181,19 @@ int sp_cache_routine(THD *thd, enum stored_procedure_type type, bool show_create_sp(THD *thd, String *buf, stored_procedure_type type, - const char *db, ulong dblen, - const char *name, ulong namelen, - const char *params, ulong paramslen, - const char *returns, ulong returnslen, - const char *body, ulong bodylen, + const LEX_CSTRING &db, + const LEX_CSTRING &name, + const LEX_CSTRING ¶ms, + const LEX_CSTRING &returns, + const LEX_CSTRING &body, const st_sp_chistics &chistics, - const LEX_CSTRING *definer_user, - const LEX_CSTRING *definer_host, + const AUTHID &definer, sql_mode_t sql_mode) { sql_mode_t old_sql_mode= thd->variables.sql_mode; /* Make some room to begin with */ - if (buf->alloc(100 + dblen + 1 + namelen + paramslen + returnslen + bodylen + + if (buf->alloc(100 + db.length + 1 + name.length + + params.length + returns.length + chistics.comment.length + 10 /* length of " DEFINER= "*/ + USER_HOST_BUFF_SIZE)) return FALSE; @@ -2214,7 +2202,7 @@ show_create_sp(THD *thd, String *buf, buf->append(STRING_WITH_LEN("CREATE ")); if (thd->lex->create_info.or_replace()) buf->append(STRING_WITH_LEN("OR REPLACE ")); - append_definer(thd, buf, definer_user, definer_host); + append_definer(thd, buf, &definer.user, &definer.host); if (type == TYPE_ENUM_FUNCTION) buf->append(STRING_WITH_LEN("FUNCTION ")); else @@ -2222,14 +2210,14 @@ show_create_sp(THD *thd, String *buf, if (thd->lex->create_info.if_not_exists()) buf->append(STRING_WITH_LEN("IF NOT EXISTS ")); - if (dblen > 0) + if (db.length > 0) { - append_identifier(thd, buf, db, dblen); + append_identifier(thd, buf, db.str, db.length); buf->append('.'); } - append_identifier(thd, buf, name, namelen); + append_identifier(thd, buf, name.str, name.length); buf->append('('); - buf->append(params, paramslen); + buf->append(params); buf->append(')'); if (type == TYPE_ENUM_FUNCTION) { @@ -2237,7 +2225,7 @@ show_create_sp(THD *thd, String *buf, buf->append(STRING_WITH_LEN(" RETURN ")); else buf->append(STRING_WITH_LEN(" RETURNS ")); - buf->append(returns, returnslen); + buf->append(returns); } buf->append('\n'); switch (chistics.daccess) { @@ -2265,12 +2253,21 @@ show_create_sp(THD *thd, String *buf, append_unescaped(buf, chistics.comment.str, chistics.comment.length); buf->append('\n'); } - buf->append(body, bodylen); + buf->append(body); thd->variables.sql_mode= old_sql_mode; return TRUE; } +static LEX_CSTRING empty_body_lex_cstring(stored_procedure_type type) +{ + static LEX_CSTRING empty_body_func= {C_STRING_WITH_LEN("RETURN NULL")}; + static LEX_CSTRING empty_body_proc= {C_STRING_WITH_LEN("BEGIN END")}; + return type == TYPE_ENUM_FUNCTION ? empty_body_func : + empty_body_proc; +} + + /** @brief The function loads sp_head struct for information schema purposes (used for I_S ROUTINES & PARAMETERS tables). @@ -2292,26 +2289,21 @@ show_create_sp(THD *thd, String *buf, */ sp_head * -sp_load_for_information_schema(THD *thd, TABLE *proc_table, String *db, - String *name, sql_mode_t sql_mode, +sp_load_for_information_schema(THD *thd, TABLE *proc_table, stored_procedure_type type, - const char *returns, const char *params, + const LEX_CSTRING &db, + const LEX_CSTRING &name, + const LEX_CSTRING ¶ms, + const LEX_CSTRING &returns, + sql_mode_t sql_mode, bool *free_sp_head) { - const char *sp_body; String defstr; - const LEX_CSTRING definer_user= {STRING_WITH_LEN("")}; - const LEX_CSTRING definer_host= {STRING_WITH_LEN("")}; - LEX_CSTRING sp_db_str; - LEX_CSTRING sp_name_str; + const AUTHID definer= {{STRING_WITH_LEN("")}, {STRING_WITH_LEN("")}}; sp_head *sp; sp_cache **spc= ((type == TYPE_ENUM_PROCEDURE) ? &thd->sp_proc_cache : &thd->sp_func_cache); - sp_db_str.str= db->c_ptr(); - sp_db_str.length= db->length(); - sp_name_str.str= name->c_ptr(); - sp_name_str.length= name->length(); - sp_name sp_name_obj(&sp_db_str, &sp_name_str, true); + sp_name sp_name_obj(&db, &name, true); *free_sp_head= 0; if ((sp= sp_cache_lookup(spc, &sp_name_obj))) { @@ -2321,15 +2313,11 @@ sp_load_for_information_schema(THD *thd, TABLE *proc_table, String *db, LEX *old_lex= thd->lex, newlex; Stored_program_creation_ctx *creation_ctx= Stored_routine_creation_ctx::load_from_db(thd, &sp_name_obj, proc_table); - sp_body= (type == TYPE_ENUM_FUNCTION ? "RETURN NULL" : "BEGIN END"); defstr.set_charset(creation_ctx->get_client_cs()); if (!show_create_sp(thd, &defstr, type, - sp_db_str.str, sp_db_str.length, - sp_name_obj.m_name.str, sp_name_obj.m_name.length, - params, strlen(params), - returns, strlen(returns), - sp_body, strlen(sp_body), - Sp_chistics(), &definer_user, &definer_host, sql_mode)) + sp_name_obj.m_db, sp_name_obj.m_name, + params, returns, empty_body_lex_cstring(type), + Sp_chistics(), definer, sql_mode)) return 0; thd->lex= &newlex; diff --git a/sql/sp.h b/sql/sp.h index dc93b8b20ee..6e9c032e4c4 100644 --- a/sql/sp.h +++ b/sql/sp.h @@ -213,10 +213,13 @@ extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen, TABLE *open_proc_table_for_read(THD *thd, Open_tables_backup *backup); sp_head * -sp_load_for_information_schema(THD *thd, TABLE *proc_table, String *db, - String *name, sql_mode_t sql_mode, +sp_load_for_information_schema(THD *thd, TABLE *proc_table, stored_procedure_type type, - const char *returns, const char *params, + const LEX_CSTRING &db, + const LEX_CSTRING &name, + const LEX_CSTRING ¶ms, + const LEX_CSTRING &returns, + sql_mode_t sql_mode, bool *free_sp_head); bool load_charset(MEM_ROOT *mem_root, @@ -234,14 +237,13 @@ void sp_returns_type(THD *thd, sp_head *sp); bool show_create_sp(THD *thd, String *buf, - stored_procedure_type type, - const char *db, ulong dblen, - const char *name, ulong namelen, - const char *params, ulong paramslen, - const char *returns, ulong returnslen, - const char *body, ulong bodylen, - const st_sp_chistics &chistics, - const LEX_CSTRING *definer_user, - const LEX_CSTRING *definer_host, - sql_mode_t sql_mode); + stored_procedure_type type, + const LEX_CSTRING &db, + const LEX_CSTRING &name, + const LEX_CSTRING ¶ms, + const LEX_CSTRING &returns, + const LEX_CSTRING &body, + const st_sp_chistics &chistics, + const AUTHID &definer, + sql_mode_t sql_mode); #endif /* _SP_H_ */ diff --git a/sql/sp_head.cc b/sql/sp_head.cc index bb3275316cc..ad45ccdc92e 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -2458,27 +2458,6 @@ sp_head::set_info(longlong created, longlong modified, } -void -sp_head::set_definer(const char *definer, uint definerlen) -{ - char user_name_holder[USERNAME_LENGTH + 1]; - LEX_CSTRING user_name= { user_name_holder, USERNAME_LENGTH }; - - char host_name_holder[HOSTNAME_LENGTH + 1]; - LEX_CSTRING host_name= { host_name_holder, HOSTNAME_LENGTH }; - - if (parse_user(definer, definerlen, user_name_holder, &user_name.length, - host_name_holder, &host_name.length) && - user_name.length && !host_name.length) - { - // 'user@' -> 'user@%' - host_name= host_not_specified; - } - - set_definer(&user_name, &host_name); -} - - void sp_head::reset_thd_mem_root(THD *thd) { diff --git a/sql/sp_head.h b/sql/sp_head.h index 1435bb460c3..f17a918cf50 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -689,7 +689,12 @@ public: void set_info(longlong created, longlong modified, const st_sp_chistics &chistics, sql_mode_t sql_mode); - void set_definer(const char *definer, uint definerlen); + void set_definer(const char *definer, uint definerlen) + { + AUTHID tmp; + tmp.parse(definer, definerlen); + m_definer.copy(mem_root, &tmp.user, &tmp.host); + } void set_definer(const LEX_CSTRING *user_name, const LEX_CSTRING *host_name) { m_definer.copy(mem_root, user_name, host_name); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index c33f01d0084..55742f7aaec 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -7475,4 +7475,34 @@ void AUTHID::copy(MEM_ROOT *mem_root, const LEX_CSTRING *user_name, } +/* + Set from a string in 'user@host' format. + This method resebmles parse_user(), + but does not need temporary buffers. +*/ +void AUTHID::parse(const char *str, size_t length) +{ + const char *p= strrchr(str, '@'); + if (!p) + { + user.str= str; + user.length= length; + host= null_clex_str; + } + else + { + user.str= str; + user.length= (size_t) (p - str); + host.str= p + 1; + host.length= (size_t) (length - user.length - 1); + if (user.length && !host.length) + host= host_not_specified; // 'user@' -> 'user@%' + } + if (user.length > USERNAME_LENGTH) + user.length= USERNAME_LENGTH; + if (host.length > HOSTNAME_LENGTH) + host.length= HOSTNAME_LENGTH; +} + + #endif /* !defined(MYSQL_CLIENT) */ diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 3c7c99dff3d..c1351b110f6 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1287,6 +1287,7 @@ struct st_sp_chistics enum enum_sp_data_access daccess; void init() { bzero(this, sizeof(*this)); } void set(const st_sp_chistics &other) { *this= other; } + bool read_from_mysql_proc_row(THD *thd, TABLE *table); }; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index fd0d11bf1bb..de7f93ca901 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -5876,14 +5876,9 @@ bool store_schema_params(THD *thd, TABLE *table, TABLE *proc_table, TABLE_SHARE share; TABLE tbl; CHARSET_INFO *cs= system_charset_info; - char params_buff[MAX_FIELD_WIDTH], returns_buff[MAX_FIELD_WIDTH], - sp_db_buff[NAME_LEN], sp_name_buff[NAME_LEN], path[FN_REFLEN], - definer_buff[DEFINER_LENGTH + 1]; - String params(params_buff, sizeof(params_buff), cs); - String returns(returns_buff, sizeof(returns_buff), cs); - String sp_db(sp_db_buff, sizeof(sp_db_buff), cs); - String sp_name(sp_name_buff, sizeof(sp_name_buff), cs); - String definer(definer_buff, sizeof(definer_buff), cs); + LEX_CSTRING definer, params, returns= empty_clex_str; + LEX_CSTRING db, name; + char path[FN_REFLEN]; sp_head *sp; stored_procedure_type routine_type; bool free_sp_head; @@ -5894,48 +5889,44 @@ bool store_schema_params(THD *thd, TABLE *table, TABLE *proc_table, (void) build_table_filename(path, sizeof(path), "", "", "", 0); init_tmp_table_share(thd, &share, "", 0, "", path); - get_field(thd->mem_root, proc_table->field[MYSQL_PROC_FIELD_DB], &sp_db); - get_field(thd->mem_root, proc_table->field[MYSQL_PROC_FIELD_NAME], &sp_name); - get_field(thd->mem_root,proc_table->field[MYSQL_PROC_FIELD_DEFINER],&definer); + proc_table->field[MYSQL_PROC_FIELD_DB]->val_str_nopad(thd->mem_root, &db); + proc_table->field[MYSQL_PROC_FIELD_NAME]->val_str_nopad(thd->mem_root, &name); + proc_table->field[MYSQL_PROC_FIELD_DEFINER]->val_str_nopad(thd->mem_root, &definer); routine_type= (stored_procedure_type) proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_int(); if (!full_access) - full_access= !strcmp(sp_user, definer.ptr()); + full_access= !strcmp(sp_user, definer.str); if (!full_access && - check_some_routine_access(thd, sp_db.ptr(),sp_name.ptr(), + check_some_routine_access(thd, db.str, name.str, routine_type == TYPE_ENUM_PROCEDURE)) DBUG_RETURN(0); - params.length(0); - get_field(thd->mem_root, proc_table->field[MYSQL_PROC_FIELD_PARAM_LIST], - ¶ms); - returns.length(0); + proc_table->field[MYSQL_PROC_FIELD_PARAM_LIST]->val_str_nopad(thd->mem_root, + ¶ms); if (routine_type == TYPE_ENUM_FUNCTION) - get_field(thd->mem_root, proc_table->field[MYSQL_PROC_FIELD_RETURNS], - &returns); + proc_table->field[MYSQL_PROC_FIELD_RETURNS]->val_str_nopad(thd->mem_root, + &returns); - sp= sp_load_for_information_schema(thd, proc_table, &sp_db, &sp_name, + sp= sp_load_for_information_schema(thd, proc_table, routine_type, db, name, + params, returns, (ulong) proc_table-> field[MYSQL_PROC_FIELD_SQL_MODE]->val_int(), - routine_type, - returns.c_ptr_safe(), - params.c_ptr_safe(), &free_sp_head); if (sp) { Field *field; - String tmp_string; + LEX_CSTRING tmp_string; if (routine_type == TYPE_ENUM_FUNCTION) { restore_record(table, s->default_values); table->field[0]->store(STRING_WITH_LEN("def"), cs); - table->field[1]->store(sp_db.ptr(), sp_db.length(), cs); - table->field[2]->store(sp_name.ptr(), sp_name.length(), cs); + table->field[1]->store(db, cs); + table->field[2]->store(name, cs); table->field[3]->store((longlong) 0, TRUE); - get_field(thd->mem_root, proc_table->field[MYSQL_PROC_MYSQL_TYPE], - &tmp_string); - table->field[15]->store(tmp_string.ptr(), tmp_string.length(), cs); + proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_str_nopad(thd->mem_root, + &tmp_string); + table->field[15]->store(tmp_string, cs); field= sp->m_return_field_def.make_field(&share, thd->mem_root, &empty_clex_str); field->table= &tbl; @@ -5973,16 +5964,16 @@ bool store_schema_params(THD *thd, TABLE *table, TABLE *proc_table, restore_record(table, s->default_values); table->field[0]->store(STRING_WITH_LEN("def"), cs); - table->field[1]->store(sp_db.ptr(), sp_db.length(), cs); - table->field[2]->store(sp_name.ptr(), sp_name.length(), cs); + table->field[1]->store(db, cs); + table->field[2]->store(name, cs); table->field[3]->store((longlong) i + 1, TRUE); table->field[4]->store(tmp_buff, strlen(tmp_buff), cs); table->field[4]->set_notnull(); table->field[5]->store(spvar->name.str, spvar->name.length, cs); table->field[5]->set_notnull(); - get_field(thd->mem_root, proc_table->field[MYSQL_PROC_MYSQL_TYPE], - &tmp_string); - table->field[15]->store(tmp_string.ptr(), tmp_string.length(), cs); + proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_str_nopad(thd->mem_root, + &tmp_string); + table->field[15]->store(tmp_string, cs); field= spvar->field_def.make_field(&share, thd->mem_root, &spvar->name); @@ -6009,23 +6000,16 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table, MYSQL_TIME time; LEX *lex= thd->lex; CHARSET_INFO *cs= system_charset_info; - char sp_db_buff[SAFE_NAME_LEN + 1], sp_name_buff[NAME_LEN + 1], - definer_buff[DEFINER_LENGTH + 1], - returns_buff[MAX_FIELD_WIDTH]; + LEX_CSTRING db, name, definer, returns= empty_clex_str; - String sp_db(sp_db_buff, sizeof(sp_db_buff), cs); - String sp_name(sp_name_buff, sizeof(sp_name_buff), cs); - String definer(definer_buff, sizeof(definer_buff), cs); - String returns(returns_buff, sizeof(returns_buff), cs); - - proc_table->field[MYSQL_PROC_FIELD_DB]->val_str(&sp_db); - proc_table->field[MYSQL_PROC_FIELD_NAME]->val_str(&sp_name); - proc_table->field[MYSQL_PROC_FIELD_DEFINER]->val_str(&definer); + proc_table->field[MYSQL_PROC_FIELD_DB]->val_str_nopad(thd->mem_root, &db); + proc_table->field[MYSQL_PROC_FIELD_NAME]->val_str_nopad(thd->mem_root, &name); + proc_table->field[MYSQL_PROC_FIELD_DEFINER]->val_str_nopad(thd->mem_root, &definer); if (!full_access) - full_access= !strcmp(sp_user, definer.c_ptr_safe()); + full_access= !strcmp(sp_user, definer.str); if (!full_access && - check_some_routine_access(thd, sp_db.c_ptr_safe(), sp_name.c_ptr_safe(), + check_some_routine_access(thd, db.str, name.str, proc_table->field[MYSQL_PROC_MYSQL_TYPE]-> val_int() == TYPE_ENUM_PROCEDURE)) return 0; @@ -6040,15 +6024,15 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table, { restore_record(table, s->default_values); if (!wild || !wild[0] || !wild_case_compare(system_charset_info, - sp_name.c_ptr_safe(), wild)) + name.str, wild)) { int enum_idx= (int) proc_table->field[MYSQL_PROC_FIELD_ACCESS]->val_int(); - table->field[3]->store(sp_name.ptr(), sp_name.length(), cs); + table->field[3]->store(name, cs); copy_field_as_string(table->field[0], proc_table->field[MYSQL_PROC_FIELD_SPECIFIC_NAME]); table->field[1]->store(STRING_WITH_LEN("def"), cs); - table->field[2]->store(sp_db.ptr(), sp_db.length(), cs); + table->field[2]->store(db, cs); copy_field_as_string(table->field[4], proc_table->field[MYSQL_PROC_MYSQL_TYPE]); @@ -6057,14 +6041,16 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table, { sp_head *sp; bool free_sp_head; - proc_table->field[MYSQL_PROC_FIELD_RETURNS]->val_str(&returns); - sp= sp_load_for_information_schema(thd, proc_table, &sp_db, &sp_name, + proc_table->field[MYSQL_PROC_FIELD_RETURNS]->val_str_nopad(thd->mem_root, + &returns); + sp= sp_load_for_information_schema(thd, proc_table, + TYPE_ENUM_FUNCTION, db, name, + empty_clex_str /*params*/, + returns, (ulong) proc_table-> field[MYSQL_PROC_FIELD_SQL_MODE]-> val_int(), - TYPE_ENUM_FUNCTION, - returns.c_ptr_safe(), - "", &free_sp_head); + &free_sp_head); if (sp) { @@ -6115,7 +6101,7 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table, copy_field_as_string(table->field[26], proc_table->field[MYSQL_PROC_FIELD_COMMENT]); - table->field[27]->store(definer.ptr(), definer.length(), cs); + table->field[27]->store(definer, cs); copy_field_as_string(table->field[28], proc_table-> field[MYSQL_PROC_FIELD_CHARACTER_SET_CLIENT]); diff --git a/sql/sql_string.h b/sql/sql_string.h index 845ed4c9746..adaef04fee4 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -471,6 +471,7 @@ public: bool append(const char *s); bool append(const LEX_STRING *ls) { return append(ls->str, ls->length); } bool append(const LEX_CSTRING *ls) { return append(ls->str, ls->length); } + bool append(const LEX_CSTRING &ls) { return append(ls.str, ls.length); } bool append(const char *s, uint32 arg_length); bool append(const char *s, uint32 arg_length, CHARSET_INFO *cs); bool append_ulonglong(ulonglong val); diff --git a/sql/structs.h b/sql/structs.h index 982eda99d30..97702e5727b 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -221,6 +221,8 @@ struct AUTHID l->length= strxmov(buf, user.str, "@", host.str, NullS) - buf; } } + void parse(const char *str, size_t length); + bool read_from_mysql_proc_row(THD *thd, TABLE *table); }; diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index 87eb97e4fec..d3726cc86a5 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -2236,6 +2236,7 @@ static int wsrep_create_sp(THD *thd, uchar** buf, size_t* buf_len) sp_head *sp = thd->lex->sphead; sql_mode_t saved_mode= thd->variables.sql_mode; String retstr(64); + LEX_CSTRING returns= empty_clex_str; retstr.set_charset(system_charset_info); log_query.set_charset(system_charset_info); @@ -2243,19 +2244,15 @@ static int wsrep_create_sp(THD *thd, uchar** buf, size_t* buf_len) if (sp->m_type == TYPE_ENUM_FUNCTION) { sp_returns_type(thd, retstr, sp); + returns= retstr.lex_cstring(); } if (!show_create_sp(thd, &log_query, - sp->m_type, - (sp->m_explicit_name ? sp->m_db.str : NULL), - (sp->m_explicit_name ? sp->m_db.length : 0), - sp->m_name.str, sp->m_name.length, - sp->m_params.str, sp->m_params.length, - retstr.c_ptr(), retstr.length(), - sp->m_body.str, sp->m_body.length, - sp->chistics(), &(thd->lex->definer->user), - &(thd->lex->definer->host), - saved_mode)) + sp->m_type, + sp->m_explicit_name ? sp->m_db : null_clex_str, + sp->m_name, sp->m_params, returns, + sp->m_body, sp->chistics(), thd->lex->definer[0], + saved_mode)) { WSREP_WARN("SP create string failed: schema: %s, query: %s", (thd->db ? thd->db : "(null)"), thd->query()); From c9218ff43989bf2385d1f62b45ed1f6229cbc5a5 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Mon, 31 Jul 2017 23:00:02 +0400 Subject: [PATCH 6/6] MDEV-13415 Wrap the code in sp.cc into a class Sp_handler --- sql/item_create.cc | 2 +- sql/item_func.cc | 11 +- sql/sp.cc | 398 +++++++++++++++++++++----------------------- sql/sp.h | 331 ++++++++++++++++++++++++++++-------- sql/sp_head.cc | 111 ++++++------ sql/sp_head.h | 31 ++-- sql/sql_acl.cc | 173 +++++++++++-------- sql/sql_acl.h | 11 +- sql/sql_base.cc | 4 +- sql/sql_lex.cc | 10 +- sql/sql_lex.h | 16 +- sql/sql_parse.cc | 71 ++++---- sql/sql_parse.h | 11 +- sql/sql_prepare.cc | 9 +- sql/sql_show.cc | 61 ++++--- sql/sql_yacc.yy | 27 +-- sql/sql_yacc_ora.yy | 42 ++--- sql/wsrep_mysqld.cc | 6 +- 18 files changed, 751 insertions(+), 574 deletions(-) diff --git a/sql/item_create.cc b/sql/item_create.cc index 8ab45e5dbe1..50f524bad40 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -3424,7 +3424,7 @@ Create_sp_func::create_with_db(THD *thd, LEX_CSTRING *db, LEX_CSTRING *name, arg_count= item_list->elements; qname= new (thd->mem_root) sp_name(db, name, use_explicit_name); - sp_add_used_routine(lex, thd, qname, TYPE_ENUM_FUNCTION); + sp_handler_function.add_used_routine(lex, thd, qname); if (arg_count > 0) func= new (thd->mem_root) Item_func_sp(thd, lex->current_context(), qname, diff --git a/sql/item_func.cc b/sql/item_func.cc index b44b29c15a5..ebd5d9860d6 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -6264,8 +6264,7 @@ Item_func_sp::init_result_field(THD *thd) DBUG_ASSERT(m_sp == NULL); DBUG_ASSERT(sp_result_field == NULL); - if (!(m_sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, m_name, - &thd->sp_func_cache, TRUE))) + if (!(m_sp= sp_handler_function.sp_find_routine(thd, m_name, true))) { my_missing_function_error (m_name->m_name, ErrConvDQName(m_name).ptr()); context->process_error(thd); @@ -6514,7 +6513,8 @@ Item_func_sp::sp_check_access(THD *thd) DBUG_ENTER("Item_func_sp::sp_check_access"); DBUG_ASSERT(m_sp); if (check_routine_access(thd, EXECUTE_ACL, - m_sp->m_db.str, m_sp->m_name.str, 0, FALSE)) + m_sp->m_db.str, m_sp->m_name.str, + &sp_handler_function, false)) DBUG_RETURN(TRUE); DBUG_RETURN(FALSE); @@ -6540,7 +6540,8 @@ Item_func_sp::fix_fields(THD *thd, Item **ref) thd->security_ctx= context->security_ctx; res= check_routine_access(thd, EXECUTE_ACL, m_name->m_db.str, - m_name->m_name.str, 0, FALSE); + m_name->m_name.str, + &sp_handler_function, false); thd->security_ctx= save_security_ctx; if (res) @@ -6583,7 +6584,7 @@ Item_func_sp::fix_fields(THD *thd, Item **ref) Try to set and restore the security context to see whether it's valid */ Security_context *save_secutiry_ctx; - res= set_routine_security_ctx(thd, m_sp, false, &save_secutiry_ctx); + res= set_routine_security_ctx(thd, m_sp, &save_secutiry_ctx); if (!res) m_sp->m_security_ctx.restore_security_context(thd, save_secutiry_ctx); diff --git a/sql/sp.cc b/sql/sp.cc index 2059c1ac508..8aefb8b3a34 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -20,6 +20,7 @@ #include "unireg.h" #include "sp.h" #include "sql_base.h" // close_thread_tables +#include "sql_lex.h" // empty_clex_str #include "sql_parse.h" // parse_sql #include "key.h" // key_copy #include "sql_show.h" // append_definer, append_identifier @@ -34,20 +35,59 @@ #include -/* Used in error handling only */ -#define SP_TYPE_STRING(type) stored_procedure_type_to_str(type) - -static int -db_load_routine(THD *thd, stored_procedure_type type, const sp_name *name, - sp_head **sphp, - sql_mode_t sql_mode, - const LEX_CSTRING ¶ms, - const LEX_CSTRING &returns, - const LEX_CSTRING &body, - const st_sp_chistics &chistics, - const AUTHID &definer, - longlong created, longlong modified, - Stored_program_creation_ctx *creation_ctx); + +sp_cache **Sp_handler_procedure::get_cache(THD *thd) const +{ + return &thd->sp_proc_cache; +} + +sp_cache **Sp_handler_function::get_cache(THD *thd) const +{ + return &thd->sp_func_cache; +} + +ulong Sp_handler_procedure::recursion_depth(THD *thd) const +{ + return thd->variables.max_sp_recursion_depth; +} + + +bool Sp_handler::add_instr_freturn(THD *thd, sp_head *sp, + sp_pcontext *spcont, + Item *item, LEX *lex) const +{ + my_error(ER_SP_BADRETURN, MYF(0)); + return true; +} + + +bool Sp_handler::add_instr_preturn(THD *thd, sp_head *sp, + sp_pcontext *spcont) const +{ + thd->parse_error(); + return true; +} + + +bool Sp_handler_function::add_instr_freturn(THD *thd, sp_head *sp, + sp_pcontext *spcont, + Item *item, LEX *lex) const +{ + return sp->add_instr_freturn(thd, spcont, item, lex); +} + + +bool Sp_handler_procedure::add_instr_preturn(THD *thd, sp_head *sp, + sp_pcontext *spcont) const +{ + return sp->add_instr_preturn(thd, spcont); +} + + +Sp_handler_procedure sp_handler_procedure; +Sp_handler_function sp_handler_function; +Sp_handler_trigger sp_handler_trigger; + static const TABLE_FIELD_TYPE proc_table_fields[MYSQL_PROC_FIELD_COUNT] = @@ -178,7 +218,7 @@ class Stored_routine_creation_ctx : public Stored_program_creation_ctx, { public: static Stored_routine_creation_ctx * - load_from_db(THD *thd, const sp_name *name, TABLE *proc_tbl); + load_from_db(THD *thd, const Database_qualified_name *name, TABLE *proc_tbl); public: virtual Stored_program_creation_ctx *clone(MEM_ROOT *mem_root) @@ -267,8 +307,8 @@ bool load_collation(MEM_ROOT *mem_root, Stored_routine_creation_ctx * Stored_routine_creation_ctx::load_from_db(THD *thd, - const sp_name *name, - TABLE *proc_tbl) + const Database_qualified_name *name, + TABLE *proc_tbl) { /* Load character set/collation attributes. */ @@ -463,7 +503,6 @@ static TABLE *open_proc_table_for_update(THD *thd) Find row in open mysql.proc table representing stored routine. @param thd Thread context - @param type Type of routine to find (function or procedure) @param name Name of routine @param table TABLE object for open mysql.proc table. @@ -473,15 +512,16 @@ static TABLE *open_proc_table_for_update(THD *thd) SP_KEY_NOT_FOUND No routine with given name */ -static int -db_find_routine_aux(THD *thd, stored_procedure_type type, - const Database_qualified_name *name, - TABLE *table) +int +Sp_handler::db_find_routine_aux(THD *thd, + const Database_qualified_name *name, + TABLE *table) const { uchar key[MAX_KEY_LENGTH]; // db, name, optional key length type DBUG_ENTER("db_find_routine_aux"); - DBUG_PRINT("enter", ("type: %d name: %.*s", - type, (int) name->m_name.length, name->m_name.str)); + DBUG_PRINT("enter", ("type: %s name: %.*s", + type_str(), + (int) name->m_name.length, name->m_name.str)); /* Create key to find row. We have to use field->store() to be able to @@ -494,7 +534,7 @@ db_find_routine_aux(THD *thd, stored_procedure_type type, DBUG_RETURN(SP_KEY_NOT_FOUND); table->field[0]->store(name->m_db, &my_charset_bin); table->field[1]->store(name->m_name, &my_charset_bin); - table->field[2]->store((longlong) type, TRUE); + table->field[2]->store((longlong) type(), true); key_copy(key, table->record[0], table->key_info, table->key_info->key_length); @@ -568,7 +608,6 @@ bool AUTHID::read_from_mysql_proc_row(THD *thd, TABLE *table) sp_head object for it. @param thd Thread context - @param type Type of routine (TYPE_ENUM_PROCEDURE/...) @param name Name of routine @param sphp Out parameter in which pointer to created sp_head object is returned (0 in case of error). @@ -583,9 +622,10 @@ bool AUTHID::read_from_mysql_proc_row(THD *thd, TABLE *table) non-0 Error (may be one of special codes like SP_KEY_NOT_FOUND) */ -static int -db_find_routine(THD *thd, stored_procedure_type type, const sp_name *name, - sp_head **sphp) +int +Sp_handler::db_find_routine(THD *thd, + const Database_qualified_name *name, + sp_head **sphp) const { TABLE *table; LEX_CSTRING params, returns, body; @@ -600,8 +640,9 @@ db_find_routine(THD *thd, stored_procedure_type type, const sp_name *name, AUTHID definer; DBUG_ENTER("db_find_routine"); - DBUG_PRINT("enter", ("type: %d name: %.*s", - type, (int) name->m_name.length, name->m_name.str)); + DBUG_PRINT("enter", ("type: %s name: %.*s", + type_str(), + (int) name->m_name.length, name->m_name.str)); *sphp= 0; // In case of errors if (!(table= open_proc_table_for_read(thd, &open_tables_state_backup))) @@ -610,7 +651,7 @@ db_find_routine(THD *thd, stored_procedure_type type, const sp_name *name, /* Reset sql_mode during data dictionary operations. */ thd->variables.sql_mode= 0; - if ((ret= db_find_routine_aux(thd, type, name, table)) != SP_OK) + if ((ret= db_find_routine_aux(thd, name, table)) != SP_OK) goto done; if (table->s->fields < MYSQL_PROC_FIELD_COUNT) @@ -628,7 +669,7 @@ db_find_routine(THD *thd, stored_procedure_type type, const sp_name *name, table->field[MYSQL_PROC_FIELD_PARAM_LIST]->val_str_nopad(thd->mem_root, ¶ms); - if (type == TYPE_ENUM_PROCEDURE) + if (type() == TYPE_ENUM_PROCEDURE) returns= empty_clex_str; else if (table->field[MYSQL_PROC_FIELD_RETURNS]->val_str_nopad(thd->mem_root, &returns)) @@ -654,7 +695,7 @@ db_find_routine(THD *thd, stored_procedure_type type, const sp_name *name, close_system_tables(thd, &open_tables_state_backup); table= 0; - ret= db_load_routine(thd, type, name, sphp, + ret= db_load_routine(thd, name, sphp, sql_mode, params, returns, body, chistics, definer, created, modified, creation_ctx); done: @@ -797,17 +838,17 @@ Bad_db_error_handler::handle_condition(THD *thd, } -static int -db_load_routine(THD *thd, stored_procedure_type type, - const sp_name *name, sp_head **sphp, - sql_mode_t sql_mode, - const LEX_CSTRING ¶ms, - const LEX_CSTRING &returns, - const LEX_CSTRING &body, - const st_sp_chistics &chistics, - const AUTHID &definer, - longlong created, longlong modified, - Stored_program_creation_ctx *creation_ctx) +int +Sp_handler::db_load_routine(THD *thd, const Database_qualified_name *name, + sp_head **sphp, + sql_mode_t sql_mode, + const LEX_CSTRING ¶ms, + const LEX_CSTRING &returns, + const LEX_CSTRING &body, + const st_sp_chistics &chistics, + const AUTHID &definer, + longlong created, longlong modified, + Stored_program_creation_ctx *creation_ctx) const { LEX *old_lex= thd->lex, newlex; String defstr; @@ -833,10 +874,9 @@ db_load_routine(THD *thd, stored_procedure_type type, */ if (!show_create_sp(thd, &defstr, - type, - null_clex_str, name->m_name, - params, returns, body, - chistics, definer, sql_mode)) + null_clex_str, name->m_name, + params, returns, body, + chistics, definer, sql_mode)) { ret= SP_INTERNAL_ERROR; goto end; @@ -911,7 +951,7 @@ end: void -sp_returns_type(THD *thd, String &result, sp_head *sp) +sp_returns_type(THD *thd, String &result, const sp_head *sp) { TABLE table; TABLE_SHARE share; @@ -947,7 +987,6 @@ sp_returns_type(THD *thd, String &result, sp_head *sp) and invalidates the stored-routine cache. @param thd Thread context. - @param type Stored routine type (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION) @param name Stored routine name. @param table A pointer to the opened mysql.proc table @@ -955,9 +994,10 @@ sp_returns_type(THD *thd, String &result, sp_head *sp) @return SP_OK on success, or SP_DELETE_ROW_FAILED on error. used to indicate about errors. */ -static int -sp_drop_routine_internal(THD *thd, stored_procedure_type type, - const Database_qualified_name *name, TABLE *table) +int +Sp_handler::sp_drop_routine_internal(THD *thd, + const Database_qualified_name *name, + TABLE *table) const { DBUG_ENTER("sp_drop_routine_internal"); @@ -974,10 +1014,9 @@ sp_drop_routine_internal(THD *thd, stored_procedure_type type, local cache. */ sp_head *sp; - sp_cache **spc= (type == TYPE_ENUM_FUNCTION ? - &thd->sp_func_cache : &thd->sp_proc_cache); - sp= sp_cache_lookup(spc, name); - if (sp) + sp_cache **spc= get_cache(thd); + DBUG_ASSERT(spc); + if ((sp= sp_cache_lookup(spc, name))) sp_cache_flush_obsolete(spc, &sp); DBUG_RETURN(SP_OK); } @@ -990,8 +1029,6 @@ sp_drop_routine_internal(THD *thd, stored_procedure_type type, the mysql.proc. @param thd Thread context. - @param type Stored routine type - (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION). @param sp Stored routine object to store. @note Opens and closes the thread tables. Therefore assumes @@ -1008,7 +1045,7 @@ sp_drop_routine_internal(THD *thd, stored_procedure_type type, */ bool -sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) +Sp_handler::sp_create_routine(THD *thd, const sp_head *sp) const { LEX *lex= thd->lex; bool ret= TRUE; @@ -1016,8 +1053,6 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) char definer_buf[USER_HOST_BUFF_SIZE]; LEX_CSTRING definer; sql_mode_t saved_mode= thd->variables.sql_mode; - MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ? - MDL_key::FUNCTION : MDL_key::PROCEDURE; CHARSET_INFO *db_cs= get_default_db_collation(thd, sp->m_db.str); @@ -1025,16 +1060,15 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) bool store_failed= FALSE; DBUG_ENTER("sp_create_routine"); - DBUG_PRINT("enter", ("type: %d name: %.*s", (int) type, + DBUG_PRINT("enter", ("type: %s name: %.*s", + type_str(), (int) sp->m_name.length, sp->m_name.str)); + MDL_key::enum_mdl_namespace mdl_type= get_mdl_type(); LEX_CSTRING returns= empty_clex_str; String retstr(64); retstr.set_charset(system_charset_info); - DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE || - type == TYPE_ENUM_FUNCTION); - /* Grab an exclusive MDL lock. */ if (lock_object_name(thd, mdl_type, sp->m_db.str, sp->m_name.str)) { @@ -1062,17 +1096,17 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) if (!(table= open_proc_table_for_update(thd))) { - my_error(ER_SP_STORE_FAILED, MYF(0), SP_TYPE_STRING(type),sp->m_name.str); + my_error(ER_SP_STORE_FAILED, MYF(0), type_str(), sp->m_name.str); goto done; } else { /* Checking if the routine already exists */ - if (db_find_routine_aux(thd, type, sp, table) == SP_OK) + if (db_find_routine_aux(thd, sp, table) == SP_OK) { if (lex->create_info.or_replace()) { - if ((ret= sp_drop_routine_internal(thd, type, sp, table))) + if ((ret= sp_drop_routine_internal(thd, sp, table))) goto done; } else if (lex->create_info.if_not_exists()) @@ -1080,13 +1114,12 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_SP_ALREADY_EXISTS, ER_THD(thd, ER_SP_ALREADY_EXISTS), - SP_TYPE_STRING(type), - sp->m_name.str); + type_str(), sp->m_name.str); ret= FALSE; // Setting retstr as it is used for logging. - if (sp->m_type == TYPE_ENUM_FUNCTION) + if (type() == TYPE_ENUM_FUNCTION) { sp_returns_type(thd, retstr, sp); returns= retstr.lex_cstring(); @@ -1095,8 +1128,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) } else { - my_error(ER_SP_ALREADY_EXISTS, MYF(0), - SP_TYPE_STRING(type), sp->m_name.str); + my_error(ER_SP_ALREADY_EXISTS, MYF(0), type_str(), sp->m_name.str); goto done; } } @@ -1108,8 +1140,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) if (table->s->fields < MYSQL_PROC_FIELD_COUNT) { - my_error(ER_SP_STORE_FAILED, MYF(0), - SP_TYPE_STRING(type), sp->m_name.str); + my_error(ER_SP_STORE_FAILED, MYF(0), type_str(), sp->m_name.str); goto done; } @@ -1137,7 +1168,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) store_failed= store_failed || table->field[MYSQL_PROC_MYSQL_TYPE]-> - store((longlong)type, TRUE); + store((longlong) type(), true); store_failed= store_failed || table->field[MYSQL_PROC_FIELD_SPECIFIC_NAME]-> @@ -1165,7 +1196,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) table->field[MYSQL_PROC_FIELD_PARAM_LIST]-> store(sp->m_params, system_charset_info); - if (sp->m_type == TYPE_ENUM_FUNCTION) + if (type() == TYPE_ENUM_FUNCTION) { sp_returns_type(thd, retstr, sp); returns= retstr.lex_cstring(); @@ -1197,7 +1228,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) store(sp->comment(), system_charset_info); } - if ((sp->m_type == TYPE_ENUM_FUNCTION) && + if (type() == TYPE_ENUM_FUNCTION && !trust_function_creators && mysql_bin_log.is_open()) { if (!sp->detistic()) @@ -1255,8 +1286,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) if (table->file->ha_write_row(table->record[0])) { - my_error(ER_SP_ALREADY_EXISTS, MYF(0), - SP_TYPE_STRING(type), sp->m_name.str); + my_error(ER_SP_ALREADY_EXISTS, MYF(0), type_str(), sp->m_name.str); goto done; } /* Make change permanent and avoid 'table is marked as crashed' errors */ @@ -1274,7 +1304,6 @@ log: log_query.set_charset(system_charset_info); if (!show_create_sp(thd, &log_query, - sp->m_type, sp->m_explicit_name ? sp->m_db : null_clex_str, sp->m_name, sp->m_params, returns, sp->m_body, @@ -1314,8 +1343,6 @@ done: from the mysql.proc table and invalidates the stored-routine cache. @param thd Thread context. - @param type Stored routine type - (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION) @param name Stored routine name. @return Error code. SP_OK is returned on success. Other SP_ constants are @@ -1323,18 +1350,16 @@ done: */ int -sp_drop_routine(THD *thd, stored_procedure_type type, const sp_name *name) +Sp_handler::sp_drop_routine(THD *thd, + const Database_qualified_name *name) const { TABLE *table; int ret; - MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ? - MDL_key::FUNCTION : MDL_key::PROCEDURE; DBUG_ENTER("sp_drop_routine"); - DBUG_PRINT("enter", ("type: %d name: %.*s", - type, (int) name->m_name.length, name->m_name.str)); - - DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE || - type == TYPE_ENUM_FUNCTION); + DBUG_PRINT("enter", ("type: %s name: %.*s", + type_str(), + (int) name->m_name.length, name->m_name.str)); + MDL_key::enum_mdl_namespace mdl_type= get_mdl_type(); /* Grab an exclusive MDL lock. */ if (lock_object_name(thd, mdl_type, name->m_db.str, name->m_name.str)) @@ -1343,8 +1368,8 @@ sp_drop_routine(THD *thd, stored_procedure_type type, const sp_name *name) if (!(table= open_proc_table_for_update(thd))) DBUG_RETURN(SP_OPEN_TABLE_FAILED); - if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK) - ret= sp_drop_routine_internal(thd, type, name, table); + if ((ret= db_find_routine_aux(thd, name, table)) == SP_OK) + ret= sp_drop_routine_internal(thd, name, table); if (ret == SP_OK && write_bin_log(thd, TRUE, thd->query(), thd->query_length())) @@ -1367,8 +1392,6 @@ sp_drop_routine(THD *thd, stored_procedure_type type, const sp_name *name) successful update, the cache is invalidated. @param thd Thread context. - @param type Stored routine type - (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION) @param name Stored routine name. @param chistics New values of stored routine attributes to write. @@ -1377,20 +1400,16 @@ sp_drop_routine(THD *thd, stored_procedure_type type, const sp_name *name) */ int -sp_update_routine(THD *thd, stored_procedure_type type, const sp_name *name, - const st_sp_chistics *chistics) +Sp_handler::sp_update_routine(THD *thd, const Database_qualified_name *name, + const st_sp_chistics *chistics) const { TABLE *table; int ret; - MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ? - MDL_key::FUNCTION : MDL_key::PROCEDURE; DBUG_ENTER("sp_update_routine"); - DBUG_PRINT("enter", ("type: %d name: %.*s", - (int) type, + DBUG_PRINT("enter", ("type: %s name: %.*s", + type_str(), (int) name->m_name.length, name->m_name.str)); - - DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE || - type == TYPE_ENUM_FUNCTION); + MDL_key::enum_mdl_namespace mdl_type= get_mdl_type(); /* Grab an exclusive MDL lock. */ if (lock_object_name(thd, mdl_type, name->m_db.str, name->m_name.str)) @@ -1399,9 +1418,9 @@ sp_update_routine(THD *thd, stored_procedure_type type, const sp_name *name, if (!(table= open_proc_table_for_update(thd))) DBUG_RETURN(SP_OPEN_TABLE_FAILED); - if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK) + if ((ret= db_find_routine_aux(thd, name, table)) == SP_OK) { - if (type == TYPE_ENUM_FUNCTION && ! trust_function_creators && + if (type() == TYPE_ENUM_FUNCTION && ! trust_function_creators && mysql_bin_log.is_open() && (chistics->daccess == SP_CONTAINS_SQL || chistics->daccess == SP_MODIFIES_SQL_DATA)) @@ -1650,8 +1669,6 @@ err: calls sp_head::show_create_routine() for the object. @param thd Thread context. - @param type Stored routine type - (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION) @param name Stored routine name. @return Error status. @@ -1660,19 +1677,16 @@ err: */ bool -sp_show_create_routine(THD *thd, - stored_procedure_type type, const sp_name *name) +Sp_handler::sp_show_create_routine(THD *thd, + const Database_qualified_name *name) const { sp_head *sp; DBUG_ENTER("sp_show_create_routine"); - DBUG_PRINT("enter", ("name: %.*s", + DBUG_PRINT("enter", ("type: %s name: %.*s", + type_str(), (int) name->m_name.length, name->m_name.str)); - - DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE || - type == TYPE_ENUM_FUNCTION); - /* @todo: Consider using prelocking for this code as well. Currently SHOW CREATE PROCEDURE/FUNCTION is a dirty read of the data @@ -1680,17 +1694,16 @@ sp_show_create_routine(THD *thd, It is "safe" to do as long as it doesn't affect the results of the binary log or the query cache, which currently it does not. */ - if (sp_cache_routine(thd, type, name, FALSE, &sp)) + if (sp_cache_routine(thd, name, false, &sp)) DBUG_RETURN(TRUE); - if (sp == NULL || sp->show_create_routine(thd, type)) + if (sp == NULL || sp->show_create_routine(thd, this)) { /* If we have insufficient privileges, pretend the routine does not exist. */ - my_error(ER_SP_DOES_NOT_EXIST, MYF(0), stored_procedure_type_to_str(type), - name->m_name.str); + my_error(ER_SP_DOES_NOT_EXIST, MYF(0), type_str(), name->m_name.str); DBUG_RETURN(TRUE); } @@ -1703,7 +1716,6 @@ sp_show_create_routine(THD *thd, stored procedures cache and looking into mysql.proc if needed. @param thd thread context - @param type type of object (TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE) @param name name of procedure @param cp hash to look routine in @param cache_only if true perform cache-only lookup @@ -1716,18 +1728,16 @@ sp_show_create_routine(THD *thd, */ sp_head * -sp_find_routine(THD *thd, stored_procedure_type type, const sp_name *name, - sp_cache **cp, bool cache_only) +Sp_handler::sp_find_routine(THD *thd, const Database_qualified_name *name, + bool cache_only) const { + sp_cache **cp= get_cache(thd); sp_head *sp; - ulong depth= (type == TYPE_ENUM_PROCEDURE ? - thd->variables.max_sp_recursion_depth : - 0); DBUG_ENTER("sp_find_routine"); - DBUG_PRINT("enter", ("name: %.*s.%.*s type: %d cache only %d", + DBUG_PRINT("enter", ("name: %.*s.%.*s type: %s cache only %d", (int) name->m_db.length, name->m_db.str, (int) name->m_name.length, name->m_name.str, - type, cache_only)); + type_str(), cache_only)); if ((sp= sp_cache_lookup(cp, name))) { @@ -1750,9 +1760,9 @@ sp_find_routine(THD *thd, stored_procedure_type type, const sp_name *name, sp->m_first_free_instance->m_recursion_level, sp->m_first_free_instance->m_flags)); DBUG_ASSERT(!(sp->m_first_free_instance->m_flags & sp_head::IS_INVOKED)); - if (sp->m_first_free_instance->m_recursion_level > depth) + if (sp->m_first_free_instance->m_recursion_level > recursion_depth(thd)) { - sp->recursion_level_error(thd); + recursion_level_error(thd, sp); DBUG_RETURN(0); } DBUG_RETURN(sp->m_first_free_instance); @@ -1764,18 +1774,18 @@ sp_find_routine(THD *thd, stored_procedure_type type, const sp_name *name, */ level= sp->m_last_cached_sp->m_recursion_level + 1; - if (level > depth) + if (level > recursion_depth(thd)) { - sp->recursion_level_error(thd); + recursion_level_error(thd, sp); DBUG_RETURN(0); } - if (type == TYPE_ENUM_FUNCTION) + if (type() == TYPE_ENUM_FUNCTION) { sp_returns_type(thd, retstr, sp); returns= retstr.lex_cstring(); } - if (db_load_routine(thd, type, name, &new_sp, + if (db_load_routine(thd, name, &new_sp, sp->m_sql_mode, sp->m_params, returns, sp->m_body, sp->chistics(), sp->m_definer, @@ -1795,7 +1805,7 @@ sp_find_routine(THD *thd, stored_procedure_type type, const sp_name *name, } if (!cache_only) { - if (db_find_routine(thd, type, name, &sp) == SP_OK) + if (db_find_routine(thd, name, &sp) == SP_OK) { sp_cache_insert(cp, sp); DBUG_PRINT("info", ("added new: 0x%lx, level: %lu, flags %x", @@ -1813,8 +1823,6 @@ sp_find_routine(THD *thd, stored_procedure_type type, const sp_name *name, @param thd Thread handler @param routines List of needles in the hay stack - @param is_proc Indicates whether routines in the list are procedures - or functions. @return @retval FALSE Found. @@ -1822,7 +1830,7 @@ sp_find_routine(THD *thd, stored_procedure_type type, const sp_name *name, */ bool -sp_exist_routines(THD *thd, TABLE_LIST *routines, bool is_proc) +Sp_handler::sp_exist_routines(THD *thd, TABLE_LIST *routines) const { TABLE_LIST *routine; bool sp_object_found; @@ -1836,12 +1844,7 @@ sp_exist_routines(THD *thd, TABLE_LIST *routines, bool is_proc) thd->make_lex_string(&lex_name, routine->table_name, strlen(routine->table_name)); name= new sp_name(&lex_db, &lex_name, true); - sp_object_found= is_proc ? 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, name, false) != NULL; thd->get_stmt_da()->clear_warning_info(thd->query_id); if (! sp_object_found) { @@ -1932,20 +1935,18 @@ bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena, @param arena Arena in which memory for new element of the set will be allocated @param rt Routine name - @param rt_type Routine type (one of TYPE_ENUM_PROCEDURE/...) @note Will also add element to end of 'Query_tables_list::sroutines_list' list (and will take into account that this is an explicitly used routine). */ -void sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena, - const sp_name *rt, enum stored_procedure_type rt_type) +void Sp_handler::add_used_routine(Query_tables_list *prelocking_ctx, + Query_arena *arena, + const Database_qualified_name *rt) const { - MDL_key key((rt_type == TYPE_ENUM_FUNCTION) ? MDL_key::FUNCTION : - MDL_key::PROCEDURE, - rt->m_db.str, rt->m_name.str); - (void)sp_add_used_routine(prelocking_ctx, arena, &key, 0); + MDL_key key(get_mdl_type(), rt->m_db.str, rt->m_name.str); + (void) sp_add_used_routine(prelocking_ctx, arena, &key, 0); prelocking_ctx->sroutines_list_own_last= prelocking_ctx->sroutines_list.next; prelocking_ctx->sroutines_list_own_elements= prelocking_ctx->sroutines_list.elements; @@ -2072,24 +2073,24 @@ void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx, prelocking until 'sp_name' is eradicated as a class. */ -int sp_cache_routine(THD *thd, Sroutine_hash_entry *rt, - bool lookup_only, sp_head **sp) +int Sroutine_hash_entry::sp_cache_routine(THD *thd, + bool lookup_only, + sp_head **sp) const { char qname_buff[NAME_LEN*2+1+1]; - sp_name name(&rt->mdl_request.key, qname_buff); - MDL_key::enum_mdl_namespace mdl_type= rt->mdl_request.key.mdl_namespace(); - stored_procedure_type type= ((mdl_type == MDL_key::FUNCTION) ? - TYPE_ENUM_FUNCTION : TYPE_ENUM_PROCEDURE); - + sp_name name(&mdl_request.key, qname_buff); + MDL_key::enum_mdl_namespace mdl_type= mdl_request.key.mdl_namespace(); + const Sp_handler *sph= Sp_handler::handler(mdl_type); + DBUG_ASSERT(sph); /* Check that we have an MDL lock on this routine, unless it's a top-level CALL. The assert below should be unambiguous: the first element in sroutines_list has an MDL lock unless it's a top-level call, or a trigger, but triggers can't occur here (see the preceding assert). */ - DBUG_ASSERT(rt->mdl_request.ticket || rt == thd->lex->sroutines_list.first); + DBUG_ASSERT(mdl_request.ticket || this == thd->lex->sroutines_list.first); - return sp_cache_routine(thd, type, &name, lookup_only, sp); + return sph->sp_cache_routine(thd, &name, lookup_only, sp); } @@ -2100,7 +2101,6 @@ int sp_cache_routine(THD *thd, Sroutine_hash_entry *rt, loading. @param[in] thd Thread context. - @param[in] type Type of object (TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE). @param[in] name Name of routine. @param[in] lookup_only Only check that the routine is in the cache. If it's not, don't try to load. If it is present, @@ -2113,17 +2113,17 @@ int sp_cache_routine(THD *thd, Sroutine_hash_entry *rt, @retval non-0 Error while loading routine from mysql,proc table. */ -int sp_cache_routine(THD *thd, enum stored_procedure_type type, - const sp_name *name, - bool lookup_only, sp_head **sp) +int Sp_handler::sp_cache_routine(THD *thd, + const Database_qualified_name *name, + bool lookup_only, + sp_head **sp) const { int ret= 0; - sp_cache **spc= (type == TYPE_ENUM_FUNCTION ? - &thd->sp_func_cache : &thd->sp_proc_cache); + sp_cache **spc= get_cache(thd); DBUG_ENTER("sp_cache_routine"); - DBUG_ASSERT(type == TYPE_ENUM_FUNCTION || type == TYPE_ENUM_PROCEDURE); + DBUG_ASSERT(spc); *sp= sp_cache_lookup(spc, name); @@ -2137,7 +2137,7 @@ int sp_cache_routine(THD *thd, enum stored_procedure_type type, DBUG_RETURN(SP_OK); } - switch ((ret= db_find_routine(thd, type, name, sp))) + switch ((ret= db_find_routine(thd, name, sp))) { case SP_OK: sp_cache_insert(spc, *sp); @@ -2179,16 +2179,15 @@ int sp_cache_routine(THD *thd, enum stored_procedure_type type, Returns TRUE on success, FALSE on (alloc) failure. */ bool -show_create_sp(THD *thd, String *buf, - stored_procedure_type type, - const LEX_CSTRING &db, - const LEX_CSTRING &name, - const LEX_CSTRING ¶ms, - const LEX_CSTRING &returns, - const LEX_CSTRING &body, - const st_sp_chistics &chistics, - const AUTHID &definer, - sql_mode_t sql_mode) +Sp_handler::show_create_sp(THD *thd, String *buf, + const LEX_CSTRING &db, + const LEX_CSTRING &name, + const LEX_CSTRING ¶ms, + const LEX_CSTRING &returns, + const LEX_CSTRING &body, + const st_sp_chistics &chistics, + const AUTHID &definer, + sql_mode_t sql_mode) const { sql_mode_t old_sql_mode= thd->variables.sql_mode; /* Make some room to begin with */ @@ -2203,10 +2202,8 @@ show_create_sp(THD *thd, String *buf, if (thd->lex->create_info.or_replace()) buf->append(STRING_WITH_LEN("OR REPLACE ")); append_definer(thd, buf, &definer.user, &definer.host); - if (type == TYPE_ENUM_FUNCTION) - buf->append(STRING_WITH_LEN("FUNCTION ")); - else - buf->append(STRING_WITH_LEN("PROCEDURE ")); + buf->append(type_lex_cstring()); + buf->append(STRING_WITH_LEN(" ")); if (thd->lex->create_info.if_not_exists()) buf->append(STRING_WITH_LEN("IF NOT EXISTS ")); @@ -2219,7 +2216,7 @@ show_create_sp(THD *thd, String *buf, buf->append('('); buf->append(params); buf->append(')'); - if (type == TYPE_ENUM_FUNCTION) + if (type() == TYPE_ENUM_FUNCTION) { if (sql_mode & MODE_ORACLE) buf->append(STRING_WITH_LEN(" RETURN ")); @@ -2259,15 +2256,6 @@ show_create_sp(THD *thd, String *buf, } -static LEX_CSTRING empty_body_lex_cstring(stored_procedure_type type) -{ - static LEX_CSTRING empty_body_func= {C_STRING_WITH_LEN("RETURN NULL")}; - static LEX_CSTRING empty_body_proc= {C_STRING_WITH_LEN("BEGIN END")}; - return type == TYPE_ENUM_FUNCTION ? empty_body_func : - empty_body_proc; -} - - /** @brief The function loads sp_head struct for information schema purposes (used for I_S ROUTINES & PARAMETERS tables). @@ -2289,21 +2277,19 @@ static LEX_CSTRING empty_body_lex_cstring(stored_procedure_type type) */ sp_head * -sp_load_for_information_schema(THD *thd, TABLE *proc_table, - stored_procedure_type type, - const LEX_CSTRING &db, - const LEX_CSTRING &name, - const LEX_CSTRING ¶ms, - const LEX_CSTRING &returns, - sql_mode_t sql_mode, - bool *free_sp_head) +Sp_handler::sp_load_for_information_schema(THD *thd, TABLE *proc_table, + const LEX_CSTRING &db, + const LEX_CSTRING &name, + const LEX_CSTRING ¶ms, + const LEX_CSTRING &returns, + sql_mode_t sql_mode, + bool *free_sp_head) const { String defstr; const AUTHID definer= {{STRING_WITH_LEN("")}, {STRING_WITH_LEN("")}}; sp_head *sp; - sp_cache **spc= ((type == TYPE_ENUM_PROCEDURE) ? - &thd->sp_proc_cache : &thd->sp_func_cache); - sp_name sp_name_obj(&db, &name, true); + sp_cache **spc= get_cache(thd); + sp_name sp_name_obj(&db, &name, true); // This can change "name" *free_sp_head= 0; if ((sp= sp_cache_lookup(spc, &sp_name_obj))) { @@ -2314,9 +2300,9 @@ sp_load_for_information_schema(THD *thd, TABLE *proc_table, Stored_program_creation_ctx *creation_ctx= Stored_routine_creation_ctx::load_from_db(thd, &sp_name_obj, proc_table); defstr.set_charset(creation_ctx->get_client_cs()); - if (!show_create_sp(thd, &defstr, type, + if (!show_create_sp(thd, &defstr, sp_name_obj.m_db, sp_name_obj.m_name, - params, returns, empty_body_lex_cstring(type), + params, returns, empty_body_lex_cstring(), Sp_chistics(), definer, sql_mode)) return 0; diff --git a/sql/sp.h b/sql/sp.h index 6e9c032e4c4..060596bda1f 100644 --- a/sql/sp.h +++ b/sql/sp.h @@ -17,7 +17,10 @@ #ifndef _SP_H_ #define _SP_H_ +#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_string.h" // LEX_STRING +#include "sql_cmd.h" +#include "mdl.h" class Field; class Open_tables_backup; @@ -28,8 +31,10 @@ class Sroutine_hash_entry; class THD; class sp_cache; class sp_head; -class sp_name; +class sp_pcontext; +class Database_qualified_name; struct st_sp_chistics; +class Stored_program_creation_ctx; struct LEX; struct TABLE; struct TABLE_LIST; @@ -49,19 +54,268 @@ enum stored_procedure_type }; -static inline const char * -stored_procedure_type_to_str(enum stored_procedure_type type) +class Sp_handler +{ + int db_find_routine_aux(THD *thd, const Database_qualified_name *name, + TABLE *table) const; + int db_find_routine(THD *thd, const Database_qualified_name *name, + sp_head **sphp) const; + int db_load_routine(THD *thd, const Database_qualified_name *name, + sp_head **sphp, + sql_mode_t sql_mode, + const LEX_CSTRING ¶ms, + const LEX_CSTRING &returns, + const LEX_CSTRING &body, + const st_sp_chistics &chistics, + const AUTHID &definer, + longlong created, longlong modified, + Stored_program_creation_ctx *creation_ctx) const; + int sp_drop_routine_internal(THD *thd, + const Database_qualified_name *name, + TABLE *table) const; +public: + static const Sp_handler *handler(enum enum_sql_command cmd); + static const Sp_handler *handler(stored_procedure_type type); + static const Sp_handler *handler(MDL_key::enum_mdl_namespace ns); + const char *type_str() const { return type_lex_cstring().str; } + virtual const char *show_create_routine_col1_caption() const + { + DBUG_ASSERT(0); + return ""; + } + virtual const char *show_create_routine_col3_caption() const + { + DBUG_ASSERT(0); + return ""; + } + virtual stored_procedure_type type() const= 0; + virtual LEX_CSTRING type_lex_cstring() const= 0; + virtual LEX_CSTRING empty_body_lex_cstring() const + { + static LEX_CSTRING m_empty_body= {C_STRING_WITH_LEN("???")}; + DBUG_ASSERT(0); + return m_empty_body; + } + virtual MDL_key::enum_mdl_namespace get_mdl_type() const= 0; + virtual sp_cache **get_cache(THD *) const { return NULL; } +#ifndef NO_EMBEDDED_ACCESS_CHECKS + virtual HASH *get_priv_hash() const { return NULL; } +#endif + virtual ulong recursion_depth(THD *thd) const { return 0; } + /** + Return appropriate error about recursion limit reaching + + @param thd Thread handle + @param sp SP routine + + @remark For functions and triggers we return error about + prohibited recursion. For stored procedures we + return about reaching recursion limit. + */ + virtual void recursion_level_error(THD *thd, const sp_head *sp) const + { + my_error(ER_SP_NO_RECURSION, MYF(0)); + } + virtual bool add_instr_freturn(THD *thd, sp_head *sp, + sp_pcontext *spcont, + Item *item, LEX *lex) const; + virtual bool add_instr_preturn(THD *thd, sp_head *sp, + sp_pcontext *spcont) const; + + void add_used_routine(Query_tables_list *prelocking_ctx, + Query_arena *arena, + const Database_qualified_name *rt) const; + + sp_head *sp_find_routine(THD *thd, const Database_qualified_name *name, + bool cache_only) const; + int sp_cache_routine(THD *thd, const Database_qualified_name *name, + bool lookup_only, sp_head **sp) const; + + bool sp_exist_routines(THD *thd, TABLE_LIST *procs) const; + bool sp_show_create_routine(THD *thd, + const Database_qualified_name *name) const; + + bool sp_create_routine(THD *thd, const sp_head *sp) const; + + int sp_update_routine(THD *thd, const Database_qualified_name *name, + const st_sp_chistics *chistics) const; + + int sp_drop_routine(THD *thd, const Database_qualified_name *name) const; + + sp_head *sp_load_for_information_schema(THD *thd, TABLE *proc_table, + const LEX_CSTRING &db, + const LEX_CSTRING &name, + const LEX_CSTRING ¶ms, + const LEX_CSTRING &returns, + sql_mode_t sql_mode, + bool *free_sp_head) const; + + bool show_create_sp(THD *thd, String *buf, + const LEX_CSTRING &db, + const LEX_CSTRING &name, + const LEX_CSTRING ¶ms, + const LEX_CSTRING &returns, + const LEX_CSTRING &body, + const st_sp_chistics &chistics, + const AUTHID &definer, + sql_mode_t sql_mode) const; +}; + + +class Sp_handler_procedure: public Sp_handler +{ +public: + stored_procedure_type type() const { return TYPE_ENUM_PROCEDURE; } + LEX_CSTRING type_lex_cstring() const + { + static LEX_CSTRING m_type_str= {C_STRING_WITH_LEN("PROCEDURE")}; + return m_type_str; + } + LEX_CSTRING empty_body_lex_cstring() const + { + static LEX_CSTRING m_empty_body= {C_STRING_WITH_LEN("BEGIN END")}; + return m_empty_body; + } + const char *show_create_routine_col1_caption() const + { + return "Procedure"; + } + const char *show_create_routine_col3_caption() const + { + return "Create Procedure"; + } + MDL_key::enum_mdl_namespace get_mdl_type() const + { + return MDL_key::PROCEDURE; + } + sp_cache **get_cache(THD *) const; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + HASH *get_priv_hash() const; +#endif + ulong recursion_depth(THD *thd) const; + void recursion_level_error(THD *thd, const sp_head *sp) const; + bool add_instr_preturn(THD *thd, sp_head *sp, sp_pcontext *spcont) const; +}; + + +class Sp_handler_function: public Sp_handler +{ +public: + stored_procedure_type type() const { return TYPE_ENUM_FUNCTION; } + LEX_CSTRING type_lex_cstring() const + { + static LEX_CSTRING m_type_str= {C_STRING_WITH_LEN("FUNCTION")}; + return m_type_str; + } + LEX_CSTRING empty_body_lex_cstring() const + { + static LEX_CSTRING m_empty_body= {C_STRING_WITH_LEN("RETURN NULL")}; + return m_empty_body; + } + const char *show_create_routine_col1_caption() const + { + return "Function"; + } + const char *show_create_routine_col3_caption() const + { + return "Create Function"; + } + MDL_key::enum_mdl_namespace get_mdl_type() const + { + return MDL_key::FUNCTION; + } + sp_cache **get_cache(THD *) const; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + HASH *get_priv_hash() const; +#endif + bool add_instr_freturn(THD *thd, sp_head *sp, sp_pcontext *spcont, + Item *item, LEX *lex) const; +}; + + +class Sp_handler_trigger: public Sp_handler +{ +public: + stored_procedure_type type() const { return TYPE_ENUM_TRIGGER; } + LEX_CSTRING type_lex_cstring() const + { + static LEX_CSTRING m_type_str= {C_STRING_WITH_LEN("TRIGGER")}; + return m_type_str; + } + MDL_key::enum_mdl_namespace get_mdl_type() const + { + DBUG_ASSERT(0); + return MDL_key::TRIGGER; + } +}; + + +extern MYSQL_PLUGIN_IMPORT Sp_handler_function sp_handler_function; +extern MYSQL_PLUGIN_IMPORT Sp_handler_procedure sp_handler_procedure; +extern MYSQL_PLUGIN_IMPORT Sp_handler_trigger sp_handler_trigger; + + +inline const Sp_handler *Sp_handler::handler(enum_sql_command cmd) +{ + switch (cmd) { + case SQLCOM_CREATE_PROCEDURE: + case SQLCOM_ALTER_PROCEDURE: + case SQLCOM_DROP_PROCEDURE: + case SQLCOM_SHOW_PROC_CODE: + case SQLCOM_SHOW_CREATE_PROC: + case SQLCOM_SHOW_STATUS_PROC: + return &sp_handler_procedure; + case SQLCOM_CREATE_SPFUNCTION: + case SQLCOM_ALTER_FUNCTION: + case SQLCOM_DROP_FUNCTION: + case SQLCOM_SHOW_FUNC_CODE: + case SQLCOM_SHOW_CREATE_FUNC: + case SQLCOM_SHOW_STATUS_FUNC: + return &sp_handler_function; + default: + break; + } + return NULL; +} + + +inline const Sp_handler *Sp_handler::handler(stored_procedure_type type) { switch (type) { - case TYPE_ENUM_PROCEDURE: return "PROCEDURE"; - case TYPE_ENUM_FUNCTION: return "FUNCTION"; - case TYPE_ENUM_TRIGGER: return "TRIGGER"; - case TYPE_ENUM_PROXY: return "PROXY"; + case TYPE_ENUM_PROCEDURE: + return &sp_handler_procedure; + case TYPE_ENUM_FUNCTION: + return &sp_handler_function; + case TYPE_ENUM_TRIGGER: + return &sp_handler_trigger; + case TYPE_ENUM_PROXY: + break; } - DBUG_ASSERT(0); - return "UNKNOWN_STORED_"; + return NULL; } + +inline const Sp_handler *Sp_handler::handler(MDL_key::enum_mdl_namespace type) +{ + switch (type) { + case MDL_key::FUNCTION: + return &sp_handler_function; + case MDL_key::PROCEDURE: + return &sp_handler_procedure; + case MDL_key::GLOBAL: + case MDL_key::SCHEMA: + case MDL_key::TABLE: + case MDL_key::TRIGGER: + case MDL_key::EVENT: + case MDL_key::COMMIT: + case MDL_key::USER_LOCK: + case MDL_key::NAMESPACE_END: + break; + } + return NULL; +} + + /* Tells what SP_DEFAULT_ACCESS should be mapped to */ #define SP_DEFAULT_ACCESS_MAPPING SP_CONTAINS_SQL @@ -121,36 +375,6 @@ sp_drop_db_routines(THD *thd, const char *db); */ bool lock_db_routines(THD *thd, const char *db); -sp_head * -sp_find_routine(THD *thd, stored_procedure_type type, const sp_name *name, - sp_cache **cp, bool cache_only); - -int -sp_cache_routine(THD *thd, Sroutine_hash_entry *rt, - bool lookup_only, sp_head **sp); - - -int -sp_cache_routine(THD *thd, stored_procedure_type type, const sp_name *name, - bool lookup_only, sp_head **sp); - -bool -sp_exist_routines(THD *thd, TABLE_LIST *procs, bool is_proc); - -bool -sp_show_create_routine(THD *thd, stored_procedure_type type, const sp_name *name); - -bool -sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp); - -int -sp_update_routine(THD *thd, stored_procedure_type type, const sp_name *name, - const st_sp_chistics *chistics); - -int -sp_drop_routine(THD *thd, stored_procedure_type type, const sp_name *name); - - /** Structure that represents element in the set of stored routines used by statement or routine. @@ -185,14 +409,11 @@ public: changes. */ ulong m_sp_cache_version; + + int sp_cache_routine(THD *thd, bool lookup_only, sp_head **sp) const; }; -/* - Procedures for handling sets of stored routines used by statement or routine. -*/ -void sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena, - const sp_name *rt, stored_procedure_type rt_type); bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena, const MDL_key *key, TABLE_LIST *belong_to_view); void sp_remove_not_own_routines(Query_tables_list *prelocking_ctx); @@ -212,16 +433,6 @@ extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen, */ TABLE *open_proc_table_for_read(THD *thd, Open_tables_backup *backup); -sp_head * -sp_load_for_information_schema(THD *thd, TABLE *proc_table, - stored_procedure_type type, - const LEX_CSTRING &db, - const LEX_CSTRING &name, - const LEX_CSTRING ¶ms, - const LEX_CSTRING &returns, - sql_mode_t sql_mode, - bool *free_sp_head); - bool load_charset(MEM_ROOT *mem_root, Field *field, CHARSET_INFO *dflt_cs, @@ -234,16 +445,6 @@ bool load_collation(MEM_ROOT *mem_root, void sp_returns_type(THD *thd, String &result, - sp_head *sp); + const sp_head *sp); -bool show_create_sp(THD *thd, String *buf, - stored_procedure_type type, - const LEX_CSTRING &db, - const LEX_CSTRING &name, - const LEX_CSTRING ¶ms, - const LEX_CSTRING &returns, - const LEX_CSTRING &body, - const st_sp_chistics &chistics, - const AUTHID &definer, - sql_mode_t sql_mode); #endif /* _SP_H_ */ diff --git a/sql/sp_head.cc b/sql/sp_head.cc index ad45ccdc92e..9737169ede6 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -540,10 +540,10 @@ sp_head::operator delete(void *ptr, size_t size) throw() } -sp_head::sp_head(stored_procedure_type type) +sp_head::sp_head(const Sp_handler *sph) :Query_arena(&main_mem_root, STMT_INITIALIZED_FOR_SP), Database_qualified_name(&null_clex_str, &null_clex_str), - m_type(type), + m_handler(sph), m_flags(0), m_explicit_name(false), /* @@ -610,7 +610,7 @@ sp_head::init(LEX *lex) void -sp_head::init_sp_name(THD *thd, sp_name *spname) +sp_head::init_sp_name(THD *thd, const sp_name *spname) { DBUG_ENTER("sp_head::init_sp_name"); @@ -733,7 +733,7 @@ sp_head::~sp_head() Field * sp_head::create_result_field(uint field_max_length, const LEX_CSTRING *field_name, - TABLE *table) + TABLE *table) const { Field *field; LEX_CSTRING name; @@ -960,30 +960,15 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str) } -/** - Return appropriate error about recursion limit reaching - - @param thd Thread handle - - @remark For functions and triggers we return error about - prohibited recursion. For stored procedures we - return about reaching recursion limit. -*/ - -void sp_head::recursion_level_error(THD *thd) +void Sp_handler_procedure::recursion_level_error(THD *thd, + const sp_head *sp) const { - if (m_type == TYPE_ENUM_PROCEDURE) - { - my_error(ER_SP_RECURSION_LIMIT, MYF(0), - static_cast(thd->variables.max_sp_recursion_depth), - m_name.str); - } - else - my_error(ER_SP_NO_RECURSION, MYF(0)); + my_error(ER_SP_RECURSION_LIMIT, MYF(0), + static_cast(thd->variables.max_sp_recursion_depth), + sp->m_name.str); } - /** Execute the routine. The main instruction jump loop is there. Assume the parameters already set. @@ -1391,7 +1376,6 @@ sp_head::execute(THD *thd, bool merge_da_on_success) @param thd thread handle @param sp stored routine to change the context for - @param is_proc TRUE is procedure, FALSE if function @param save_ctx pointer to an old security context @todo @@ -1406,8 +1390,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success) */ bool -set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc, - Security_context **save_ctx) +set_routine_security_ctx(THD *thd, sp_head *sp, Security_context **save_ctx) { *save_ctx= 0; if (sp->suid() != SP_IS_NOT_SUID && @@ -1429,7 +1412,7 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc, */ if (*save_ctx && check_routine_access(thd, EXECUTE_ACL, - sp->m_db.str, sp->m_name.str, is_proc, FALSE)) + sp->m_db.str, sp->m_name.str, sp->m_handler, false)) { sp->m_security_ctx.restore_security_context(thd, *save_ctx); *save_ctx= 0; @@ -1450,20 +1433,17 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc, so we can omit the security context switch for performance purposes. @param thd - @param sphead - @param is_proc - @param root_pctx @param ret_value @retval NULL - error (access denided or EOM) @retval !NULL - success (the invoker has rights to all %TYPE tables) */ -sp_rcontext *sp_head::rcontext_create(THD *thd, bool is_proc, Field *ret_value) +sp_rcontext *sp_head::rcontext_create(THD *thd, Field *ret_value) { bool has_column_type_refs= m_flags & HAS_COLUMN_TYPE_REFS; #ifndef NO_EMBEDDED_ACCESS_CHECKS Security_context *save_security_ctx; if (has_column_type_refs && - set_routine_security_ctx(thd, this, is_proc, &save_security_ctx)) + set_routine_security_ctx(thd, this, &save_security_ctx)) return NULL; #endif sp_rcontext *res= sp_rcontext::create(thd, m_pcont, ret_value, @@ -1689,7 +1669,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, init_sql_alloc(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0, MYF(0)); thd->set_n_backup_active_arena(&call_arena, &backup_arena); - if (!(nctx= rcontext_create(thd, false, return_value_fld))) + if (!(nctx= rcontext_create(thd, return_value_fld))) { thd->restore_active_arena(&call_arena, &backup_arena); err_status= TRUE; @@ -1760,7 +1740,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, #ifndef NO_EMBEDDED_ACCESS_CHECKS Security_context *save_security_ctx; - if (set_routine_security_ctx(thd, this, FALSE, &save_security_ctx)) + if (set_routine_security_ctx(thd, this, &save_security_ctx)) { err_status= TRUE; goto err_with_cleanup; @@ -1904,7 +1884,7 @@ sp_head::execute_procedure(THD *thd, List *args) if (! octx) { /* Create a temporary old context. */ - if (!(octx= rcontext_create(thd, true, NULL))) + if (!(octx= rcontext_create(thd, NULL))) { DBUG_PRINT("error", ("Could not create octx")); DBUG_RETURN(TRUE); @@ -1919,7 +1899,7 @@ sp_head::execute_procedure(THD *thd, List *args) thd->spcont->callers_arena= thd; } - if (!(nctx= rcontext_create(thd, true, NULL))) + if (!(nctx= rcontext_create(thd, NULL))) { delete nctx; /* Delete nctx if it was init() that failed. */ thd->spcont= save_spcont; @@ -2042,7 +2022,7 @@ sp_head::execute_procedure(THD *thd, List *args) #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); + err_status= set_routine_security_ctx(thd, this, &save_security_ctx); #endif if (!err_status) @@ -2532,7 +2512,7 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access) thd->security_ctx->priv_host))); if (!*full_access) return check_some_routine_access(thd, sp->m_db.str, sp->m_name.str, - sp->m_type == TYPE_ENUM_PROCEDURE); + sp->m_handler); return 0; } @@ -2541,9 +2521,8 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access) Collect metadata for SHOW CREATE statement for stored routines. @param thd Thread context. - @param type Stored routine type - @param type Stored routine type - (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION) + @param sph Stored routine handler + @param fields Item list to populate @return Error status. @retval FALSE on success @@ -2551,13 +2530,11 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access) */ void -sp_head::show_create_routine_get_fields(THD *thd, int type, List *fields) +sp_head::show_create_routine_get_fields(THD *thd, const Sp_handler *sph, + List *fields) { - const char *col1_caption= type == TYPE_ENUM_PROCEDURE ? - "Procedure" : "Function"; - - const char *col3_caption= type == TYPE_ENUM_PROCEDURE ? - "Create Procedure" : "Create Function"; + const char *col1_caption= sph->show_create_routine_col1_caption(); + const char *col3_caption= sph->show_create_routine_col3_caption(); MEM_ROOT *mem_root= thd->mem_root; @@ -2604,8 +2581,7 @@ sp_head::show_create_routine_get_fields(THD *thd, int type, List *fields) Implement SHOW CREATE statement for stored routines. @param thd Thread context. - @param type Stored routine type - (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION) + @param sph Stored routine handler @return Error status. @retval FALSE on success @@ -2613,13 +2589,10 @@ sp_head::show_create_routine_get_fields(THD *thd, int type, List *fields) */ bool -sp_head::show_create_routine(THD *thd, int type) +sp_head::show_create_routine(THD *thd, const Sp_handler *sph) { - const char *col1_caption= type == TYPE_ENUM_PROCEDURE ? - "Procedure" : "Function"; - - const char *col3_caption= type == TYPE_ENUM_PROCEDURE ? - "Create Procedure" : "Create Function"; + const char *col1_caption= sph->show_create_routine_col1_caption(); + const char *col3_caption= sph->show_create_routine_col3_caption(); bool err_status; @@ -2634,9 +2607,6 @@ sp_head::show_create_routine(THD *thd, int type) DBUG_ENTER("sp_head::show_create_routine"); DBUG_PRINT("info", ("routine %s", m_name.str)); - DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE || - type == TYPE_ENUM_FUNCTION); - if (check_show_routine_access(thd, this, &full_access)) DBUG_RETURN(TRUE); @@ -2763,6 +2733,29 @@ bool sp_head::add_instr_jump_forward_with_backpatch(THD *thd, } +bool sp_head::add_instr_freturn(THD *thd, sp_pcontext *spcont, + Item *item, LEX *lex) +{ + sp_instr_freturn *i= new (thd->mem_root) + sp_instr_freturn(instructions(), spcont, item, + m_return_field_def.type_handler(), thd->lex); + if (i == NULL || add_instr(i)) + return true; + m_flags|= sp_head::HAS_RETURN; + return false; +} + + +bool sp_head::add_instr_preturn(THD *thd, sp_pcontext *spcont) +{ + sp_instr_preturn *i= new (thd->mem_root) + sp_instr_preturn(instructions(), spcont); + if (i == NULL || add_instr(i)) + return true; + return false; +} + + /* Replace an instruction at position to "no operation". diff --git a/sql/sp_head.h b/sql/sp_head.h index f17a918cf50..f1d89a083ab 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -40,11 +40,6 @@ @{ */ -// Values for the type enum. This reflects the order of the enum declaration -// in the CREATE TABLE command. -//#define TYPE_ENUM_FUNCTION 1 #define TYPE_ENUM_PROCEDURE 2 #define -//TYPE_ENUM_TRIGGER 3 #define TYPE_ENUM_PROXY 4 - Item::Type sp_map_item_type(enum enum_field_types type); @@ -173,7 +168,7 @@ public: HAS_COLUMN_TYPE_REFS= 8192 }; - stored_procedure_type m_type; + const Sp_handler *m_handler; uint m_flags; // Boolean attributes of a stored routine Column_definition m_return_field_def; /**< This is used for FUNCTIONs only. */ @@ -220,7 +215,7 @@ public: m_sp_cache_version= version_arg; } - sp_rcontext *rcontext_create(THD *thd, bool is_proc, Field *ret_value); + sp_rcontext *rcontext_create(THD *thd, Field *retval); private: /** @@ -317,7 +312,7 @@ public: static void operator delete(void *ptr, size_t size) throw (); - sp_head(stored_procedure_type type); + sp_head(const Sp_handler *handler); /// Initialize after we have reset mem_root void @@ -325,7 +320,7 @@ public: /** Copy sp name from parser. */ void - init_sp_name(THD *thd, sp_name *spname); + init_sp_name(THD *thd, const sp_name *spname); /** Set the body-definition start position. */ void @@ -350,10 +345,11 @@ public: execute_procedure(THD *thd, List *args); static void - show_create_routine_get_fields(THD *thd, int type, List *fields); + show_create_routine_get_fields(THD *thd, const Sp_handler *sph, + List *fields); bool - show_create_routine(THD *thd, int type); + show_create_routine(THD *thd, const Sp_handler *sph); MEM_ROOT *get_main_mem_root() { return &main_mem_root; } @@ -376,6 +372,12 @@ public: spcont->last_label()); } + bool + add_instr_freturn(THD *thd, sp_pcontext *spcont, Item *item, LEX *lex); + + bool + add_instr_preturn(THD *thd, sp_pcontext *spcont); + Item *adjust_assignment_source(THD *thd, Item *val, Item *val2); /** @param thd - the current thd @@ -622,7 +624,7 @@ public: char *create_string(THD *thd, ulong *lenp); Field *create_result_field(uint field_max_length, const LEX_CSTRING *field_name, - TABLE *table); + TABLE *table) const; /** @@ -718,8 +720,6 @@ public: */ void add_mark_lead(uint ip, List *leads); - void recursion_level_error(THD *thd); - inline sp_instr * get_instr(uint i) { @@ -1864,8 +1864,7 @@ 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); +set_routine_security_ctx(THD *thd, sp_head *sp, Security_context **save_ctx); #endif /* NO_EMBEDDED_ACCESS_CHECKS */ TABLE_LIST * diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 79acb996740..c2b2bdc54b5 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -754,6 +754,19 @@ static int traverse_role_graph_down(ACL_USER_BASE *, void *, int (*) (ACL_USER_BASE *, void *), int (*) (ACL_USER_BASE *, ACL_ROLE *, void *)); + +HASH *Sp_handler_procedure::get_priv_hash() const +{ + return &proc_priv_hash; +} + + +HASH *Sp_handler_function::get_priv_hash() const +{ + return &func_priv_hash; +} + + /* Enumeration of ACL/GRANT tables in the mysql database */ @@ -4973,10 +4986,11 @@ static GRANT_NAME *name_hash_search(HASH *name_hash, static GRANT_NAME * routine_hash_search(const char *host, const char *ip, const char *db, - const char *user, const char *tname, bool proc, bool exact) + const char *user, const char *tname, const Sp_handler *sph, + bool exact) { return (GRANT_TABLE*) - name_hash_search(proc ? &proc_priv_hash : &func_priv_hash, + name_hash_search(sph->get_priv_hash(), host, ip, db, user, tname, exact, TRUE); } @@ -5351,13 +5365,14 @@ table_error: static int replace_routine_table(THD *thd, GRANT_NAME *grant_name, TABLE *table, const LEX_USER &combo, const char *db, const char *routine_name, - bool is_proc, ulong rights, bool revoke_grant) + const Sp_handler *sph, + ulong rights, bool revoke_grant) { char grantor[USER_HOST_BUFF_SIZE]; int old_row_exists= 1; int error=0; ulong store_proc_rights; - HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash; + HASH *hash= sph->get_priv_hash(); DBUG_ENTER("replace_routine_table"); if (revoke_grant && !grant_name->init_privs) // only inherited role privs @@ -5381,9 +5396,7 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name, table->field[2]->store(combo.user.str,combo.user.length, &my_charset_latin1); table->field[3]->store(routine_name,(uint) strlen(routine_name), &my_charset_latin1); - table->field[4]->store((longlong)(is_proc ? - TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION), - TRUE); + table->field[4]->store((longlong) sph->type(), true); store_record(table,record[1]); // store at pos 1 if (table->file->ha_index_read_idx_map(table->record[0], 0, @@ -5503,6 +5516,23 @@ struct PRIVS_TO_MERGE const char *db, *name; }; + +static enum PRIVS_TO_MERGE::what sp_privs_to_merge(stored_procedure_type type) +{ + switch (type) { + case TYPE_ENUM_FUNCTION: + return PRIVS_TO_MERGE::FUNC; + case TYPE_ENUM_PROCEDURE: + return PRIVS_TO_MERGE::PROC; + case TYPE_ENUM_TRIGGER: + case TYPE_ENUM_PROXY: + break; + } + DBUG_ASSERT(0); + return PRIVS_TO_MERGE::PROC; +} + + static int init_role_for_merging(ACL_ROLE *role, void *context) { role->counter= 0; @@ -6627,7 +6657,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, @param thd Thread handle @param table_list List of routines to give grant - @param is_proc Is this a list of procedures? + @param sph SP handler @param user_list List of users to give grant @param rights Table level grant @param revoke_grant Is this is a REVOKE command? @@ -6637,7 +6667,8 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, @retval TRUE An error occurred. */ -bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, +bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, + const Sp_handler *sph, List &user_list, ulong rights, bool revoke_grant, bool write_to_binlog) { @@ -6657,7 +6688,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, if (!revoke_grant) { - if (sp_exist_routines(thd, table_list, is_proc)) + if (sph->sp_exist_routines(thd, table_list)) DBUG_RETURN(TRUE); } @@ -6698,7 +6729,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, db_name= table_list->db; table_name= table_list->table_name; grant_name= routine_hash_search(Str->host.str, NullS, db_name, - Str->user.str, table_name, is_proc, 1); + Str->user.str, table_name, sph, 1); if (!grant_name || !grant_name->init_privs) { if (revoke_grant) @@ -6712,8 +6743,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, Str->user.str, table_name, rights, TRUE); if (!grant_name || - my_hash_insert(is_proc ? - &proc_priv_hash : &func_priv_hash,(uchar*) grant_name)) + my_hash_insert(sph->get_priv_hash(), (uchar*) grant_name)) { result= TRUE; continue; @@ -6724,7 +6754,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, instead of TABLE directly. */ if (tables.procs_priv_table().no_such_table() || replace_routine_table(thd, grant_name, tables.procs_priv_table().table(), - *Str, db_name, table_name, is_proc, rights, + *Str, db_name, table_name, sph, rights, revoke_grant) != 0) { result= TRUE; @@ -6732,7 +6762,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, } if (Str->is_role()) propagate_role_grants(find_acl_role(Str->user.str), - is_proc ? PRIVS_TO_MERGE::PROC : PRIVS_TO_MERGE::FUNC, + sp_privs_to_merge(sph->type()), db_name, table_name); } thd->mem_root= old_root; @@ -7341,16 +7371,10 @@ static bool grant_load(THD *thd, continue; } } - if (procs_priv.routine_type()->val_int() == TYPE_ENUM_PROCEDURE) - { - hash= &proc_priv_hash; - } - else - if (procs_priv.routine_type()->val_int() == TYPE_ENUM_FUNCTION) - { - hash= &func_priv_hash; - } - else + uint type= procs_priv.routine_type()->val_int(); + const Sp_handler *sph= Sp_handler::handler((stored_procedure_type) + type); + if (!sph || !(hash= sph->get_priv_hash())) { sql_print_warning("'procs_priv' entry '%s' " "ignored, bad routine type", @@ -8088,7 +8112,7 @@ bool check_grant_db(THD *thd, const char *db) thd Thread handler want_access Bits of privileges user needs to have procs List of routines to check. The user should have 'want_access' - is_proc True if the list is all procedures, else functions + sph SP handler no_errors If 0 then we write an error. The error is sent directly to the client @@ -8098,7 +8122,8 @@ bool check_grant_db(THD *thd, const char *db) ****************************************************************************/ bool check_grant_routine(THD *thd, ulong want_access, - TABLE_LIST *procs, bool is_proc, bool no_errors) + TABLE_LIST *procs, const Sp_handler *sph, + bool no_errors) { TABLE_LIST *table; Security_context *sctx= thd->security_ctx; @@ -8116,12 +8141,12 @@ bool check_grant_routine(THD *thd, ulong want_access, { GRANT_NAME *grant_proc; if ((grant_proc= routine_hash_search(host, sctx->ip, table->db, user, - table->table_name, is_proc, 0))) + table->table_name, sph, 0))) table->grant.privilege|= grant_proc->privs; if (role[0]) /* current role set check */ { if ((grant_proc= routine_hash_search("", NULL, table->db, role, - table->table_name, is_proc, 0))) + table->table_name, sph, 0))) table->grant.privilege|= grant_proc->privs; } @@ -8170,7 +8195,7 @@ err: */ bool check_routine_level_acl(THD *thd, const char *db, const char *name, - bool is_proc) + const Sp_handler *sph) { bool no_routine_acl= 1; GRANT_NAME *grant_proc; @@ -8179,7 +8204,7 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name, if ((grant_proc= routine_hash_search(sctx->priv_host, sctx->ip, db, sctx->priv_user, - name, is_proc, 0))) + name, sph, 0))) no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS); if (no_routine_acl && sctx->priv_role[0]) /* current set role check */ @@ -8187,7 +8212,7 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name, if ((grant_proc= routine_hash_search("", NULL, db, sctx->priv_role, - name, is_proc, 0))) + name, sph, 0))) no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS); } mysql_rwlock_unlock(&LOCK_grant); @@ -10503,6 +10528,45 @@ int mysql_alter_user(THD* thd, List &users_list) DBUG_RETURN(result); } + +static bool +mysql_revoke_sp_privs(THD *thd, + Grant_tables *tables, + const Sp_handler *sph, + const LEX_USER *lex_user) +{ + bool rc= false; + uint counter, revoked; + do { + HASH *hash= sph->get_priv_hash(); + for (counter= 0, revoked= 0 ; counter < hash->records ; ) + { + const char *user,*host; + GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter); + user= safe_str(grant_proc->user); + host= safe_str(grant_proc->host.hostname); + + if (!strcmp(lex_user->user.str, user) && + !strcmp(lex_user->host.str, host)) + { + if (replace_routine_table(thd, grant_proc, + tables->procs_priv_table().table(), + *lex_user, + grant_proc->db, grant_proc->tname, + sph, ~(ulong)0, 1) == 0) + { + revoked= 1; + continue; + } + rc= true; // Something went wrong + } + counter++; + } + } while (revoked); + return rc; +} + + /* Revoke all privileges from a list of users. @@ -10519,7 +10583,7 @@ int mysql_alter_user(THD* thd, List &users_list) bool mysql_revoke_all(THD *thd, List &list) { - uint counter, revoked, is_proc; + uint counter, revoked; int result; ACL_DB *acl_db; DBUG_ENTER("mysql_revoke_all"); @@ -10648,32 +10712,9 @@ bool mysql_revoke_all(THD *thd, List &list) } while (revoked); /* Remove procedure access */ - for (is_proc=0; is_proc<2; is_proc++) do { - HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash; - for (counter= 0, revoked= 0 ; counter < hash->records ; ) - { - const char *user,*host; - GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter); - user= safe_str(grant_proc->user); - host= safe_str(grant_proc->host.hostname); - - if (!strcmp(lex_user->user.str,user) && - !strcmp(lex_user->host.str, host)) - { - if (replace_routine_table(thd, grant_proc, - tables.procs_priv_table().table(), - *lex_user, - grant_proc->db, grant_proc->tname, - is_proc, ~(ulong)0, 1) == 0) - { - revoked= 1; - continue; - } - result= -1; // Something went wrong - } - counter++; - } - } while (revoked); + if (mysql_revoke_sp_privs(thd, &tables, &sp_handler_function, lex_user) || + mysql_revoke_sp_privs(thd, &tables, &sp_handler_procedure, lex_user)) + result= -1; ACL_USER_BASE *user_or_role; /* remove role grants */ @@ -10825,11 +10866,11 @@ Silence_routine_definer_errors::handle_condition( */ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, - bool is_proc) + const Sp_handler *sph) { uint counter, revoked; int result; - HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash; + HASH *hash= sph->get_priv_hash(); Silence_routine_definer_errors error_handler; DBUG_ENTER("sp_revoke_privileges"); @@ -10865,7 +10906,7 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, if (replace_routine_table(thd, grant_proc, tables.procs_priv_table().table(), lex_user, grant_proc->db, grant_proc->tname, - is_proc, ~(ulong)0, 1) == 0) + sph, ~(ulong)0, 1) == 0) { revoked= 1; continue; @@ -10890,7 +10931,7 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, @param thd The current thread. @param sp_db @param sp_name - @param is_proc + @param sph @return @retval FALSE Success @@ -10898,7 +10939,7 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, */ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, - bool is_proc) + const Sp_handler *sph) { Security_context *sctx= thd->security_ctx; LEX_USER *combo; @@ -10960,7 +11001,7 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, as all errors will be handled later. */ thd->push_internal_handler(&error_handler); - result= mysql_routine_grant(thd, tables, is_proc, user_list, + result= mysql_routine_grant(thd, tables, sph, user_list, DEFAULT_CREATE_PROC_ACLS, FALSE, FALSE); thd->pop_internal_handler(); DBUG_RETURN(result); @@ -11746,7 +11787,7 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant, ****************************************************************************/ bool check_routine_level_acl(THD *thd, const char *db, const char *name, - bool is_proc) + const Sp_handler *sph) { return FALSE; } diff --git a/sql/sql_acl.h b/sql/sql_acl.h index f9646649c93..6164c6fa57d 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -215,7 +215,7 @@ bool mysql_grant(THD *thd, const char *db, List &user_list, int mysql_table_grant(THD *thd, TABLE_LIST *table, List &user_list, List &column_list, ulong rights, bool revoke); -bool mysql_routine_grant(THD *thd, TABLE_LIST *table, bool is_proc, +bool mysql_routine_grant(THD *thd, TABLE_LIST *table, const Sp_handler *sph, List &user_list, ulong rights, bool revoke, bool write_to_binlog); bool grant_init(); @@ -231,7 +231,8 @@ bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref, bool check_grant_all_columns(THD *thd, ulong want_access, Field_iterator_table_ref *fields); bool check_grant_routine(THD *thd, ulong want_access, - TABLE_LIST *procs, bool is_proc, bool no_error); + TABLE_LIST *procs, const Sp_handler *sph, + bool no_error); bool check_grant_db(THD *thd,const char *db); bool check_global_access(THD *thd, ulong want_access, bool no_errors= false); bool check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, @@ -257,11 +258,11 @@ bool mysql_revoke_all(THD *thd, List &list); void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant, const char *db, const char *table); bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, - bool is_proc); + const Sp_handler *sph); bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, - bool is_proc); + const Sp_handler *sph); bool check_routine_level_acl(THD *thd, const char *db, const char *name, - bool is_proc); + const Sp_handler *sph); bool is_acl_user(const char *host, const char *user); int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond); int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 7041e22075b..60247c83a08 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -3127,7 +3127,7 @@ open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx, DEBUG_SYNC(thd, "after_shared_lock_pname"); /* Ensures the routine is up-to-date and cached, if exists. */ - if (sp_cache_routine(thd, rt, has_prelocking_list, &sp)) + if (rt->sp_cache_routine(thd, has_prelocking_list, &sp)) DBUG_RETURN(TRUE); /* Remember the version of the routine in the parse tree. */ @@ -3152,7 +3152,7 @@ open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx, Validating routine version is unnecessary, since CALL does not affect the prepared statement prelocked list. */ - if (sp_cache_routine(thd, rt, FALSE, &sp)) + if (rt->sp_cache_routine(thd, false, &sp)) DBUG_RETURN(TRUE); } } diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 144ff021ea3..ebaa0ee6eb5 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -5149,7 +5149,7 @@ bool LEX::init_internal_variable(struct sys_var_with_base *variable, bool LEX::is_trigger_new_or_old_reference(const LEX_CSTRING *name) { - return sphead && sphead->m_type == TYPE_ENUM_TRIGGER && + return sphead && sphead->m_handler->type() == TYPE_ENUM_TRIGGER && name->length == 3 && (!my_strcasecmp(system_charset_info, name->str, "NEW") || !my_strcasecmp(system_charset_info, name->str, "OLD")); @@ -5825,13 +5825,13 @@ sp_name *LEX::make_sp_name(THD *thd, LEX_CSTRING *name1, LEX_CSTRING *name2) } -sp_head *LEX::make_sp_head(THD *thd, sp_name *name, - enum stored_procedure_type type) +sp_head *LEX::make_sp_head(THD *thd, const sp_name *name, + const Sp_handler *sph) { sp_head *sp; /* Order is important here: new - reset - init */ - if ((sp= new sp_head(type))) + if ((sp= new sp_head(sph))) { sp->reset_thd_mem_root(thd); sp->init(this); @@ -6127,7 +6127,7 @@ bool LEX::maybe_start_compound_statement(THD *thd) { if (!sphead) { - if (!make_sp_head(thd, NULL, TYPE_ENUM_PROCEDURE)) + if (!make_sp_head(thd, NULL, &sp_handler_procedure)) return true; sphead->set_suid(SP_IS_NOT_SUID); sphead->set_body_start(thd, thd->m_parser_state->m_lip.get_cpp_ptr()); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index c1351b110f6..b72f52c5e4d 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -3156,24 +3156,22 @@ public: void set_stmt_init(); sp_name *make_sp_name(THD *thd, LEX_CSTRING *name); sp_name *make_sp_name(THD *thd, LEX_CSTRING *name1, LEX_CSTRING *name2); - sp_head *make_sp_head(THD *thd, sp_name *name, - enum stored_procedure_type type); - sp_head *make_sp_head_no_recursive(THD *thd, sp_name *name, - enum stored_procedure_type type) + sp_head *make_sp_head(THD *thd, const sp_name *name, const Sp_handler *sph); + sp_head *make_sp_head_no_recursive(THD *thd, const sp_name *name, + const Sp_handler *sph) { if (!sphead) - return make_sp_head(thd, name, type); - my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), - stored_procedure_type_to_str(type)); + return make_sp_head(thd, name, sph); + my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), sph->type_str()); return NULL; } sp_head *make_sp_head_no_recursive(THD *thd, DDL_options_st options, sp_name *name, - enum stored_procedure_type type) + const Sp_handler *sph) { if (add_create_options_with_check(options)) return NULL; - return make_sp_head_no_recursive(thd, name, type); + return make_sp_head_no_recursive(thd, name, sph); } bool init_internal_variable(struct sys_var_with_base *variable, const LEX_CSTRING *name); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index b63f804b086..056f4cefe16 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2937,13 +2937,13 @@ static int mysql_create_routine(THD *thd, LEX *lex) { if (check_routine_access(thd, ALTER_PROC_ACL, lex->sphead->m_db.str, lex->sphead->m_name.str, - lex->sql_command == SQLCOM_CREATE_PROCEDURE, 0)) + Sp_handler::handler(lex->sql_command), 0)) return true; } const LEX_CSTRING *name= lex->sphead->name(); #ifdef HAVE_DLOPEN - if (lex->sphead->m_type == TYPE_ENUM_FUNCTION) + if (lex->sphead->m_handler->type() == TYPE_ENUM_FUNCTION) { udf_func *udf = find_udf(name->str, name->length); @@ -2959,7 +2959,7 @@ static int mysql_create_routine(THD *thd, LEX *lex) return true; WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) - if (!sp_create_routine(thd, lex->sphead->m_type, lex->sphead)) + if (!lex->sphead->m_handler->sp_create_routine(thd, lex->sphead)) { #ifndef NO_EMBEDDED_ACCESS_CHECKS /* only add privileges if really neccessary */ @@ -3006,10 +3006,10 @@ static int mysql_create_routine(THD *thd, LEX *lex) if (sp_automatic_privileges && !opt_noacl && check_routine_access(thd, DEFAULT_CREATE_PROC_ACLS, lex->sphead->m_db.str, name->str, - lex->sql_command == SQLCOM_CREATE_PROCEDURE, 1)) + Sp_handler::handler(lex->sql_command), 1)) { if (sp_grant_privileges(thd, lex->sphead->m_db.str, name->str, - lex->sql_command == SQLCOM_CREATE_PROCEDURE)) + Sp_handler::handler(lex->sql_command))) push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_PROC_AUTO_GRANT_FAIL, ER_THD(thd, ER_PROC_AUTO_GRANT_FAIL)); thd->clear_error(); @@ -5353,16 +5353,16 @@ end_with_restore_list: if (lex->type == TYPE_ENUM_PROCEDURE || lex->type == TYPE_ENUM_FUNCTION) { + const Sp_handler *sph= Sp_handler::handler((stored_procedure_type) + lex->type); uint grants= lex->all_privileges ? (PROC_ACLS & ~GRANT_ACL) | (lex->grant & GRANT_ACL) : lex->grant; - if (check_grant_routine(thd, grants | GRANT_ACL, all_tables, - lex->type == TYPE_ENUM_PROCEDURE, 0)) + if (check_grant_routine(thd, grants | GRANT_ACL, all_tables, sph, 0)) goto error; /* Conditionally writes to binlog */ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) - res= mysql_routine_grant(thd, all_tables, - lex->type == TYPE_ENUM_PROCEDURE, + res= mysql_routine_grant(thd, all_tables, sph, lex->users_list, grants, lex->sql_command == SQLCOM_REVOKE, TRUE); if (!res) @@ -5768,15 +5768,15 @@ end_with_restore_list: goto error; if (check_routine_access(thd, EXECUTE_ACL, lex->spname->m_db.str, - lex->spname->m_name.str, TRUE, FALSE)) + lex->spname->m_name.str, &sp_handler_procedure, + false)) goto error; /* By this moment all needed SPs should be in cache so no need to look into DB. */ - if (!(sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname, - &thd->sp_proc_cache, TRUE))) + if (!(sp= sp_handler_procedure.sp_find_routine(thd, lex->spname, true))) { /* sp_find_routine can have issued an ER_SP_RECURSION_LIMIT error. @@ -5821,13 +5821,9 @@ end_with_restore_list: case SQLCOM_ALTER_FUNCTION: { int sp_result; - enum stored_procedure_type type; - type= (lex->sql_command == SQLCOM_ALTER_PROCEDURE ? - TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION); - + const Sp_handler *sph= Sp_handler::handler(lex->sql_command); if (check_routine_access(thd, ALTER_PROC_ACL, lex->spname->m_db.str, - lex->spname->m_name.str, - lex->sql_command == SQLCOM_ALTER_PROCEDURE, 0)) + lex->spname->m_name.str, sph, 0)) goto error; /* @@ -5837,7 +5833,7 @@ end_with_restore_list: already puts on CREATE FUNCTION. */ /* Conditionally writes to binlog */ - sp_result= sp_update_routine(thd, type, lex->spname, &lex->sp_chistics); + sp_result= sph->sp_update_routine(thd, lex->spname, &lex->sp_chistics); switch (sp_result) { case SP_OK: @@ -5899,19 +5895,17 @@ end_with_restore_list: #endif int sp_result; - enum stored_procedure_type type; - type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ? - TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION); + const Sp_handler *sph= Sp_handler::handler(lex->sql_command); const char *db= lex->spname->m_db.str; const char *name= lex->spname->m_name.str; if (check_routine_access(thd, ALTER_PROC_ACL, db, name, - lex->sql_command == SQLCOM_DROP_PROCEDURE, 0)) + Sp_handler::handler(lex->sql_command), 0)) goto error; WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) /* Conditionally writes to binlog */ - sp_result= sp_drop_routine(thd, type, lex->spname); + sp_result= sph->sp_drop_routine(thd, lex->spname); #ifndef NO_EMBEDDED_ACCESS_CHECKS /* @@ -5935,7 +5929,7 @@ end_with_restore_list: if (sp_result != SP_KEY_NOT_FOUND && sp_automatic_privileges && !opt_noacl && sp_revoke_privileges(thd, db, name, - lex->sql_command == SQLCOM_DROP_PROCEDURE)) + Sp_handler::handler(lex->sql_command))) { push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_PROC_AUTO_REVOKE_FAIL, @@ -5973,21 +5967,14 @@ end_with_restore_list: break; } case SQLCOM_SHOW_CREATE_PROC: - { -#ifdef WITH_WSREP - if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd)) goto error; -#endif /* WITH_WSREP */ - if (sp_show_create_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname)) - goto error; - break; - } case SQLCOM_SHOW_CREATE_FUNC: { + const Sp_handler *sph= Sp_handler::handler(lex->sql_command); #ifdef WITH_WSREP if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd)) goto error; #endif /* WITH_WSREP */ - if (sp_show_create_routine(thd, TYPE_ENUM_FUNCTION, lex->spname)) - goto error; + if (sph->sp_show_create_routine(thd, lex->spname)) + goto error; break; } case SQLCOM_SHOW_PROC_CODE: @@ -5995,13 +5982,11 @@ end_with_restore_list: { #ifndef DBUG_OFF sp_head *sp; - stored_procedure_type type= (lex->sql_command == SQLCOM_SHOW_PROC_CODE ? - TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION); - + const Sp_handler *sph= Sp_handler::handler(lex->sql_command); #ifdef WITH_WSREP if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd)) goto error; #endif /* WITH_WSREP */ - if (sp_cache_routine(thd, type, lex->spname, FALSE, &sp)) + if (sph->sp_cache_routine(thd, lex->spname, false, &sp)) goto error; if (!sp || sp->show_routine_code(thd)) { @@ -7061,7 +7046,7 @@ deny: bool check_routine_access(THD *thd, ulong want_access, const char *db, const char *name, - bool is_proc, bool no_errors) + const Sp_handler *sph, bool no_errors) { TABLE_LIST tables[1]; @@ -7090,7 +7075,7 @@ check_routine_access(THD *thd, ulong want_access, const char *db, 0, no_errors)) return TRUE; - return check_grant_routine(thd, want_access, tables, is_proc, no_errors); + return check_grant_routine(thd, want_access, tables, sph, no_errors); } @@ -7108,7 +7093,7 @@ check_routine_access(THD *thd, ulong want_access, const char *db, */ bool check_some_routine_access(THD *thd, const char *db, const char *name, - bool is_proc) + const Sp_handler *sph) { ulong save_priv; /* @@ -7125,7 +7110,7 @@ bool check_some_routine_access(THD *thd, const char *db, const char *name, if (!check_access(thd, SHOW_PROC_ACLS, db, &save_priv, NULL, 0, 1) || (save_priv & SHOW_PROC_ACLS)) return FALSE; - return check_routine_level_acl(thd, db, name, is_proc); + return check_routine_level_acl(thd, db, name, sph); } diff --git a/sql/sql_parse.h b/sql/sql_parse.h index bf241d04e7b..c97d824ffa1 100644 --- a/sql/sql_parse.h +++ b/sql/sql_parse.h @@ -152,9 +152,10 @@ bool check_single_table_access(THD *thd, ulong privilege, TABLE_LIST *tables, bool no_errors); bool check_routine_access(THD *thd,ulong want_access,const char *db, const char *name, - bool is_proc, bool no_errors); + const Sp_handler *sph, bool no_errors); bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table); -bool check_some_routine_access(THD *thd, const char *db, const char *name, bool is_proc); +bool check_some_routine_access(THD *thd, const char *db, const char *name, + const Sp_handler *sph); bool check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables, bool any_combination_of_privileges_will_do, uint number, @@ -166,7 +167,8 @@ inline bool check_single_table_access(THD *thd, ulong privilege, TABLE_LIST *tables, bool no_errors) { return false; } inline bool check_routine_access(THD *thd,ulong want_access, const char *db, - const char *name, bool is_proc, bool no_errors) + const char *name, + const Sp_handler *sph, bool no_errors) { return false; } inline bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table) { @@ -174,7 +176,8 @@ inline bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table) return false; } inline bool check_some_routine_access(THD *thd, const char *db, - const char *name, bool is_proc) + const char *name, + const Sp_handler *sph) { return false; } inline bool check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables, diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 1095c6ead99..997ff2aeb38 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -2040,13 +2040,14 @@ static int mysql_test_show_binlogs(Prepared_statement *stmt) TRUE error, error message is set in THD */ -static int mysql_test_show_create_routine(Prepared_statement *stmt, int type) +static int mysql_test_show_create_routine(Prepared_statement *stmt, + const Sp_handler *sph) { DBUG_ENTER("mysql_test_show_binlogs"); THD *thd= stmt->thd; List fields; - sp_head::show_create_routine_get_fields(thd, type, &fields); + sp_head::show_create_routine_get_fields(thd, sph, &fields); DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields)); } @@ -2436,14 +2437,14 @@ static bool check_prepared_statement(Prepared_statement *stmt) break; #endif /* EMBEDDED_LIBRARY */ case SQLCOM_SHOW_CREATE_PROC: - if ((res= mysql_test_show_create_routine(stmt, TYPE_ENUM_PROCEDURE)) == 2) + if ((res= mysql_test_show_create_routine(stmt, &sp_handler_procedure)) == 2) { /* Statement and field info has already been sent */ DBUG_RETURN(FALSE); } break; case SQLCOM_SHOW_CREATE_FUNC: - if ((res= mysql_test_show_create_routine(stmt, TYPE_ENUM_FUNCTION)) == 2) + if ((res= mysql_test_show_create_routine(stmt, &sp_handler_function)) == 2) { /* Statement and field info has already been sent */ DBUG_RETURN(FALSE); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index de7f93ca901..ed0d9ae15e9 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -5880,7 +5880,7 @@ bool store_schema_params(THD *thd, TABLE *table, TABLE *proc_table, LEX_CSTRING db, name; char path[FN_REFLEN]; sp_head *sp; - stored_procedure_type routine_type; + const Sp_handler *sph; bool free_sp_head; bool error= 0; DBUG_ENTER("store_schema_params"); @@ -5892,32 +5892,32 @@ bool store_schema_params(THD *thd, TABLE *table, TABLE *proc_table, proc_table->field[MYSQL_PROC_FIELD_DB]->val_str_nopad(thd->mem_root, &db); proc_table->field[MYSQL_PROC_FIELD_NAME]->val_str_nopad(thd->mem_root, &name); proc_table->field[MYSQL_PROC_FIELD_DEFINER]->val_str_nopad(thd->mem_root, &definer); - routine_type= (stored_procedure_type) proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_int(); + sph= Sp_handler::handler((stored_procedure_type) proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_int()); + if (!sph) + sph= &sp_handler_procedure; if (!full_access) full_access= !strcmp(sp_user, definer.str); if (!full_access && - check_some_routine_access(thd, db.str, name.str, - routine_type == TYPE_ENUM_PROCEDURE)) + check_some_routine_access(thd, db.str, name.str, sph)) DBUG_RETURN(0); proc_table->field[MYSQL_PROC_FIELD_PARAM_LIST]->val_str_nopad(thd->mem_root, ¶ms); - if (routine_type == TYPE_ENUM_FUNCTION) + if (sph->type() == TYPE_ENUM_FUNCTION) proc_table->field[MYSQL_PROC_FIELD_RETURNS]->val_str_nopad(thd->mem_root, &returns); - - sp= sp_load_for_information_schema(thd, proc_table, routine_type, db, name, - params, returns, - (ulong) proc_table-> - field[MYSQL_PROC_FIELD_SQL_MODE]->val_int(), - &free_sp_head); - + sp= sph->sp_load_for_information_schema(thd, proc_table, db, name, + params, returns, + (ulong) proc_table-> + field[MYSQL_PROC_FIELD_SQL_MODE]-> + val_int(), + &free_sp_head); if (sp) { Field *field; LEX_CSTRING tmp_string; - if (routine_type == TYPE_ENUM_FUNCTION) + if (sph->type() == TYPE_ENUM_FUNCTION) { restore_record(table, s->default_values); table->field[0]->store(STRING_WITH_LEN("def"), cs); @@ -6000,26 +6000,27 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table, MYSQL_TIME time; LEX *lex= thd->lex; CHARSET_INFO *cs= system_charset_info; + const Sp_handler *sph; LEX_CSTRING db, name, definer, returns= empty_clex_str; proc_table->field[MYSQL_PROC_FIELD_DB]->val_str_nopad(thd->mem_root, &db); proc_table->field[MYSQL_PROC_FIELD_NAME]->val_str_nopad(thd->mem_root, &name); proc_table->field[MYSQL_PROC_FIELD_DEFINER]->val_str_nopad(thd->mem_root, &definer); + sph= Sp_handler::handler((stored_procedure_type) + proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_int()); + if (!sph) + sph= &sp_handler_procedure; if (!full_access) full_access= !strcmp(sp_user, definer.str); if (!full_access && - check_some_routine_access(thd, db.str, name.str, - proc_table->field[MYSQL_PROC_MYSQL_TYPE]-> - val_int() == TYPE_ENUM_PROCEDURE)) + check_some_routine_access(thd, db.str, name.str, sph)) return 0; if ((lex->sql_command == SQLCOM_SHOW_STATUS_PROC && - proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_int() == - TYPE_ENUM_PROCEDURE) || + sph->type() == TYPE_ENUM_PROCEDURE) || (lex->sql_command == SQLCOM_SHOW_STATUS_FUNC && - proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_int() == - TYPE_ENUM_FUNCTION) || + sph->type() == TYPE_ENUM_FUNCTION) || (sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND) == 0) { restore_record(table, s->default_values); @@ -6036,22 +6037,20 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table, copy_field_as_string(table->field[4], proc_table->field[MYSQL_PROC_MYSQL_TYPE]); - if (proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_int() == - TYPE_ENUM_FUNCTION) + if (sph->type() == TYPE_ENUM_FUNCTION) { sp_head *sp; bool free_sp_head; proc_table->field[MYSQL_PROC_FIELD_RETURNS]->val_str_nopad(thd->mem_root, &returns); - sp= sp_load_for_information_schema(thd, proc_table, - TYPE_ENUM_FUNCTION, db, name, - empty_clex_str /*params*/, - returns, - (ulong) proc_table-> - field[MYSQL_PROC_FIELD_SQL_MODE]-> - val_int(), - &free_sp_head); - + sp= sph->sp_load_for_information_schema(thd, proc_table, + db, name, + empty_clex_str /*params*/, + returns, + (ulong) proc_table-> + field[MYSQL_PROC_FIELD_SQL_MODE]-> + val_int(), + &free_sp_head); if (sp) { char path[FN_REFLEN]; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index c9c922f6a55..40850ccc43f 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -2947,7 +2947,7 @@ ev_sql_stmt: my_yyabort_error((ER_EVENT_RECURSION_FORBIDDEN, MYF(0))); if (!lex->make_sp_head(thd, lex->event_parse_data->identifier, - TYPE_ENUM_PROCEDURE)) + &sp_handler_procedure)) MYSQL_YYABORT; lex->sphead->set_body_start(thd, lip->get_cpp_ptr()); @@ -3045,7 +3045,7 @@ call: lex->sql_command= SQLCOM_CALL; lex->spname= $2; lex->value_list.empty(); - sp_add_used_routine(lex, thd, $2, TYPE_ENUM_PROCEDURE); + sp_handler_procedure.add_used_routine(lex, thd, $2); } opt_sp_cparam_list {} ; @@ -3879,20 +3879,9 @@ sp_proc_stmt_return: { LEX *lex= Lex; sp_head *sp= lex->sphead; - - if (sp->m_type != TYPE_ENUM_FUNCTION) - my_yyabort_error((ER_SP_BADRETURN, MYF(0))); - - sp_instr_freturn *i; - - i= new (thd->mem_root) - sp_instr_freturn(sp->instructions(), lex->spcont, $3, - sp->m_return_field_def.type_handler(), lex); - if (i == NULL || sp->add_instr(i)) - MYSQL_YYABORT; - sp->m_flags|= sp_head::HAS_RETURN; - - if (sp->restore_lex(thd)) + if (sp->m_handler->add_instr_freturn(thd, sp, lex->spcont, + $3, lex) || + sp->restore_lex(thd)) MYSQL_YYABORT; } ; @@ -16588,7 +16577,7 @@ trigger_tail: (*static_cast(&lex->trg_chistics))= ($17); lex->trg_chistics.ordering_clause_end= lip->get_cpp_ptr(); - if (!lex->make_sp_head(thd, $4, TYPE_ENUM_TRIGGER)) + if (!lex->make_sp_head(thd, $4, &sp_handler_trigger)) MYSQL_YYABORT; lex->sphead->set_body_start(thd, lip->get_cpp_tok_start()); @@ -16662,7 +16651,7 @@ sf_tail: sp_name { if (!Lex->make_sp_head_no_recursive(thd, $1, $2, - TYPE_ENUM_FUNCTION)) + &sp_handler_function)) MYSQL_YYABORT; } sp_parenthesized_fdparam_list @@ -16697,7 +16686,7 @@ sp_tail: opt_if_not_exists sp_name { if (!Lex->make_sp_head_no_recursive(thd, $1, $2, - TYPE_ENUM_PROCEDURE)) + &sp_handler_procedure)) MYSQL_YYABORT; } sp_parenthesized_pdparam_list diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index 95d60b463c2..7c4d8cb5c81 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -2388,7 +2388,7 @@ ev_sql_stmt: my_yyabort_error((ER_EVENT_RECURSION_FORBIDDEN, MYF(0))); if (!lex->make_sp_head(thd, lex->event_parse_data->identifier, - TYPE_ENUM_PROCEDURE)) + &sp_handler_procedure)) MYSQL_YYABORT; lex->sphead->set_body_start(thd, lip->get_cpp_ptr()); @@ -2491,7 +2491,7 @@ call: lex->sql_command= SQLCOM_CALL; lex->spname= $2; lex->value_list.empty(); - sp_add_used_routine(lex, thd, $2, TYPE_ENUM_PROCEDURE); + sp_handler_procedure.add_used_routine(lex, thd, $2); } opt_sp_cparam_list {} ; @@ -3379,7 +3379,7 @@ sp_statement: MYSQL_YYABORT; lex->sql_command= SQLCOM_CALL; lex->value_list.empty(); - sp_add_used_routine(lex, thd, lex->spname, TYPE_ENUM_PROCEDURE); + sp_handler_procedure.add_used_routine(lex, thd, lex->spname); } opt_sp_cparam_list | ident_directly_assignable '.' ident @@ -3389,7 +3389,7 @@ sp_statement: MYSQL_YYABORT; lex->sql_command= SQLCOM_CALL; lex->value_list.empty(); - sp_add_used_routine(lex, thd, lex->spname, TYPE_ENUM_PROCEDURE); + sp_handler_procedure.add_used_routine(lex, thd, lex->spname); } opt_sp_cparam_list ; @@ -3453,36 +3453,16 @@ sp_proc_stmt_return: { LEX *lex= Lex; sp_head *sp= lex->sphead; - - if (sp->m_type != TYPE_ENUM_FUNCTION) - my_yyabort_error((ER_SP_BADRETURN, MYF(0))); - - sp_instr_freturn *i; - - i= new (thd->mem_root) - sp_instr_freturn(sp->instructions(), lex->spcont, $3, - sp->m_return_field_def.type_handler(), lex); - if (i == NULL || sp->add_instr(i)) - MYSQL_YYABORT; - sp->m_flags|= sp_head::HAS_RETURN; - - if (sp->restore_lex(thd)) + if (sp->m_handler->add_instr_freturn(thd, sp, lex->spcont, + $3, lex) || + sp->restore_lex(thd)) MYSQL_YYABORT; } | RETURN_SYM { LEX *lex= Lex; sp_head *sp= lex->sphead; - - if (sp->m_type != TYPE_ENUM_PROCEDURE) - { - thd->parse_error(); - MYSQL_YYABORT; - } - sp_instr_preturn *i; - i= new (thd->mem_root) - sp_instr_preturn(sp->instructions(), lex->spcont); - if (i == NULL || sp->add_instr(i)) + if (sp->m_handler->add_instr_preturn(thd, sp, lex->spcont)) MYSQL_YYABORT; } ; @@ -16825,7 +16805,7 @@ trigger_tail: (*static_cast(&lex->trg_chistics))= ($17); lex->trg_chistics.ordering_clause_end= lip->get_cpp_ptr(); - if (!lex->make_sp_head(thd, $4, TYPE_ENUM_TRIGGER)) + if (!lex->make_sp_head(thd, $4, &sp_handler_trigger)) MYSQL_YYABORT; lex->sphead->set_body_start(thd, lip->get_cpp_tok_start()); @@ -16901,7 +16881,7 @@ sf_tail: sp_name { if (!Lex->make_sp_head_no_recursive(thd, $1, $2, - TYPE_ENUM_FUNCTION)) + &sp_handler_function)) MYSQL_YYABORT; } opt_sp_parenthesized_fdparam_list @@ -16939,7 +16919,7 @@ sp_tail: opt_if_not_exists sp_name { if (!Lex->make_sp_head_no_recursive(thd, $1, $2, - TYPE_ENUM_PROCEDURE)) + &sp_handler_procedure)) MYSQL_YYABORT; } opt_sp_parenthesized_pdparam_list diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index d3726cc86a5..ba8312d302a 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -2241,14 +2241,14 @@ static int wsrep_create_sp(THD *thd, uchar** buf, size_t* buf_len) log_query.set_charset(system_charset_info); - if (sp->m_type == TYPE_ENUM_FUNCTION) + if (sp->m_handler->type() == TYPE_ENUM_FUNCTION) { sp_returns_type(thd, retstr, sp); returns= retstr.lex_cstring(); } - if (!show_create_sp(thd, &log_query, - sp->m_type, + if (!sp->m_handler-> + show_create_sp(thd, &log_query, sp->m_explicit_name ? sp->m_db : null_clex_str, sp->m_name, sp->m_params, returns, sp->m_body, sp->chistics(), thd->lex->definer[0],