Commit 0f1cadd9 authored by Alexander Barkov's avatar Alexander Barkov

MDEV-13450 Cleanup SP code for packages

parent b3977ac2
......@@ -711,6 +711,23 @@ Sp_handler::db_find_routine(THD *thd,
}
int
Sp_handler::db_find_and_cache_routine(THD *thd,
const Database_qualified_name *name,
sp_head **sp) const
{
int rc= db_find_routine(thd, name, sp);
if (rc == SP_OK)
{
sp_cache_insert(get_cache(thd), *sp);
DBUG_PRINT("info", ("added new: 0x%lx, level: %lu, flags %x",
(ulong) sp[0], sp[0]->m_recursion_level,
sp[0]->m_flags));
}
return rc;
}
/**
Silence DEPRECATED SYNTAX warnings when loading a stored procedure
into the cache.
......@@ -1335,6 +1352,26 @@ Sp_handler::sp_create_routine(THD *thd, const sp_head *sp) const
}
static bool
append_suid(String *buf, enum_sp_suid_behaviour suid)
{
return suid == SP_IS_NOT_SUID &&
buf->append(STRING_WITH_LEN(" SQL SECURITY INVOKER\n"));
}
static bool
append_comment(String *buf, const LEX_CSTRING &comment)
{
if (!comment.length)
return false;
if (buf->append(STRING_WITH_LEN(" COMMENT ")))
return true;
append_unescaped(buf, comment.str, comment.length);
return buf->append('\n');
}
/**
Delete the record for the stored routine object from mysql.proc
and do binary logging.
......@@ -1711,6 +1748,85 @@ Sp_handler::sp_show_create_routine(THD *thd,
}
/*
In case of recursions, we create multiple copies of the same SP.
This methods checks the current recursion depth.
In case if the recursion limit exceeded, it throws an error
and returns NULL.
Otherwise, depending on the current recursion level, it:
- either returns the original SP,
- or makes and returns a new clone of SP
*/
sp_head *
Sp_handler::sp_clone_and_link_routine(THD *thd,
const Database_qualified_name *name,
sp_head *sp) const
{
DBUG_ENTER("sp_link_routine");
ulong level;
sp_head *new_sp;
LEX_CSTRING returns= empty_clex_str;
/*
String buffer for RETURNS data type must have system charset;
64 -- size of "returns" column of mysql.proc.
*/
String retstr(64);
retstr.set_charset(sp->get_creation_ctx()->get_client_cs());
DBUG_PRINT("info", ("found: 0x%lx", (ulong)sp));
if (sp->m_first_free_instance)
{
DBUG_PRINT("info", ("first free: 0x%lx level: %lu flags %x",
(ulong)sp->m_first_free_instance,
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 > recursion_depth(thd))
{
recursion_level_error(thd, sp);
DBUG_RETURN(0);
}
DBUG_RETURN(sp->m_first_free_instance);
}
/*
Actually depth could be +1 than the actual value in case a SP calls
SHOW CREATE PROCEDURE. Hence, the linked list could hold up to one more
instance.
*/
level= sp->m_last_cached_sp->m_recursion_level + 1;
if (level > recursion_depth(thd))
{
recursion_level_error(thd, sp);
DBUG_RETURN(0);
}
if (type() == TYPE_ENUM_FUNCTION)
{
sp_returns_type(thd, retstr, sp);
returns= retstr.lex_cstring();
}
if (db_load_routine(thd, name, &new_sp,
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)
{
sp->m_last_cached_sp->m_next_cached_sp= new_sp;
new_sp->m_recursion_level= level;
new_sp->m_first_instance= sp;
sp->m_last_cached_sp= sp->m_first_free_instance= new_sp;
DBUG_PRINT("info", ("added level: 0x%lx, level: %lu, flags %x",
(ulong)new_sp, new_sp->m_recursion_level,
new_sp->m_flags));
DBUG_RETURN(new_sp);
}
DBUG_RETURN(0);
}
/**
Obtain object representing stored procedure/function by its name from
stored procedures cache and looking into mysql.proc if needed.
......@@ -1731,88 +1847,18 @@ sp_head *
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;
DBUG_ENTER("sp_find_routine");
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_str(), cache_only));
sp_cache **cp= get_cache(thd);
sp_head *sp;
if ((sp= sp_cache_lookup(cp, name)))
{
ulong level;
sp_head *new_sp;
LEX_CSTRING returns= empty_clex_str;
/*
String buffer for RETURNS data type must have system charset;
64 -- size of "returns" column of mysql.proc.
*/
String retstr(64);
retstr.set_charset(sp->get_creation_ctx()->get_client_cs());
DBUG_PRINT("info", ("found: 0x%lx", (ulong)sp));
if (sp->m_first_free_instance)
{
DBUG_PRINT("info", ("first free: 0x%lx level: %lu flags %x",
(ulong)sp->m_first_free_instance,
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 > recursion_depth(thd))
{
recursion_level_error(thd, sp);
DBUG_RETURN(0);
}
DBUG_RETURN(sp->m_first_free_instance);
}
/*
Actually depth could be +1 than the actual value in case a SP calls
SHOW CREATE PROCEDURE. Hence, the linked list could hold up to one more
instance.
*/
level= sp->m_last_cached_sp->m_recursion_level + 1;
if (level > recursion_depth(thd))
{
recursion_level_error(thd, sp);
DBUG_RETURN(0);
}
if (type() == TYPE_ENUM_FUNCTION)
{
sp_returns_type(thd, retstr, sp);
returns= retstr.lex_cstring();
}
if (db_load_routine(thd, name, &new_sp,
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)
{
sp->m_last_cached_sp->m_next_cached_sp= new_sp;
new_sp->m_recursion_level= level;
new_sp->m_first_instance= sp;
sp->m_last_cached_sp= sp->m_first_free_instance= new_sp;
DBUG_PRINT("info", ("added level: 0x%lx, level: %lu, flags %x",
(ulong)new_sp, new_sp->m_recursion_level,
new_sp->m_flags));
DBUG_RETURN(new_sp);
}
DBUG_RETURN(0);
}
DBUG_RETURN(sp_clone_and_link_routine(thd, name, sp));
if (!cache_only)
{
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",
(ulong)sp, sp->m_recursion_level,
sp->m_flags));
}
}
db_find_and_cache_routine(thd, name, &sp);
DBUG_RETURN(sp);
}
......@@ -2137,10 +2183,9 @@ int Sp_handler::sp_cache_routine(THD *thd,
DBUG_RETURN(SP_OK);
}
switch ((ret= db_find_routine(thd, name, sp)))
switch ((ret= db_find_and_cache_routine(thd, name, sp)))
{
case SP_OK:
sp_cache_insert(spc, *sp);
break;
case SP_KEY_NOT_FOUND:
ret= SP_OK;
......@@ -2243,14 +2288,8 @@ Sp_handler::show_create_sp(THD *thd, String *buf,
}
if (chistics.detistic)
buf->append(STRING_WITH_LEN(" DETERMINISTIC\n"));
if (chistics.suid == SP_IS_NOT_SUID)
buf->append(STRING_WITH_LEN(" SQL SECURITY INVOKER\n"));
if (chistics.comment.length)
{
buf->append(STRING_WITH_LEN(" COMMENT "));
append_unescaped(buf, chistics.comment.str, chistics.comment.length);
buf->append('\n');
}
append_suid(buf, chistics.suid);
append_comment(buf, chistics.comment);
buf->append(body);
thd->variables.sql_mode= old_sql_mode;
return false;
......
......@@ -60,6 +60,9 @@ class Sp_handler
TABLE *table) const;
int db_find_routine(THD *thd, const Database_qualified_name *name,
sp_head **sphp) const;
int db_find_and_cache_routine(THD *thd,
const Database_qualified_name *name,
sp_head **sp) const;
int db_load_routine(THD *thd, const Database_qualified_name *name,
sp_head **sphp,
sql_mode_t sql_mode,
......@@ -73,6 +76,10 @@ class Sp_handler
int sp_drop_routine_internal(THD *thd,
const Database_qualified_name *name,
TABLE *table) const;
sp_head *sp_clone_and_link_routine(THD *thd,
const Database_qualified_name *name,
sp_head *sp) const;
public:
static const Sp_handler *handler(enum enum_sql_command cmd);
static const Sp_handler *handler(stored_procedure_type type);
......
......@@ -5798,7 +5798,7 @@ bool LEX::sp_block_finalize(THD *thd, const Lex_spblock_st spblock,
}
sp_name *LEX::make_sp_name(THD *thd, LEX_CSTRING *name)
sp_name *LEX::make_sp_name(THD *thd, const LEX_CSTRING *name)
{
sp_name *res;
LEX_CSTRING db;
......@@ -5810,16 +5810,20 @@ sp_name *LEX::make_sp_name(THD *thd, LEX_CSTRING *name)
}
sp_name *LEX::make_sp_name(THD *thd, LEX_CSTRING *name1, LEX_CSTRING *name2)
sp_name *LEX::make_sp_name(THD *thd, const LEX_CSTRING *name1,
const LEX_CSTRING *name2)
{
sp_name *res;
if (!name1->str || check_db_name((LEX_STRING*) name1))
LEX_CSTRING norm_name1;
if (!name1->str ||
!thd->make_lex_string(&norm_name1, name1->str, name1->length) ||
check_db_name((LEX_STRING *) &norm_name1))
{
my_error(ER_WRONG_DB_NAME, MYF(0), name1->str);
return NULL;
}
if (check_routine_name(name2) ||
(!(res= new (thd->mem_root) sp_name(name1, name2, true))))
(!(res= new (thd->mem_root) sp_name(&norm_name1, name2, true))))
return NULL;
return res;
}
......@@ -6570,6 +6574,18 @@ Item *LEX::create_item_limit(THD *thd,
}
bool LEX::set_user_variable(THD *thd, const LEX_CSTRING *name, Item *val)
{
Item_func_set_user_var *item;
set_var_user *var;
if (!(item= new (thd->mem_root) Item_func_set_user_var(thd, name, val)) ||
!(var= new (thd->mem_root) set_var_user(item)))
return true;
var_list.push_back(var, thd->mem_root);
return false;
}
/*
Perform assignment for a trigger, a system variable, or an SP variable.
"variable" be previously set by init_internal_variable(variable, name).
......@@ -7101,3 +7117,42 @@ bool LEX::add_create_view(THD *thd, DDL_options_st ddl,
return true;
return create_or_alter_view_finalize(thd, table_ident);
}
bool LEX::call_statement_start(THD *thd, sp_name *name)
{
sql_command= SQLCOM_CALL;
spname= name;
value_list.empty();
sp_handler_procedure.add_used_routine(this, thd, name);
return false;
}
bool LEX::call_statement_start(THD *thd, const LEX_CSTRING *name)
{
sp_name *spname= make_sp_name(thd, name);
return !spname || call_statement_start(thd, spname);
}
bool LEX::call_statement_start(THD *thd, const LEX_CSTRING *name1,
const LEX_CSTRING *name2)
{
sp_name *spname= make_sp_name(thd, name1, name2);
return !spname || call_statement_start(thd, spname);
}
bool LEX::add_grant_command(THD *thd, enum_sql_command sql_command_arg,
stored_procedure_type type_arg)
{
if (columns.elements)
{
thd->parse_error();
return true;
}
sql_command= sql_command_arg,
type= type_arg;
return false;
}
......@@ -3153,9 +3153,11 @@ struct LEX: public Query_tables_list
bool set_trigger_new_row(LEX_CSTRING *name, Item *val);
bool set_system_variable(struct sys_var_with_base *tmp,
enum enum_var_type var_type, Item *val);
bool set_user_variable(THD *thd, const LEX_CSTRING *name, Item *val);
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_name *make_sp_name(THD *thd, const LEX_CSTRING *name);
sp_name *make_sp_name(THD *thd, const LEX_CSTRING *name1,
const LEX_CSTRING *name2);
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)
......@@ -3173,6 +3175,10 @@ struct LEX: public Query_tables_list
return NULL;
return make_sp_head_no_recursive(thd, name, sph);
}
bool call_statement_start(THD *thd, sp_name *name);
bool call_statement_start(THD *thd, const LEX_CSTRING *name);
bool call_statement_start(THD *thd, const LEX_CSTRING *name1,
const LEX_CSTRING *name2);
bool init_internal_variable(struct sys_var_with_base *variable,
const LEX_CSTRING *name);
bool init_internal_variable(struct sys_var_with_base *variable,
......@@ -3651,6 +3657,9 @@ struct LEX: public Query_tables_list
bool add_create_view(THD *thd, DDL_options_st ddl,
uint16 algorithm, enum_view_suid suid,
Table_ident *table_ident);
bool add_grant_command(THD *thd, enum_sql_command sql_command_arg,
stored_procedure_type type_arg);
};
......
......@@ -3040,12 +3040,8 @@ sp_suid:
call:
CALL_SYM sp_name
{
LEX *lex = Lex;
lex->sql_command= SQLCOM_CALL;
lex->spname= $2;
lex->value_list.empty();
sp_handler_procedure.add_used_routine(lex, thd, $2);
if (Lex->call_statement_start(thd, $2))
MYSQL_YYABORT;
}
opt_sp_cparam_list {}
;
......@@ -15172,14 +15168,8 @@ option_value_no_option_type:
}
| '@' ident_or_text equal expr
{
Item_func_set_user_var *item;
item= new (thd->mem_root) Item_func_set_user_var(thd, &$2, $4);
if (item == NULL)
MYSQL_YYABORT;
set_var_user *var= new (thd->mem_root) set_var_user(item);
if (var == NULL)
if (Lex->set_user_variable(thd, &$2, $4))
MYSQL_YYABORT;
Lex->var_list.push_back(var, thd->mem_root);
}
| '@' '@' opt_var_ident_type internal_variable_name equal set_expr_or_default
{
......@@ -15584,25 +15574,13 @@ revoke_command:
}
| grant_privileges ON FUNCTION_SYM grant_ident FROM user_and_role_list
{
LEX *lex= Lex;
if (lex->columns.elements)
{
thd->parse_error();
if (Lex->add_grant_command(thd, SQLCOM_REVOKE, TYPE_ENUM_FUNCTION))
MYSQL_YYABORT;
}
lex->sql_command= SQLCOM_REVOKE;
lex->type= TYPE_ENUM_FUNCTION;
}
| grant_privileges ON PROCEDURE_SYM grant_ident FROM user_and_role_list
{
LEX *lex= Lex;
if (lex->columns.elements)
{
thd->parse_error();
if (Lex->add_grant_command(thd, SQLCOM_REVOKE, TYPE_ENUM_PROCEDURE))
MYSQL_YYABORT;
}
lex->sql_command= SQLCOM_REVOKE;
lex->type= TYPE_ENUM_PROCEDURE;
}
| ALL opt_privileges ',' GRANT OPTION FROM user_and_role_list
{
......@@ -15646,26 +15624,14 @@ grant_command:
| grant_privileges ON FUNCTION_SYM grant_ident TO_SYM grant_list
opt_require_clause opt_grant_options
{
LEX *lex= Lex;
if (lex->columns.elements)
{
thd->parse_error();
if (Lex->add_grant_command(thd, SQLCOM_GRANT, TYPE_ENUM_FUNCTION))
MYSQL_YYABORT;
}
lex->sql_command= SQLCOM_GRANT;
lex->type= TYPE_ENUM_FUNCTION;
}
| grant_privileges ON PROCEDURE_SYM grant_ident TO_SYM grant_list
opt_require_clause opt_grant_options
{
LEX *lex= Lex;
if (lex->columns.elements)
{
thd->parse_error();
if (Lex->add_grant_command(thd, SQLCOM_GRANT, TYPE_ENUM_PROCEDURE))
MYSQL_YYABORT;
}
lex->sql_command= SQLCOM_GRANT;
lex->type= TYPE_ENUM_PROCEDURE;
}
| PROXY_SYM ON user TO_SYM grant_list opt_grant_option
{
......
......@@ -2486,12 +2486,8 @@ sp_suid:
call:
CALL_SYM sp_name
{
LEX *lex = Lex;
lex->sql_command= SQLCOM_CALL;
lex->spname= $2;
lex->value_list.empty();
sp_handler_procedure.add_used_routine(lex, thd, $2);
if (Lex->call_statement_start(thd, $2))
MYSQL_YYABORT;
}
opt_sp_cparam_list {}
;
......@@ -3374,22 +3370,14 @@ sp_statement:
| ident_directly_assignable
{
// Direct procedure call (without the CALL keyword)
LEX *lex = Lex;
if (!(lex->spname= lex->make_sp_name(thd, &$1)))
if (Lex->call_statement_start(thd, &$1))
MYSQL_YYABORT;
lex->sql_command= SQLCOM_CALL;
lex->value_list.empty();
sp_handler_procedure.add_used_routine(lex, thd, lex->spname);
}
opt_sp_cparam_list
| ident_directly_assignable '.' ident
{
LEX *lex = Lex;
if (!(lex->spname= lex->make_sp_name(thd, &$1, &$3)))
if (Lex->call_statement_start(thd, &$1, &$3))
MYSQL_YYABORT;
lex->sql_command= SQLCOM_CALL;
lex->value_list.empty();
sp_handler_procedure.add_used_routine(lex, thd, lex->spname);
}
opt_sp_cparam_list
;
......@@ -15385,14 +15373,8 @@ option_value_no_option_type:
}
| '@' ident_or_text equal expr
{
Item_func_set_user_var *item;
item= new (thd->mem_root) Item_func_set_user_var(thd, &$2, $4);
if (item == NULL)
MYSQL_YYABORT;
set_var_user *var= new (thd->mem_root) set_var_user(item);
if (var == NULL)
if (Lex->set_user_variable(thd, &$2, $4))
MYSQL_YYABORT;
Lex->var_list.push_back(var, thd->mem_root);
}
| '@' '@' opt_var_ident_type internal_variable_name equal set_expr_or_default
{
......@@ -15821,25 +15803,13 @@ revoke_command:
}
| grant_privileges ON FUNCTION_SYM grant_ident FROM user_and_role_list
{
LEX *lex= Lex;
if (lex->columns.elements)
{
thd->parse_error();
if (Lex->add_grant_command(thd, SQLCOM_REVOKE, TYPE_ENUM_FUNCTION))
MYSQL_YYABORT;
}
lex->sql_command= SQLCOM_REVOKE;
lex->type= TYPE_ENUM_FUNCTION;
}
| grant_privileges ON PROCEDURE_SYM grant_ident FROM user_and_role_list
{
LEX *lex= Lex;
if (lex->columns.elements)
{
thd->parse_error();
if (Lex->add_grant_command(thd, SQLCOM_REVOKE, TYPE_ENUM_PROCEDURE))
MYSQL_YYABORT;
}
lex->sql_command= SQLCOM_REVOKE;
lex->type= TYPE_ENUM_PROCEDURE;
}
| ALL opt_privileges ',' GRANT OPTION FROM user_and_role_list
{
......@@ -15883,26 +15853,14 @@ grant_command:
| grant_privileges ON FUNCTION_SYM grant_ident TO_SYM grant_list
opt_require_clause opt_grant_options
{
LEX *lex= Lex;
if (lex->columns.elements)
{
thd->parse_error();
if (Lex->add_grant_command(thd, SQLCOM_GRANT, TYPE_ENUM_FUNCTION))
MYSQL_YYABORT;
}
lex->sql_command= SQLCOM_GRANT;
lex->type= TYPE_ENUM_FUNCTION;
}
| grant_privileges ON PROCEDURE_SYM grant_ident TO_SYM grant_list
opt_require_clause opt_grant_options
{
LEX *lex= Lex;
if (lex->columns.elements)
{
thd->parse_error();
if (Lex->add_grant_command(thd, SQLCOM_GRANT, TYPE_ENUM_PROCEDURE))
MYSQL_YYABORT;
}
lex->sql_command= SQLCOM_GRANT;
lex->type= TYPE_ENUM_PROCEDURE;
}
| PROXY_SYM ON user TO_SYM grant_list opt_grant_option
{
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment