summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Barkov <bar@mariadb.org>2017-08-15 14:13:42 +0400
committerAlexander Barkov <bar@mariadb.org>2017-08-15 14:13:42 +0400
commit4d50594dfc758774f116d3919a2a039f95182f8e (patch)
tree3aab020bd53f09edb67d9400b97d1d32083e8355
parent966cc80299d7ebc6b63427e3a94c5218cb8e240d (diff)
downloadmariadb-git-4d50594dfc758774f116d3919a2a039f95182f8e.tar.gz
MDEV-13529 Add class Sql_cmd_call
-rw-r--r--mysql-test/r/lowercase_fs_off.result2
-rw-r--r--sql/item_func.cc25
-rw-r--r--sql/item_func.h2
-rw-r--r--sql/sp_head.cc8
-rw-r--r--sql/sp_head.h2
-rw-r--r--sql/sql_cmd.h29
-rw-r--r--sql/sql_lex.cc2
-rw-r--r--sql/sql_parse.cc118
8 files changed, 120 insertions, 68 deletions
diff --git a/mysql-test/r/lowercase_fs_off.result b/mysql-test/r/lowercase_fs_off.result
index 12da5127629..91bd2924096 100644
--- a/mysql-test/r/lowercase_fs_off.result
+++ b/mysql-test/r/lowercase_fs_off.result
@@ -61,7 +61,7 @@ connect con2,localhost,USER_1,,db1;
call p1();
ERROR 42000: execute command denied to user 'USER_1'@'localhost' for routine 'db1.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);
ERROR 42000: execute command denied to user 'USER_1'@'localhost' for routine 'db1.f1'
connection default;
diff --git a/sql/item_func.cc b/sql/item_func.cc
index ebd5d9860d6..8a2db6bc0d5 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -6256,7 +6256,7 @@ void my_missing_function_error(const LEX_CSTRING &token, const char *func_name)
*/
bool
-Item_func_sp::init_result_field(THD *thd)
+Item_func_sp::init_result_field(THD *thd, sp_head *sp)
{
TABLE_SHARE *share;
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(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());
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_ASSERT(m_sp);
- if (check_routine_access(thd, EXECUTE_ACL,
- m_sp->m_db.str, m_sp->m_name.str,
- &sp_handler_function, false))
- DBUG_RETURN(TRUE);
-
- DBUG_RETURN(FALSE);
+ DBUG_RETURN(m_sp->check_execute_access(thd));
}
@@ -6527,6 +6522,7 @@ Item_func_sp::fix_fields(THD *thd, Item **ref)
bool res;
DBUG_ENTER("Item_func_sp::fix_fields");
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
@@ -6539,9 +6535,14 @@ Item_func_sp::fix_fields(THD *thd, Item **ref)
if (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,
- &sp_handler_function, false);
+ /*
+ 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,
+ &sp_handler_function, false);
thd->security_ctx= save_security_ctx;
if (res)
@@ -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(),
which is called from Item_func::fix_fields().
*/
- res= init_result_field(thd);
+ res= init_result_field(thd, sp);
if (res)
DBUG_RETURN(res);
diff --git a/sql/item_func.h b/sql/item_func.h
index 91129d27fe5..9a6254438f0 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -2679,7 +2679,7 @@ private:
bool execute();
bool execute_impl(THD *thd);
- bool init_result_field(THD *thd);
+ bool init_result_field(THD *thd, sp_head *sp);
protected:
bool is_expensive_processor(void *arg)
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 1f71139b9af..f9d3aa56458 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -1424,6 +1424,14 @@ set_routine_security_ctx(THD *thd, sp_head *sp, Security_context **save_ctx)
#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.
This is important for sql_mode=ORACLE to make sure that the invoker has
diff --git a/sql/sp_head.h b/sql/sp_head.h
index f9a021d535e..e3cedb6e30a 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -791,6 +791,8 @@ public:
sp_pcontext *get_parse_context() { return m_pcont; }
+ bool check_execute_access(THD *thd) const;
+
private:
MEM_ROOT *m_thd_root; ///< Temp. store for thd's mem_root
diff --git a/sql/sql_cmd.h b/sql/sql_cmd.h
index 4ae9353d6ff..d95b1c828b9 100644
--- a/sql/sql_cmd.h
+++ b/sql/sql_cmd.h
@@ -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
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index ea01bb04d9e..ef1a41a34a7 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -7156,6 +7156,8 @@ bool LEX::call_statement_start(THD *thd, sp_name *name)
sql_command= SQLCOM_CALL;
spname= name;
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);
return false;
}
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index c64ca8961ae..84bc7a4e4ed 100644
--- a/sql/sql_parse.cc
+++ b/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);
}
+
+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.
@@ -5748,60 +5811,6 @@ end_with_restore_list:
my_ok(thd);
break; /* break super switch */
} /* 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:
DBUG_ASSERT(all_tables == 0);
DBUG_ASSERT(thd->in_sub_stmt == 0);
@@ -6196,6 +6205,7 @@ end_with_restore_list:
case SQLCOM_SIGNAL:
case SQLCOM_RESIGNAL:
case SQLCOM_GET_DIAGNOSTICS:
+ case SQLCOM_CALL:
DBUG_ASSERT(lex->m_sql_cmd != NULL);
res= lex->m_sql_cmd->execute(thd);
break;