diff options
-rw-r--r-- | mysql-test/r/lowercase_fs_off.result | 2 | ||||
-rw-r--r-- | sql/item_func.cc | 25 | ||||
-rw-r--r-- | sql/item_func.h | 2 | ||||
-rw-r--r-- | sql/sp_head.cc | 21 | ||||
-rw-r--r-- | sql/sp_head.h | 4 | ||||
-rw-r--r-- | sql/sql_class.cc | 11 | ||||
-rw-r--r-- | sql/sql_class.h | 6 | ||||
-rw-r--r-- | sql/sql_cmd.h | 29 | ||||
-rw-r--r-- | sql/sql_lex.cc | 30 | ||||
-rw-r--r-- | sql/sql_lex.h | 2 | ||||
-rw-r--r-- | sql/sql_parse.cc | 118 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 23 | ||||
-rw-r--r-- | sql/sql_yacc_ora.yy | 24 |
13 files changed, 177 insertions, 120 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 8ab5fd5f739..4a91f9a4938 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -6257,7 +6257,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"); @@ -6265,7 +6265,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); @@ -6513,12 +6513,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)); } @@ -6528,6 +6523,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 @@ -6540,9 +6536,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) @@ -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(), 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..bb6fe6613bf 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -610,7 +610,7 @@ sp_head::init(LEX *lex) 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"); @@ -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); /* We have to copy strings to get them into the right memroot. */ - - 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); - + Database_qualified_name::copy(&main_mem_root, spname->m_db, spname->m_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; } @@ -1424,6 +1417,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..730b2c11b43 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -320,7 +320,7 @@ public: /** Copy sp name from parser. */ void - init_sp_name(THD *thd, const sp_name *spname); + init_sp_name(const sp_name *spname); /** Set the body-definition start position. */ void @@ -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_class.cc b/sql/sql_class.cc index 3851a258bd9..be613f38b81 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -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) */ diff --git a/sql/sql_class.h b/sql/sql_class.h index 4b39cad82bc..ce002a3c35a 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -6001,6 +6001,8 @@ public: (const uchar *) m_name.str, 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' size_t make_qname(char *dst, size_t dstlen) const { @@ -6009,13 +6011,13 @@ public: (int) m_name.length, m_name.str); } // 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; char *tmp; /* format: [database + dot] + name + '\0' */ 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; sprintf(tmp, "%.*s%.*s%.*s", (int) m_db.length, (m_db.length ? m_db.str : ""), 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 9c1c7c2cead..fbd781a9ae8 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -5845,7 +5845,7 @@ sp_head *LEX::make_sp_head(THD *thd, const sp_name *name, sp->reset_thd_mem_root(thd); sp->init(this); if (name) - sp->init_sp_name(thd, name); + sp->init_sp_name(name); sphead= sp; } 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) { /* @@ -7160,6 +7186,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_lex.h b/sql/sql_lex.h index a052775adba..4da86538006 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -3176,6 +3176,8 @@ public: return NULL; 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, const LEX_CSTRING *name); bool call_statement_start(THD *thd, const LEX_CSTRING *name1, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index f8a1e3bb01f..5053d311735 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; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 377be90056c..91844508507 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -16634,6 +16634,7 @@ sf_tail: opt_if_not_exists sp_name { + Lex->sql_command= SQLCOM_CREATE_SPFUNCTION; if (!Lex->make_sp_head_no_recursive(thd, $1, $2, &sp_handler_function)) MYSQL_YYABORT; @@ -16650,25 +16651,15 @@ sf_tail: } sp_proc_stmt_in_returns_clause { - LEX *lex= thd->lex; - sp_head *sp= lex->sphead; - - if (sp->is_not_allowed_in_function("function")) + if (Lex->sp_body_finalize_function(thd)) 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: opt_if_not_exists sp_name { + Lex->sql_command= SQLCOM_CREATE_PROCEDURE; if (!Lex->make_sp_head_no_recursive(thd, $1, $2, &sp_handler_procedure)) MYSQL_YYABORT; @@ -16681,12 +16672,8 @@ sp_tail: } sp_proc_stmt { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - - sp->set_stmt_end(thd); - lex->sql_command= SQLCOM_CREATE_PROCEDURE; - sp->restore_thd_mem_root(thd); + if (Lex->sp_body_finalize_procedure(thd)) + MYSQL_YYABORT; } ; diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index c5e8a75d125..5bb343d8251 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -16856,6 +16856,7 @@ sf_tail: opt_if_not_exists sp_name { + Lex->sql_command= SQLCOM_CREATE_SPFUNCTION; if (!Lex->make_sp_head_no_recursive(thd, $1, $2, &sp_handler_function)) MYSQL_YYABORT; @@ -16873,27 +16874,15 @@ sf_tail: sp_tail_is sp_body { - LEX *lex= thd->lex; - sp_head *sp= lex->sphead; - if (sp->check_unresolved_goto()) + if (Lex->sp_body_finalize_function(thd)) 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: opt_if_not_exists sp_name { + Lex->sql_command= SQLCOM_CREATE_PROCEDURE; if (!Lex->make_sp_head_no_recursive(thd, $1, $2, &sp_handler_procedure)) MYSQL_YYABORT; @@ -16907,13 +16896,8 @@ sp_tail: sp_tail_is sp_body { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - if (sp->check_unresolved_goto()) + if (Lex->sp_body_finalize_procedure(thd)) MYSQL_YYABORT; - sp->set_stmt_end(thd); - lex->sql_command= SQLCOM_CREATE_PROCEDURE; - sp->restore_thd_mem_root(thd); } ; |