Merge remote-tracking branch 'origin/bb-10.2-ext' into 10.3

This commit is contained in:
Alexander Barkov 2017-08-15 17:13:59 +04:00
commit 6ac3d7511c
13 changed files with 177 additions and 120 deletions

View File

@ -61,7 +61,7 @@ connect con2,localhost,USER_1,,db1;
call p1(); call p1();
ERROR 42000: execute command denied to user 'USER_1'@'localhost' for routine 'db1.p1' ERROR 42000: execute command denied to user 'USER_1'@'localhost' for routine 'db1.p1'
call P1(); call P1();
ERROR 42000: execute command denied to user 'USER_1'@'localhost' for routine 'db1.P1' ERROR 42000: execute command denied to user 'USER_1'@'localhost' for routine 'db1.p1'
select f1(1); select f1(1);
ERROR 42000: execute command denied to user 'USER_1'@'localhost' for routine 'db1.f1' ERROR 42000: execute command denied to user 'USER_1'@'localhost' for routine 'db1.f1'
connection default; connection default;

View File

@ -6257,7 +6257,7 @@ void my_missing_function_error(const LEX_CSTRING &token, const char *func_name)
*/ */
bool bool
Item_func_sp::init_result_field(THD *thd) Item_func_sp::init_result_field(THD *thd, sp_head *sp)
{ {
TABLE_SHARE *share; TABLE_SHARE *share;
DBUG_ENTER("Item_func_sp::init_result_field"); DBUG_ENTER("Item_func_sp::init_result_field");
@ -6265,7 +6265,7 @@ Item_func_sp::init_result_field(THD *thd)
DBUG_ASSERT(m_sp == NULL); DBUG_ASSERT(m_sp == NULL);
DBUG_ASSERT(sp_result_field == NULL); DBUG_ASSERT(sp_result_field == NULL);
if (!(m_sp= sp_handler_function.sp_find_routine(thd, m_name, true))) if (!(m_sp= sp))
{ {
my_missing_function_error (m_name->m_name, ErrConvDQName(m_name).ptr()); my_missing_function_error (m_name->m_name, ErrConvDQName(m_name).ptr());
context->process_error(thd); context->process_error(thd);
@ -6513,12 +6513,7 @@ Item_func_sp::sp_check_access(THD *thd)
{ {
DBUG_ENTER("Item_func_sp::sp_check_access"); DBUG_ENTER("Item_func_sp::sp_check_access");
DBUG_ASSERT(m_sp); DBUG_ASSERT(m_sp);
if (check_routine_access(thd, EXECUTE_ACL, DBUG_RETURN(m_sp->check_execute_access(thd));
m_sp->m_db.str, m_sp->m_name.str,
&sp_handler_function, false))
DBUG_RETURN(TRUE);
DBUG_RETURN(FALSE);
} }
@ -6528,6 +6523,7 @@ Item_func_sp::fix_fields(THD *thd, Item **ref)
bool res; bool res;
DBUG_ENTER("Item_func_sp::fix_fields"); DBUG_ENTER("Item_func_sp::fix_fields");
DBUG_ASSERT(fixed == 0); DBUG_ASSERT(fixed == 0);
sp_head *sp= sp_handler_function.sp_find_routine(thd, m_name, true);
/* /*
Checking privileges to execute the function while creating view and Checking privileges to execute the function while creating view and
@ -6540,9 +6536,14 @@ Item_func_sp::fix_fields(THD *thd, Item **ref)
if (context->security_ctx) if (context->security_ctx)
thd->security_ctx= context->security_ctx; thd->security_ctx= context->security_ctx;
res= check_routine_access(thd, EXECUTE_ACL, m_name->m_db.str, /*
m_name->m_name.str, If the routine is not found, let's still check EXECUTE_ACL to decide
&sp_handler_function, false); whether to return "Access denied" or "Routine does not exist".
*/
res= sp ? sp->check_execute_access(thd) :
check_routine_access(thd, EXECUTE_ACL, m_name->m_db.str,
m_name->m_name.str,
&sp_handler_function, false);
thd->security_ctx= save_security_ctx; thd->security_ctx= save_security_ctx;
if (res) if (res)
@ -6557,7 +6558,7 @@ Item_func_sp::fix_fields(THD *thd, Item **ref)
to make m_sp and result_field members available to fix_length_and_dec(), to make m_sp and result_field members available to fix_length_and_dec(),
which is called from Item_func::fix_fields(). which is called from Item_func::fix_fields().
*/ */
res= init_result_field(thd); res= init_result_field(thd, sp);
if (res) if (res)
DBUG_RETURN(res); DBUG_RETURN(res);

View File

@ -2679,7 +2679,7 @@ private:
bool execute(); bool execute();
bool execute_impl(THD *thd); bool execute_impl(THD *thd);
bool init_result_field(THD *thd); bool init_result_field(THD *thd, sp_head *sp);
protected: protected:
bool is_expensive_processor(void *arg) bool is_expensive_processor(void *arg)

View File

@ -610,7 +610,7 @@ sp_head::init(LEX *lex)
void void
sp_head::init_sp_name(THD *thd, const sp_name *spname) sp_head::init_sp_name(const sp_name *spname)
{ {
DBUG_ENTER("sp_head::init_sp_name"); DBUG_ENTER("sp_head::init_sp_name");
@ -619,17 +619,10 @@ sp_head::init_sp_name(THD *thd, const sp_name *spname)
DBUG_ASSERT(spname && spname->m_db.str && spname->m_db.length); DBUG_ASSERT(spname && spname->m_db.str && spname->m_db.length);
/* We have to copy strings to get them into the right memroot. */ /* We have to copy strings to get them into the right memroot. */
Database_qualified_name::copy(&main_mem_root, spname->m_db, spname->m_name);
m_db.length= spname->m_db.length;
m_db.str= strmake_root(thd->mem_root, spname->m_db.str, spname->m_db.length);
m_name.length= spname->m_name.length;
m_name.str= strmake_root(thd->mem_root, spname->m_name.str,
spname->m_name.length);
m_explicit_name= spname->m_explicit_name; m_explicit_name= spname->m_explicit_name;
spname->make_qname(thd, &m_qname); spname->make_qname(&main_mem_root, &m_qname);
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
@ -1424,6 +1417,14 @@ set_routine_security_ctx(THD *thd, sp_head *sp, Security_context **save_ctx)
#endif // ! NO_EMBEDDED_ACCESS_CHECKS #endif // ! NO_EMBEDDED_ACCESS_CHECKS
bool sp_head::check_execute_access(THD *thd) const
{
return check_routine_access(thd, EXECUTE_ACL,
m_db.str, m_name.str,
m_handler, false);
}
/** /**
Create rcontext using the routine security. Create rcontext using the routine security.
This is important for sql_mode=ORACLE to make sure that the invoker has This is important for sql_mode=ORACLE to make sure that the invoker has

View File

@ -320,7 +320,7 @@ public:
/** Copy sp name from parser. */ /** Copy sp name from parser. */
void void
init_sp_name(THD *thd, const sp_name *spname); init_sp_name(const sp_name *spname);
/** Set the body-definition start position. */ /** Set the body-definition start position. */
void void
@ -791,6 +791,8 @@ public:
sp_pcontext *get_parse_context() { return m_pcont; } sp_pcontext *get_parse_context() { return m_pcont; }
bool check_execute_access(THD *thd) const;
private: private:
MEM_ROOT *m_thd_root; ///< Temp. store for thd's mem_root MEM_ROOT *m_thd_root; ///< Temp. store for thd's mem_root

View File

@ -7505,4 +7505,15 @@ void AUTHID::parse(const char *str, size_t length)
} }
void Database_qualified_name::copy(MEM_ROOT *mem_root,
const LEX_CSTRING &db,
const LEX_CSTRING &name)
{
m_db.length= db.length;
m_db.str= strmake_root(mem_root, db.str, db.length);
m_name.length= name.length;
m_name.str= strmake_root(mem_root, name.str, name.length);
}
#endif /* !defined(MYSQL_CLIENT) */ #endif /* !defined(MYSQL_CLIENT) */

View File

@ -6001,6 +6001,8 @@ public:
(const uchar *) m_name.str, m_name.length, (const uchar *) m_name.str, m_name.length,
(const uchar *) other->m_name.str, other->m_name.length); (const uchar *) other->m_name.str, other->m_name.length);
} }
void copy(MEM_ROOT *mem_root, const LEX_CSTRING &db,
const LEX_CSTRING &name);
// Export db and name as a qualified name string: 'db.name' // Export db and name as a qualified name string: 'db.name'
size_t make_qname(char *dst, size_t dstlen) const size_t make_qname(char *dst, size_t dstlen) const
{ {
@ -6009,13 +6011,13 @@ public:
(int) m_name.length, m_name.str); (int) m_name.length, m_name.str);
} }
// Export db and name as a qualified name string, allocate on mem_root. // Export db and name as a qualified name string, allocate on mem_root.
bool make_qname(THD *thd, LEX_CSTRING *dst) const bool make_qname(MEM_ROOT *mem_root, LEX_CSTRING *dst) const
{ {
const uint dot= !!m_db.length; const uint dot= !!m_db.length;
char *tmp; char *tmp;
/* format: [database + dot] + name + '\0' */ /* format: [database + dot] + name + '\0' */
dst->length= m_db.length + dot + m_name.length; dst->length= m_db.length + dot + m_name.length;
if (!(dst->str= tmp= (char*) thd->alloc(dst->length + 1))) if (!(dst->str= tmp= (char*) alloc_root(mem_root, dst->length + 1)))
return true; return true;
sprintf(tmp, "%.*s%.*s%.*s", sprintf(tmp, "%.*s%.*s%.*s",
(int) m_db.length, (m_db.length ? m_db.str : ""), (int) m_db.length, (m_db.length ? m_db.str : ""),

View File

@ -167,4 +167,33 @@ protected:
} }
}; };
/**
Sql_cmd_call represents the CALL statement.
*/
class Sql_cmd_call : public Sql_cmd
{
public:
class sp_name *m_name;
Sql_cmd_call(class sp_name *name)
:m_name(name)
{}
virtual ~Sql_cmd_call()
{}
/**
Execute a CALL statement at runtime.
@param thd the current thread.
@return false on success.
*/
bool execute(THD *thd);
virtual enum_sql_command sql_command_code() const
{
return SQLCOM_CALL;
}
};
#endif // SQL_CMD_INCLUDED #endif // SQL_CMD_INCLUDED

