summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorAlexander Barkov <bar@mariadb.org>2017-08-04 16:33:58 +0400
committerAlexander Barkov <bar@mariadb.org>2017-08-04 16:33:58 +0400
commit0f1cadd9a5e6e8aa2103100502b0a5b4227e8d7b (patch)
treee595d36fd2f5d0f1d69114a2dada71e64f1bfb55 /sql
parentb3977ac23f90c11be7f0f6e019239fd9580130cb (diff)
downloadmariadb-git-0f1cadd9a5e6e8aa2103100502b0a5b4227e8d7b.tar.gz
MDEV-13450 Cleanup SP code for packages
Diffstat (limited to 'sql')
-rw-r--r--sql/sp.cc207
-rw-r--r--sql/sp.h7
-rw-r--r--sql/sql_lex.cc63
-rw-r--r--sql/sql_lex.h13
-rw-r--r--sql/sql_yacc.yy48
-rw-r--r--sql/sql_yacc_ora.yy60
6 files changed, 216 insertions, 182 deletions
diff --git a/sql/sp.cc b/sql/sp.cc
index 87061315987..015e2f77b21 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -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 @@ done:
}
+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;
diff --git a/sql/sp.h b/sql/sp.h
index 94638299ca3..8192d211fc8 100644
--- a/sql/sp.h
+++ b/sql/sp.h
@@ -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);
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index ebaa0ee6eb5..5756cf6c485 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -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;
+}
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index b72f52c5e4d..725a8e27fcb 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -3153,9 +3153,11 @@ public:
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 @@ public:
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 @@ public:
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);
};
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 40850ccc43f..e7ea40ca334 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -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
{
diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy
index 7c4d8cb5c81..9a5ebe45468 100644
--- a/sql/sql_yacc_ora.yy
+++ b/sql/sql_yacc_ora.yy
@@ -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
{