summaryrefslogtreecommitdiff
path: root/sql/sp_head.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sp_head.cc')
-rw-r--r--sql/sp_head.cc269
1 files changed, 230 insertions, 39 deletions
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 8368e828fdc..c967a82cffe 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -450,7 +450,14 @@ sp_head::sp_head()
m_first_instance(this), m_first_free_instance(this), m_last_cached_sp(this),
m_cont_level(0)
{
+ const LEX_STRING str_reset= { NULL, 0 };
m_return_field_def.charset = NULL;
+ /*
+ FIXME: the only use case when name is NULL is events, and it should
+ be rewritten soon. Remove the else part and replace 'if' with
+ an assert when this is done.
+ */
+ m_db= m_name= m_qname= str_reset;
extern byte *
sp_table_key(const byte *ptr, uint *plen, my_bool first);
@@ -479,7 +486,7 @@ sp_head::init(LEX *lex)
lex->trg_table_fields.empty();
my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8);
m_param_begin= m_param_end= m_body_begin= 0;
- m_qname.str= m_db.str= m_name.str= m_params.str=
+ m_qname.str= m_db.str= m_name.str= m_params.str=
m_body.str= m_defstr.str= 0;
m_qname.length= m_db.length= m_name.length= m_params.length=
m_body.length= m_defstr.length= 0;
@@ -487,41 +494,42 @@ sp_head::init(LEX *lex)
DBUG_VOID_RETURN;
}
+
void
-sp_head::init_strings(THD *thd, LEX *lex, sp_name *name)
+sp_head::init_sp_name(THD *thd, sp_name *spname)
+{
+ DBUG_ENTER("sp_head::init_sp_name");
+
+ /* Must be initialized in the parser. */
+
+ 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);
+
+ if (spname->m_qname.length == 0)
+ spname->init_qname(thd);
+
+ m_qname.length= spname->m_qname.length;
+ m_qname.str= strmake_root(thd->mem_root, spname->m_qname.str,
+ m_qname.length);
+}
+
+
+void
+sp_head::init_strings(THD *thd, LEX *lex)
{
DBUG_ENTER("sp_head::init_strings");
const uchar *endp; /* Used to trim the end */
/* During parsing, we must use thd->mem_root */
MEM_ROOT *root= thd->mem_root;
- if (name)
- {
- /* Must be initialized in the parser */
- DBUG_ASSERT(name->m_db.str && name->m_db.length);
-
- /* We have to copy strings to get them into the right memroot */
- m_db.length= name->m_db.length;
- m_db.str= strmake_root(root, name->m_db.str, name->m_db.length);
- m_name.length= name->m_name.length;
- m_name.str= strmake_root(root, name->m_name.str, name->m_name.length);
-
- if (name->m_qname.length == 0)
- name->init_qname(thd);
- m_qname.length= name->m_qname.length;
- m_qname.str= strmake_root(root, name->m_qname.str, m_qname.length);
- }
- else
- {
- /*
- FIXME: the only use case when name is NULL is events, and it should
- be rewritten soon. Remove the else part and replace 'if' with
- an assert when this is done.
- */
- LEX_STRING str_reset= { NULL, 0 };
- m_db= m_name= m_qname= str_reset;
- }
-
if (m_param_begin && m_param_end)
{
m_params.length= m_param_end - m_param_begin;
@@ -535,10 +543,7 @@ sp_head::init_strings(THD *thd, LEX *lex, sp_name *name)
Trim "garbage" at the end. This is sometimes needed with the
"/ * ! VERSION... * /" wrapper in dump files.
*/
- while (m_body_begin < endp &&
- (endp[-1] <= ' ' || endp[-1] == '*' ||
- endp[-1] == '/' || endp[-1] == ';'))
- endp-= 1;
+ endp= skip_rear_comments(m_body_begin, endp);
m_body.length= endp - m_body_begin;
m_body.str= strmake_root(root, (char *)m_body_begin, m_body.length);
@@ -1125,6 +1130,7 @@ sp_head::execute(THD *thd)
thd->restore_active_arena(&execute_arena, &backup_arena);
+ thd->spcont->pop_all_cursors(); // To avoid memory leaks after an error
/* Restore all saved */
old_packet.swap(thd->packet);
@@ -1186,6 +1192,161 @@ sp_head::execute(THD *thd)
m_first_instance->m_first_free_instance->m_recursion_level ==
m_recursion_level + 1));
m_first_instance->m_first_free_instance= this;
+
+ DBUG_RETURN(err_status);
+}
+
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+/*
+ set_routine_security_ctx() changes routine security context, and
+ checks if there is an EXECUTE privilege in new context. If there is
+ no EXECUTE privilege, it changes the context back and returns a
+ error.
+
+ SYNOPSIS
+ set_routine_security_ctx()
+ thd thread handle
+ sp stored routine to change the context for
+ is_proc TRUE is procedure, FALSE if function
+ save_ctx pointer to an old security context
+
+ RETURN
+ TRUE if there was a error, and the context wasn't changed.
+ FALSE if the context was changed.
+*/
+
+bool
+set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc,
+ Security_context **save_ctx)
+{
+ *save_ctx= 0;
+ if (sp_change_security_context(thd, sp, save_ctx))
+ return TRUE;
+
+ /*
+ If we changed context to run as another user, we need to check the
+ access right for the new context again as someone may have revoked
+ the right to use the procedure from this user.
+
+ TODO:
+ Cache if the definer has the right to use the object on the
+ first usage and only reset the cache if someone does a GRANT
+ statement that 'may' affect this.
+ */
+ if (*save_ctx &&
+ check_routine_access(thd, EXECUTE_ACL,
+ sp->m_db.str, sp->m_name.str, is_proc, FALSE))
+ {
+ sp_restore_security_context(thd, *save_ctx);
+ *save_ctx= 0;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+#endif // ! NO_EMBEDDED_ACCESS_CHECKS
+
+
+/*
+ Execute a trigger:
+ - changes security context for triggers
+ - switch to new memroot
+ - call sp_head::execute
+ - restore old memroot
+ - restores security context
+
+ SYNOPSIS
+ sp_head::execute_trigger()
+ thd Thread handle
+ db database name
+ table table name
+ grant_info GRANT_INFO structure to be filled with
+ information about definer's privileges
+ on subject table
+
+ RETURN
+ FALSE on success
+ TRUE on error
+*/
+
+bool
+sp_head::execute_trigger(THD *thd, const char *db, const char *table,
+ GRANT_INFO *grant_info)
+{
+ sp_rcontext *octx = thd->spcont;
+ sp_rcontext *nctx = NULL;
+ bool err_status= FALSE;
+ MEM_ROOT call_mem_root;
+ Query_arena call_arena(&call_mem_root, Query_arena::INITIALIZED_FOR_SP);
+ Query_arena backup_arena;
+
+ DBUG_ENTER("sp_head::execute_trigger");
+ DBUG_PRINT("info", ("trigger %s", m_name.str));
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ Security_context *save_ctx;
+ if (sp_change_security_context(thd, this, &save_ctx))
+ DBUG_RETURN(TRUE);
+
+ /*
+ NOTE: TRIGGER_ACL should be used here.
+ */
+ if (check_global_access(thd, SUPER_ACL))
+ {
+ sp_restore_security_context(thd, save_ctx);
+ DBUG_RETURN(TRUE);
+ }
+
+ /*
+ Fetch information about table-level privileges to GRANT_INFO
+ structure for subject table. Check of privileges that will use it
+ and information about column-level privileges will happen in
+ Item_trigger_field::fix_fields().
+ */
+ fill_effective_table_privileges(thd, grant_info, db, table);
+#endif // NO_EMBEDDED_ACCESS_CHECKS
+
+ /*
+ Prepare arena and memroot for objects which lifetime is whole
+ duration of trigger call (sp_rcontext, it's tables and items,
+ sp_cursor and Item_cache holders for case expressions). We can't
+ use caller's arena/memroot for those objects because in this case
+ some fixed amount of memory will be consumed for each trigger
+ invocation and so statements which involve lot of them will hog
+ memory.
+
+ TODO: we should create sp_rcontext once per command and reuse it
+ on subsequent executions of a trigger.
+ */
+ init_sql_alloc(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0);
+ thd->set_n_backup_active_arena(&call_arena, &backup_arena);
+
+ if (!(nctx= new sp_rcontext(m_pcont, 0, octx)) ||
+ nctx->init(thd))
+ {
+ err_status= TRUE;
+ goto err_with_cleanup;
+ }
+
+#ifndef DBUG_OFF
+ nctx->sp= this;
+#endif
+
+ thd->spcont= nctx;
+
+ err_status= execute(thd);
+
+err_with_cleanup:
+ thd->restore_active_arena(&call_arena, &backup_arena);
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ sp_restore_security_context(thd, save_ctx);
+#endif // NO_EMBEDDED_ACCESS_CHECKS
+ delete nctx;
+ call_arena.free_items();
+ free_root(&call_mem_root, MYF(0));
+ thd->spcont= octx;
+
DBUG_RETURN(err_status);
}
@@ -1193,8 +1354,12 @@ sp_head::execute(THD *thd)
/*
Execute a function:
- evaluate parameters
+ - changes security context for SUID routines
+ - switch to new memroot
- call sp_head::execute
+ - restore old memroot
- evaluate the return value
+ - restores security context
SYNOPSIS
sp_head::execute_function()
@@ -1328,6 +1493,15 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
}
thd->spcont= nctx;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ Security_context *save_security_ctx;
+ if (set_routine_security_ctx(thd, this, FALSE, &save_security_ctx))
+ {
+ err_status= TRUE;
+ goto err_with_cleanup;
+ }
+#endif
+
if (need_binlog_call)
{
reset_dynamic(&thd->user_var_events);
@@ -1369,7 +1543,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
}
}
- if (m_type == TYPE_ENUM_FUNCTION && !err_status)
+ if (!err_status)
{
/* We need result only in function but not in trigger */
@@ -1380,8 +1554,9 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
}
}
-
- nctx->pop_all_cursors(); // To avoid memory leaks after an error
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ sp_restore_security_context(thd, save_security_ctx);
+#endif
err_with_cleanup:
delete nctx;
@@ -1404,8 +1579,10 @@ err_with_cleanup:
The function does the following steps:
- Set all parameters
+ - changes security context for SUID routines
- call sp_head::execute
- copy back values of INOUT and OUT parameters
+ - restores security context
RETURN
FALSE on success
@@ -1539,7 +1716,13 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
thd->options |= OPTION_LOG_OFF;
}
thd->spcont= nctx;
-
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ Security_context *save_security_ctx= 0;
+ if (!err_status)
+ err_status= set_routine_security_ctx(thd, this, TRUE, &save_security_ctx);
+#endif
+
if (!err_status)
err_status= execute(thd);
@@ -1588,10 +1771,14 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
}
}
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (save_security_ctx)
+ sp_restore_security_context(thd, save_security_ctx);
+#endif
+
if (!save_spcont)
delete octx;
- nctx->pop_all_cursors(); // To avoid memory leaks after an error
delete nctx;
thd->spcont= save_spcont;
@@ -1738,14 +1925,18 @@ sp_head::fill_field_definition(THD *thd, LEX *lex,
enum enum_field_types field_type,
create_field *field_def)
{
+ HA_CREATE_INFO sp_db_info;
LEX_STRING cmt = { 0, 0 };
uint unused1= 0;
int unused2= 0;
+ load_db_opt_by_name(thd, m_db.str, &sp_db_info);
+
if (field_def->init(thd, (char*) "", field_type, lex->length, lex->dec,
lex->type, (Item*) 0, (Item*) 0, &cmt, 0,
&lex->interval_list,
- (lex->charset ? lex->charset : default_charset_info),
+ (lex->charset ? lex->charset :
+ sp_db_info.default_table_charset),
lex->uint_geom_type))
return TRUE;