View File

@ -5845,7 +5845,7 @@ sp_head *LEX::make_sp_head(THD *thd, const sp_name *name,
sp->reset_thd_mem_root(thd); sp->reset_thd_mem_root(thd);
sp->init(this); sp->init(this);
if (name) if (name)
sp->init_sp_name(thd, name); sp->init_sp_name(name);
sphead= sp; sphead= sp;
} }
sp_chistics.init(); sp_chistics.init();
@ -5853,6 +5853,32 @@ sp_head *LEX::make_sp_head(THD *thd, const sp_name *name,
} }
bool LEX::sp_body_finalize_procedure(THD *thd)
{
if (sphead->check_unresolved_goto())
return true;
sphead->set_stmt_end(thd);
sphead->restore_thd_mem_root(thd);
return false;
}
bool LEX::sp_body_finalize_function(THD *thd)
{
if (sphead->is_not_allowed_in_function("function"))
return true;
if (!(sphead->m_flags & sp_head::HAS_RETURN))
{
my_error(ER_SP_NORETURN, MYF(0), ErrConvDQName(sphead).ptr());
return true;
}
if (sp_body_finalize_procedure(thd))
return true;
(void) is_native_function_with_warn(thd, &sphead->m_name);
return false;
}
bool LEX::sp_block_with_exceptions_finalize_declarations(THD *thd) bool LEX::sp_block_with_exceptions_finalize_declarations(THD *thd)
{ {
/* /*
@ -7160,6 +7186,8 @@ bool LEX::call_statement_start(THD *thd, sp_name *name)
sql_command= SQLCOM_CALL; sql_command= SQLCOM_CALL;
spname= name; spname= name;
value_list.empty(); value_list.empty();
if (!(m_sql_cmd= new (thd->mem_root) Sql_cmd_call(name)))
return true;
sp_handler_procedure.add_used_routine(this, thd, name); sp_handler_procedure.add_used_routine(this, thd, name);
return false; return false;
} }

View File

@ -3176,6 +3176,8 @@ public:
return NULL; return NULL;
return make_sp_head_no_recursive(thd, name, sph); return make_sp_head_no_recursive(thd, name, sph);
} }
bool sp_body_finalize_function(THD *);
bool sp_body_finalize_procedure(THD *);
bool call_statement_start(THD *thd, sp_name *name); 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 *name);
bool call_statement_start(THD *thd, const LEX_CSTRING *name1, bool call_statement_start(THD *thd, const LEX_CSTRING *name1,

View File

@ -3074,6 +3074,69 @@ static bool prepare_db_action(THD *thd, ulong want_access, LEX_CSTRING *dbname)
return check_access(thd, want_access, dbname->str, NULL, NULL, 1, 0); return check_access(thd, want_access, dbname->str, NULL, NULL, 1, 0);
} }
bool Sql_cmd_call::execute(THD *thd)
{
TABLE_LIST *all_tables= thd->lex->query_tables;
sp_head *sp;
/*
This will cache all SP and SF and open and lock all tables
required for execution.
*/
if (check_table_access(thd, SELECT_ACL, all_tables, FALSE,
UINT_MAX, FALSE) ||
open_and_lock_tables(thd, all_tables, TRUE, 0))
return true;
/*
By this moment all needed SPs should be in cache so no need to look
into DB.
*/
if (!(sp= sp_handler_procedure.sp_find_routine(thd, m_name, true)))
{
/*
If the routine is not found, let's still check EXECUTE_ACL to decide
whether to return "Access denied" or "Routine does not exist".
*/
if (check_routine_access(thd, EXECUTE_ACL, m_name->m_db.str,
m_name->m_name.str,
&sp_handler_procedure,
false))
return true;
/*
sp_find_routine can have issued an ER_SP_RECURSION_LIMIT error.
Send message ER_SP_DOES_NOT_EXIST only if procedure is not found in
cache.
*/
if (!sp_cache_lookup(&thd->sp_proc_cache, m_name))
my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PROCEDURE",
ErrConvDQName(m_name).ptr());
return true;
}
else
{
if (sp->check_execute_access(thd))
return true;
/*
Check that the stored procedure doesn't contain Dynamic SQL
and doesn't return result sets: such stored procedures can't
be called from a function or trigger.
*/
if (thd->in_sub_stmt)
{
const char *where= (thd->in_sub_stmt & SUB_STMT_TRIGGER ?
"trigger" : "function");
if (sp->is_not_allowed_in_function(where))
return true;
}
if (do_execute_sp(thd, sp))
return true;
}
return false;
}
/** /**
Execute command saved in thd and lex->sql_command. Execute command saved in thd and lex->sql_command.
@ -5748,60 +5811,6 @@ end_with_restore_list:
my_ok(thd); my_ok(thd);
break; /* break super switch */ break; /* break super switch */
} /* end case group bracket */ } /* end case group bracket */
case SQLCOM_CALL:
{
sp_head *sp;
/*
This will cache all SP and SF and open and lock all tables
required for execution.
*/
if (check_table_access(thd, SELECT_ACL, all_tables, FALSE,
UINT_MAX, FALSE) ||
open_and_lock_tables(thd, all_tables, TRUE, 0))
goto error;
if (check_routine_access(thd, EXECUTE_ACL, lex->spname->m_db.str,
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_handler_procedure.sp_find_routine(thd, lex->spname, true)))
{
/*
sp_find_routine can have issued an ER_SP_RECURSION_LIMIT error.
Send message ER_SP_DOES_NOT_EXIST only if procedure is not found in
cache.
*/
if (!sp_cache_lookup(&thd->sp_proc_cache, lex->spname))
my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PROCEDURE",
ErrConvDQName(lex->spname).ptr());
goto error;
}
else
{
/*
Check that the stored procedure doesn't contain Dynamic SQL
and doesn't return result sets: such stored procedures can't
be called from a function or trigger.
*/
if (thd->in_sub_stmt)
{
const char *where= (thd->in_sub_stmt & SUB_STMT_TRIGGER ?
"trigger" : "function");
if (sp->is_not_allowed_in_function(where))
goto error;
}
if (do_execute_sp(thd, sp))
goto error;
}
break;
}
case SQLCOM_COMPOUND: case SQLCOM_COMPOUND:
DBUG_ASSERT(all_tables == 0); DBUG_ASSERT(all_tables == 0);
DBUG_ASSERT(thd->in_sub_stmt == 0); DBUG_ASSERT(thd->in_sub_stmt == 0);
@ -6196,6 +6205,7 @@ end_with_restore_list:
case SQLCOM_SIGNAL: case SQLCOM_SIGNAL:
case SQLCOM_RESIGNAL: case SQLCOM_RESIGNAL:
case SQLCOM_GET_DIAGNOSTICS: case SQLCOM_GET_DIAGNOSTICS:
case SQLCOM_CALL:
DBUG_ASSERT(lex->m_sql_cmd != NULL); DBUG_ASSERT(lex->m_sql_cmd != NULL);
res= lex->m_sql_cmd->execute(thd); res= lex->m_sql_cmd->execute(thd);
break; break;

