mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
MDEV-13529 Add class Sql_cmd_call
This commit is contained in:
@ -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;
|
||||||
|
@ -6256,7 +6256,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");
|
||||||
@ -6264,7 +6264,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);
|
||||||
@ -6512,12 +6512,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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -6527,6 +6522,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
|
||||||
@ -6539,7 +6535,12 @@ 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,
|
/*
|
||||||
|
If the routine is not found, let's still check EXECUTE_ACL to decide
|
||||||
|
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,
|
m_name->m_name.str,
|
||||||
&sp_handler_function, false);
|
&sp_handler_function, false);
|
||||||
thd->security_ctx= save_security_ctx;
|
thd->security_ctx= save_security_ctx;
|
||||||
@ -6556,7 +6557,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);
|
||||||
|
@ -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)
|
||||||
|
@ -1424,6 +1424,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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -7156,6 +7156,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;
|
||||||
}
|
}
|
||||||
|
118
sql/sql_parse.cc
118
sql/sql_parse.cc
@ -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;
|
||||||
|
Reference in New Issue
Block a user