View File

@ -16634,6 +16634,7 @@ sf_tail:
opt_if_not_exists opt_if_not_exists
sp_name sp_name
{ {
Lex->sql_command= SQLCOM_CREATE_SPFUNCTION;
if (!Lex->make_sp_head_no_recursive(thd, $1, $2, if (!Lex->make_sp_head_no_recursive(thd, $1, $2,
&sp_handler_function)) &sp_handler_function))
MYSQL_YYABORT; MYSQL_YYABORT;
@ -16650,25 +16651,15 @@ sf_tail:
} }
sp_proc_stmt_in_returns_clause sp_proc_stmt_in_returns_clause
{ {
LEX *lex= thd->lex; if (Lex->sp_body_finalize_function(thd))
sp_head *sp= lex->sphead;
if (sp->is_not_allowed_in_function("function"))
MYSQL_YYABORT; MYSQL_YYABORT;
lex->sql_command= SQLCOM_CREATE_SPFUNCTION;
sp->set_stmt_end(thd);
if (!(sp->m_flags & sp_head::HAS_RETURN))
my_yyabort_error((ER_SP_NORETURN, MYF(0),
ErrConvDQName(sp).ptr()));
(void) is_native_function_with_warn(thd, &sp->m_name);
sp->restore_thd_mem_root(thd);
} }
; ;
sp_tail: sp_tail:
opt_if_not_exists sp_name opt_if_not_exists sp_name
{ {
Lex->sql_command= SQLCOM_CREATE_PROCEDURE;
if (!Lex->make_sp_head_no_recursive(thd, $1, $2, if (!Lex->make_sp_head_no_recursive(thd, $1, $2,
&sp_handler_procedure)) &sp_handler_procedure))
MYSQL_YYABORT; MYSQL_YYABORT;
@ -16681,12 +16672,8 @@ sp_tail:
} }
sp_proc_stmt sp_proc_stmt
{ {
LEX *lex= Lex; if (Lex->sp_body_finalize_procedure(thd))
sp_head *sp= lex->sphead; MYSQL_YYABORT;
sp->set_stmt_end(thd);
lex->sql_command= SQLCOM_CREATE_PROCEDURE;
sp->restore_thd_mem_root(thd);
} }
; ;

View File

@ -16856,6 +16856,7 @@ sf_tail:
opt_if_not_exists opt_if_not_exists
sp_name sp_name
{ {
Lex->sql_command= SQLCOM_CREATE_SPFUNCTION;
if (!Lex->make_sp_head_no_recursive(thd, $1, $2, if (!Lex->make_sp_head_no_recursive(thd, $1, $2,
&sp_handler_function)) &sp_handler_function))
MYSQL_YYABORT; MYSQL_YYABORT;
@ -16873,27 +16874,15 @@ sf_tail:
sp_tail_is sp_tail_is
sp_body sp_body
{ {
LEX *lex= thd->lex; if (Lex->sp_body_finalize_function(thd))
sp_head *sp= lex->sphead;
if (sp->check_unresolved_goto())
MYSQL_YYABORT; MYSQL_YYABORT;
if (sp->is_not_allowed_in_function("function"))
MYSQL_YYABORT;
lex->sql_command= SQLCOM_CREATE_SPFUNCTION;
sp->set_stmt_end(thd);
if (!(sp->m_flags & sp_head::HAS_RETURN))
my_yyabort_error((ER_SP_NORETURN, MYF(0),
ErrConvDQName(sp).ptr()));
(void) is_native_function_with_warn(thd, &sp->m_name);
sp->restore_thd_mem_root(thd);
} }
; ;
sp_tail: sp_tail:
opt_if_not_exists sp_name opt_if_not_exists sp_name
{ {
Lex->sql_command= SQLCOM_CREATE_PROCEDURE;
if (!Lex->make_sp_head_no_recursive(thd, $1, $2, if (!Lex->make_sp_head_no_recursive(thd, $1, $2,
&sp_handler_procedure)) &sp_handler_procedure))
MYSQL_YYABORT; MYSQL_YYABORT;
@ -16907,13 +16896,8 @@ sp_tail:
sp_tail_is sp_tail_is
sp_body sp_body
{ {
LEX *lex= Lex; if (Lex->sp_body_finalize_procedure(thd))
sp_head *sp= lex->sphead;
if (sp->check_unresolved_goto())
MYSQL_YYABORT; MYSQL_YYABORT;
sp->set_stmt_end(thd);
lex->sql_command= SQLCOM_CREATE_PROCEDURE;
sp->restore_thd_mem_root(thd);
} }
; ;