summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorunknown <dlenev@mysql.com>2005-03-04 17:46:45 +0300
committerunknown <dlenev@mysql.com>2005-03-04 17:46:45 +0300
commitb1095fb0cd0b42158f6ca3d8f157df5d23bec423 (patch)
treebff7a84c42deaf288f2aee7fdb2011a47b92ba6c /sql
parentd6949b3e9f8164e28bb24b7c1d948cdce12db005 (diff)
parentac9f68b9fac716ebc09b9f31b4348b0db135519b (diff)
downloadmariadb-git-b1095fb0cd0b42158f6ca3d8f157df5d23bec423.tar.gz
Manual merge SP-locking improvements patch with current tree.
mysql-test/r/mysqldump.result: Auto merged mysql-test/r/sp.result: Auto merged mysql-test/t/mysqldump.test: Auto merged mysql-test/t/sp.test: Auto merged sql/item_func.cc: Auto merged sql/mysql_priv.h: Auto merged sql/sp.cc: Auto merged sql/sp_rcontext.cc: Auto merged sql/sql_acl.cc: Auto merged sql/sql_base.cc: Auto merged sql/sql_class.h: Auto merged sql/sql_lex.cc: Auto merged sql/sql_lex.h: Auto merged sql/sql_prepare.cc: Auto merged sql/sql_update.cc: Auto merged sql/sql_view.cc: Auto merged sql/table.h: Auto merged sql/sp_head.cc: Manual merge. sql/sql_class.cc: Manual merge. sql/sql_parse.cc: Manual merge. sql/sql_yacc.yy: Manual merge.
Diffstat (limited to 'sql')
-rw-r--r--sql/item_func.cc5
-rw-r--r--sql/mysql_priv.h2
-rw-r--r--sql/sp.cc189
-rw-r--r--sql/sp.h8
-rw-r--r--sql/sp_head.cc674
-rw-r--r--sql/sp_head.h221
-rw-r--r--sql/sp_rcontext.cc8
-rw-r--r--sql/sp_rcontext.h11
-rw-r--r--sql/sql_acl.cc29
-rw-r--r--sql/sql_base.cc371
-rw-r--r--sql/sql_class.cc5
-rw-r--r--sql/sql_class.h44
-rw-r--r--sql/sql_handler.cc2
-rw-r--r--sql/sql_lex.cc7
-rw-r--r--sql/sql_lex.h36
-rw-r--r--sql/sql_parse.cc168
-rw-r--r--sql/sql_prepare.cc25
-rw-r--r--sql/sql_trigger.h13
-rw-r--r--sql/sql_update.cc4
-rw-r--r--sql/sql_view.cc72
-rw-r--r--sql/sql_yacc.yy230
-rw-r--r--sql/table.h5
-rw-r--r--sql/tztime.cc4
23 files changed, 1340 insertions, 793 deletions
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 37e59fa89d3..0d88c689d9f 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -4414,11 +4414,6 @@ Item_func_sp::execute(Item **itp)
}
#endif
- /*
- We don't need to suppress sending of OK packet here (by setting
- thd->net.no_send_ok to true), because we are not allowing statements
- in functions now.
- */
res= m_sp->execute_function(thd, args, arg_count, itp);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 428f3e51801..aef1cd1efec 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -870,7 +870,7 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves,
int setup_ftfuncs(SELECT_LEX* select);
int init_ftfuncs(THD *thd, SELECT_LEX* select, bool no_order);
void wait_for_refresh(THD *thd);
-int open_tables(THD *thd, TABLE_LIST *tables, uint *counter);
+int open_tables(THD *thd, TABLE_LIST **tables, uint *counter);
int simple_open_n_lock_tables(THD *thd,TABLE_LIST *tables);
bool open_and_lock_tables(THD *thd,TABLE_LIST *tables);
bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables);
diff --git a/sql/sp.cc b/sql/sp.cc
index a57d90dbf7a..8b8e8897180 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -720,8 +720,29 @@ sp_drop_db_routines(THD *thd, char *db)
PROCEDURE
******************************************************************************/
+/*
+ Obtain object representing stored procedure by its name from
+ stored procedures cache and looking into mysql.proc if needed.
+
+ SYNOPSIS
+ sp_find_procedure()
+ thd - thread context
+ name - name of procedure
+ cache_only - if true perform cache-only lookup
+ (Don't look in mysql.proc).
+
+ TODO
+ We should consider merging of sp_find_procedure() and
+ sp_find_function() into one sp_find_routine() function
+ (the same applies to other similarly paired functions).
+
+ RETURN VALUE
+ Non-0 pointer to sp_head object for the procedure, or
+ 0 - in case of error.
+*/
+
sp_head *
-sp_find_procedure(THD *thd, sp_name *name)
+sp_find_procedure(THD *thd, sp_name *name, bool cache_only)
{
sp_head *sp;
DBUG_ENTER("sp_find_procedure");
@@ -729,7 +750,7 @@ sp_find_procedure(THD *thd, sp_name *name)
name->m_db.length, name->m_db.str,
name->m_name.length, name->m_name.str));
- if (!(sp= sp_cache_lookup(&thd->sp_proc_cache, name)))
+ if (!(sp= sp_cache_lookup(&thd->sp_proc_cache, name)) && !cache_only)
{
if (db_find_routine(thd, TYPE_ENUM_PROCEDURE, name, &sp) == SP_OK)
sp_cache_insert(&thd->sp_proc_cache, sp);
@@ -855,6 +876,25 @@ sp_show_status_procedure(THD *thd, const char *wild)
FUNCTION
******************************************************************************/
+/*
+ Obtain object representing stored function by its name from
+ stored functions cache and looking into mysql.proc if needed.
+
+ SYNOPSIS
+ sp_find_function()
+ thd - thread context
+ name - name of function
+ cache_only - if true perform cache-only lookup
+ (Don't look in mysql.proc).
+
+ NOTE
+ See TODO section for sp_find_procedure().
+
+ RETURN VALUE
+ Non-0 pointer to sp_head object for the function, or
+ 0 - in case of error.
+*/
+
sp_head *
sp_find_function(THD *thd, sp_name *name, bool cache_only)
{
@@ -988,79 +1028,120 @@ sp_add_to_hash(HASH *h, sp_name *fun)
}
-void
+/*
+ Merge contents of two hashes containing LEX_STRING's
+
+ SYNOPSIS
+ sp_merge_hash()
+ dst - hash to which elements should be added
+ src - hash from which elements merged
+
+ RETURN VALUE
+ TRUE - if we have added some new elements to destination hash.
+ FALSE - there were no new elements in src.
+*/
+
+bool
sp_merge_hash(HASH *dst, HASH *src)
{
+ bool res= FALSE;
for (uint i=0 ; i < src->records ; i++)
{
LEX_STRING *ls= (LEX_STRING *)hash_element(src, i);
if (! hash_search(dst, (byte *)ls->str, ls->length))
+ {
my_hash_insert(dst, (byte *)ls);
+ res= TRUE;
+ }
}
+ return res;
}
-int
-sp_cache_routines(THD *thd, LEX *lex, int type)
+/*
+ Cache all routines implicitly or explicitly used by query
+ (or whatever object is represented by LEX).
+
+ SYNOPSIS
+ sp_cache_routines()
+ thd - thread context
+ lex - LEX representing query
+
+ NOTE
+ If some function is missing this won't be reported here.
+ Instead this fact will be discovered during query execution.
+
+ TODO
+ Currently if after passing through routine hashes we discover
+ that we have added something to them, we do one more pass to
+ process all routines which were missed on previous pass because
+ of these additions. We can avoid this if along with hashes
+ we use lists holding routine names and iterate other these
+ lists instead of hashes (since addition to the end of list
+ does not reorder elements in it).
+*/
+
+void
+sp_cache_routines(THD *thd, LEX *lex)
{
- HASH *h= (type == TYPE_ENUM_FUNCTION ? &lex->spfuns : &lex->spprocs);
- int ret= 0;
+ bool routines_added= TRUE;
- for (uint i=0 ; i < h->records ; i++)
+ DBUG_ENTER("sp_cache_routines");
+
+ while (routines_added)
{
- LEX_STRING *ls= (LEX_STRING *)hash_element(h, i);
- sp_name name(*ls);
+ routines_added= FALSE;
- name.m_qname= *ls;
- if (! sp_cache_lookup((type == TYPE_ENUM_FUNCTION ?
- &thd->sp_func_cache : &thd->sp_proc_cache),
- &name))
+ for (int type= TYPE_ENUM_FUNCTION; type < TYPE_ENUM_TRIGGER; type++)
{
- sp_head *sp;
- LEX *oldlex= thd->lex;
- LEX *newlex= new st_lex;
-
- thd->lex= newlex;
- newlex->proc_table= oldlex->proc_table; // hint if mysql.oper is opened
- newlex->current_select= NULL;
- name.m_name.str= strchr(name.m_qname.str, '.');
- name.m_db.length= name.m_name.str - name.m_qname.str;
- name.m_db.str= strmake_root(thd->mem_root,
- name.m_qname.str, name.m_db.length);
- name.m_name.str+= 1;
- name.m_name.length= name.m_qname.length - name.m_db.length - 1;
-
- if (db_find_routine(thd, type, &name, &sp) == SP_OK)
- {
- if (type == TYPE_ENUM_FUNCTION)
- sp_cache_insert(&thd->sp_func_cache, sp);
- else
- sp_cache_insert(&thd->sp_proc_cache, sp);
- ret= sp_cache_routines(thd, newlex, TYPE_ENUM_FUNCTION);
- if (!ret)
- {
- sp_merge_hash(&lex->spfuns, &newlex->spfuns);
- ret= sp_cache_routines(thd, newlex, TYPE_ENUM_PROCEDURE);
- }
- if (!ret)
- {
- sp_merge_hash(&lex->spprocs, &newlex->spprocs);
- sp_merge_table_hash(&lex->sptabs, &sp->m_sptabs);
- }
- delete newlex;
- thd->lex= oldlex;
- if (ret)
- break;
- }
- else
+ HASH *h= (type == TYPE_ENUM_FUNCTION ? &lex->spfuns : &lex->spprocs);
+
+ for (uint i=0 ; i < h->records ; i++)
{
- delete newlex;
- thd->lex= oldlex;
+ LEX_STRING *ls= (LEX_STRING *)hash_element(h, i);
+ sp_name name(*ls);
+ sp_head *sp;
+
+ name.m_qname= *ls;
+ if (!(sp= sp_cache_lookup((type == TYPE_ENUM_FUNCTION ?
+ &thd->sp_func_cache : &thd->sp_proc_cache),
+ &name)))
+ {
+ LEX *oldlex= thd->lex;
+ LEX *newlex= new st_lex;
+
+ thd->lex= newlex;
+ /* Pass hint pointer to mysql.proc table */
+ newlex->proc_table= oldlex->proc_table;
+ newlex->current_select= NULL;
+ name.m_name.str= strchr(name.m_qname.str, '.');
+ name.m_db.length= name.m_name.str - name.m_qname.str;
+ name.m_db.str= strmake_root(thd->mem_root, name.m_qname.str,
+ name.m_db.length);
+ name.m_name.str+= 1;
+ name.m_name.length= name.m_qname.length - name.m_db.length - 1;
+
+ if (db_find_routine(thd, type, &name, &sp) == SP_OK)
+ {
+ if (type == TYPE_ENUM_FUNCTION)
+ sp_cache_insert(&thd->sp_func_cache, sp);
+ else
+ sp_cache_insert(&thd->sp_proc_cache, sp);
+ }
+ delete newlex;
+ thd->lex= oldlex;
+ }
+
+ if (sp)
+ {
+ routines_added|= sp_merge_hash(&lex->spfuns, &sp->m_spfuns);
+ routines_added|= sp_merge_hash(&lex->spprocs, &sp->m_spprocs);
+ }
}
}
}
- return ret;
+ DBUG_VOID_RETURN;
}
/*
diff --git a/sql/sp.h b/sql/sp.h
index 6290324bb86..00dd8416c1d 100644
--- a/sql/sp.h
+++ b/sql/sp.h
@@ -34,7 +34,7 @@ int
sp_drop_db_routines(THD *thd, char *db);
sp_head *
-sp_find_procedure(THD *thd, sp_name *name);
+sp_find_procedure(THD *thd, sp_name *name, bool cache_only = 0);
int
sp_exists_routine(THD *thd, TABLE_LIST *procs, bool any, bool no_error);
@@ -82,10 +82,10 @@ sp_function_exists(THD *thd, sp_name *name);
*/
void
sp_add_to_hash(HASH *h, sp_name *fun);
-void
+bool
sp_merge_hash(HASH *dst, HASH *src);
-int
-sp_cache_routines(THD *thd, LEX *lex, int type);
+void
+sp_cache_routines(THD *thd, LEX *lex);
//
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index ad527d39d21..59cdac1b153 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -279,12 +279,16 @@ sp_head::sp_head()
{
extern byte *
sp_table_key(const byte *ptr, uint *plen, my_bool first);
+ extern byte
+ *sp_lex_sp_key(const byte *ptr, uint *plen, my_bool first);
DBUG_ENTER("sp_head::sp_head");
state= INITIALIZED;
m_backpatch.empty();
m_lex.empty();
hash_init(&m_sptabs, system_charset_info, 0, 0, 0, sp_table_key, 0, 0);
+ hash_init(&m_spfuns, system_charset_info, 0, 0, 0, sp_lex_sp_key, 0, 0);
+ hash_init(&m_spprocs, system_charset_info, 0, 0, 0, sp_lex_sp_key, 0, 0);
DBUG_VOID_RETURN;
}
@@ -460,8 +464,9 @@ sp_head::destroy()
if (lex != &m_thd->main_lex) // We got interrupted and have lex'es left
delete lex;
}
- if (m_sptabs.array.buffer)
- hash_free(&m_sptabs);
+ hash_free(&m_sptabs);
+ hash_free(&m_spfuns);
+ hash_free(&m_spprocs);
DBUG_VOID_RETURN;
}
@@ -475,6 +480,11 @@ sp_head::execute(THD *thd)
int ret= 0;
uint ip= 0;
Item_arena *old_arena;
+ query_id_t old_query_id;
+ TABLE *old_derived_tables;
+ LEX *old_lex;
+ Item_change_list old_change_list;
+ String old_packet;
#ifndef EMBEDDED_LIBRARY
@@ -495,6 +505,34 @@ sp_head::execute(THD *thd)
old_arena= thd->current_arena;
thd->current_arena= this;
+ /*
+ We have to save/restore this info when we are changing call level to
+ be able properly do close_thread_tables() in instructions.
+ */
+ old_query_id= thd->query_id;
+ old_derived_tables= thd->derived_tables;
+ thd->derived_tables= 0;
+ /*
+ It is also more efficient to save/restore current thd->lex once when
+ do it in each instruction
+ */
+ old_lex= thd->lex;
+ /*
+ We should also save Item tree change list to avoid rollback something
+ too early in the calling query.
+ */
+ old_change_list= thd->change_list;
+ thd->change_list.empty();
+ /*
+ Cursors will use thd->packet, so they may corrupt data which was prepared
+ for sending by upper level. OTOH cursors in the same routine can share this
+ buffer safely so let use use routine-local packet instead of having own
+ packet buffer for each cursor.
+
+ It is probably safe to use same thd->convert_buff everywhere.
+ */
+ old_packet.swap(thd->packet);
+
do
{
sp_instr *i;
@@ -506,7 +544,6 @@ sp_head::execute(THD *thd)
DBUG_PRINT("execute", ("Instruction %u", ip));
thd->set_time(); // Make current_time() et al work
ret= i->execute(thd, &ip);
- thd->rollback_item_tree_changes();
if (i->free_list)
cleanup_items(i->free_list);
// Check if an exception has occurred and a handler has been found
@@ -535,6 +572,17 @@ sp_head::execute(THD *thd)
}
} while (ret == 0 && !thd->killed);
+ /* Restore all saved */
+ old_packet.swap(thd->packet);
+ DBUG_ASSERT(thd->change_list.is_empty());
+ thd->change_list= old_change_list;
+ /* To avoid wiping out thd->change_list on old_change_list destruction */
+ old_change_list.empty();
+ thd->lex= old_lex;
+ thd->query_id= old_query_id;
+ DBUG_ASSERT(!thd->derived_tables);
+ thd->derived_tables= old_derived_tables;
+
cleanup_items(thd->current_arena->free_list);
thd->current_arena= old_arena;
@@ -592,14 +640,6 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)
DBUG_RETURN(-1);
}
}
-#ifdef NOT_WORKING
- /*
- Close tables opened for subselect in argument list
- This can't be done as this will close all other tables used
- by the query.
- */
- close_thread_tables(thd);
-#endif
// The rest of the frame are local variables which are all IN.
// Default all variables to null (those with default clauses will
// be set by an set instruction).
@@ -705,10 +745,6 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
nctx->set_oindex(i, static_cast<Item_splocal *>(it)->get_offset());
}
}
- // Clean up the joins before closing the tables.
- thd->lex->unit.cleanup();
- // Close tables opened for subselect in argument list
- close_thread_tables(thd);
// The rest of the frame are local variables which are all IN.
// Default all variables to null (those with default clauses will
@@ -828,11 +864,17 @@ sp_head::restore_lex(THD *thd)
oldlex->next_state= sublex->next_state;
oldlex->trg_table_fields.push_back(&sublex->trg_table_fields);
- // Collect some data from the sub statement lex.
- sp_merge_hash(&oldlex->spfuns, &sublex->spfuns);
- sp_merge_hash(&oldlex->spprocs, &sublex->spprocs);
- // Merge used tables
- sp_merge_table_list(thd, &m_sptabs, sublex->query_tables, sublex);
+ /*
+ Add routines which are used by statement to respective sets for
+ this routine
+ */
+ sp_merge_hash(&m_spfuns, &sublex->spfuns);
+ sp_merge_hash(&m_spprocs, &sublex->spprocs);
+ /*
+ Merge tables used by this statement (but not by its functions or
+ procedures) to multiset of tables used by this routine.
+ */
+ merge_table_list(thd, sublex->query_tables, sublex);
if (! sublex->sp_lex_in_use)
delete sublex;
thd->lex= oldlex;
@@ -1134,22 +1176,121 @@ sp_head::opt_mark(uint ip)
// ------------------------------------------------------------------
+
+/*
+ Prepare LEX and thread for execution of instruction, if requested open
+ and lock LEX's tables, execute instruction's core function, perform
+ cleanup afterwards.
+
+ SYNOPSIS
+ reset_lex_and_exec_core()
+ thd - thread context
+ nextp - out - next instruction
+ open_tables - if TRUE then check read access to tables in LEX's table
+ list and open and lock them (used in instructions which
+ need to calculate some expression and don't execute
+ complete statement).
+ sp_instr - instruction for which we prepare context, and which core
+ function execute by calling its exec_core() method.
+
+ NOTE
+ We are not saving/restoring some parts of THD which may need this because
+ we do this once for whole routine execution in sp_head::execute().
+
+ RETURN VALUE
+ 0/non-0 - Success/Failure
+*/
+
+int
+sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
+ bool open_tables, sp_instr* instr)
+{
+ int res= 0;
+
+ DBUG_ASSERT(!thd->derived_tables);
+ DBUG_ASSERT(thd->change_list.is_empty());
+ /*
+ Use our own lex.
+ We should not save old value since it is saved/restored in
+ sp_head::execute() when we are entering/leaving routine.
+ */
+ thd->lex= m_lex;
+
+ VOID(pthread_mutex_lock(&LOCK_thread_count));
+ thd->query_id= next_query_id();
+ VOID(pthread_mutex_unlock(&LOCK_thread_count));
+
+ /*
+ FIXME. Resetting statement (and using it) is not reentrant, thus recursive
+ functions which try to use the same LEX twice will crash server.
+ We should prevent such situations by tracking if LEX is already
+ in use and throwing error about unallowed recursion if needed.
+ OTOH it is nice to allow recursion in cases when LEX is not really
+ used (e.g. in mathematical functions), so such tracking should be
+ implemented at the same time as ability not to store LEX for
+ instruction if it is not really used.
+ */
+ reset_stmt_for_execute(thd, m_lex);
+
+ /*
+ If requested check whenever we have access to tables in LEX's table list
+ and open and lock them before executing instructtions core function.
+ */
+ if (open_tables &&
+ (check_table_access(thd, SELECT_ACL, m_lex->query_tables, 0) ||
+ open_and_lock_tables(thd, m_lex->query_tables)))
+ res= -1;
+
+ if (!res)
+ res= instr->exec_core(thd, nextp);
+
+ m_lex->unit.cleanup();
+
+ thd->proc_info="closing tables";
+ close_thread_tables(thd);
+
+ thd->rollback_item_tree_changes();
+
+ /*
+ Unlike for PS we should not call Item's destructors for newly created
+ items after execution of each instruction in stored routine. This is
+ because SP often create Item (like Item_int, Item_string etc...) when
+ they want to store some value in local variable, pass return value and
+ etc... So their life time should be longer than one instruction.
+
+ Probably we can call destructors for most of them then we are leaving
+ routine. But this won't help much as they are allocated in main query
+ MEM_ROOT anyway. So they all go to global thd->free_list.
+
+ May be we can use some other MEM_ROOT for this purprose ???
+
+ What else should we do for cleanup ?
+ cleanup_items() is called in sp_head::execute()
+ */
+ return res;
+}
+
+
//
-// sp_instr_stmt
+// sp_instr
//
-sp_instr_stmt::~sp_instr_stmt()
+int sp_instr::exec_core(THD *thd, uint *nextp)
{
- if (m_lex)
- delete m_lex;
+ DBUG_ASSERT(0);
+ return 0;
}
+
+//
+// sp_instr_stmt
+//
int
sp_instr_stmt::execute(THD *thd, uint *nextp)
{
char *query;
uint32 query_length;
DBUG_ENTER("sp_instr_stmt::execute");
- DBUG_PRINT("info", ("command: %d", m_lex->sql_command));
+ DBUG_PRINT("info", ("command: %d", m_lex_keeper.sql_command()));
int res;
query= thd->query;
@@ -1159,13 +1300,14 @@ sp_instr_stmt::execute(THD *thd, uint *nextp)
if (query_cache_send_result_to_client(thd,
thd->query, thd->query_length) <= 0)
{
- res= exec_stmt(thd, m_lex);
+ res= m_lex_keeper.reset_lex_and_exec_core(thd, nextp, FALSE, this);
query_cache_end_of_result(thd);
}
+ else
+ *nextp= m_ip+1;
thd->query= query;
thd->query_length= query_length;
}
- *nextp = m_ip+1;
DBUG_RETURN(res);
}
@@ -1174,39 +1316,15 @@ sp_instr_stmt::print(String *str)
{
str->reserve(12);
str->append("stmt ");
- str->qs_append((uint)m_lex->sql_command);
+ str->qs_append((uint)m_lex_keeper.sql_command());
}
int
-sp_instr_stmt::exec_stmt(THD *thd, LEX *lex)
+sp_instr_stmt::exec_core(THD *thd, uint *nextp)
{
- LEX *olex; // The other lex
- int res;
-
- olex= thd->lex; // Save the other lex
- thd->lex= lex; // Use my own lex
- thd->lex->thd = thd; // QQ Not reentrant!
- thd->lex->unit.thd= thd; // QQ Not reentrant
- thd->free_list= NULL;
-
- VOID(pthread_mutex_lock(&LOCK_thread_count));
- thd->query_id= next_query_id();
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
-
- reset_stmt_for_execute(thd, lex);
-
- res= mysql_execute_command(thd);
-
- lex->unit.cleanup();
- if (thd->lock || thd->open_tables || thd->derived_tables)
- {
- thd->proc_info="closing tables";
- close_thread_tables(thd); /* Free tables */
- }
-
- thd->lex= olex; // Restore the other lex
-
+ int res= mysql_execute_command(thd);
+ *nextp= m_ip+1;
return res;
}
@@ -1218,14 +1336,16 @@ sp_instr_set::execute(THD *thd, uint *nextp)
{
DBUG_ENTER("sp_instr_set::execute");
DBUG_PRINT("info", ("offset: %u", m_offset));
+
+ DBUG_RETURN(m_lex_keeper.reset_lex_and_exec_core(thd, nextp, TRUE, this));
+}
+
+int
+sp_instr_set::exec_core(THD *thd, uint *nextp)
+{
Item *it;
int res;
- if (tables &&
- ((res= check_table_access(thd, SELECT_ACL, tables, 0)) ||
- (res= open_and_lock_tables(thd, tables))))
- DBUG_RETURN(res);
-
it= sp_eval_func_item(thd, m_value, m_type);
if (! it)
res= -1;
@@ -1235,9 +1355,8 @@ sp_instr_set::execute(THD *thd, uint *nextp)
thd->spcont->set_item(m_offset, it);
}
*nextp = m_ip+1;
- if (tables && (thd->lock || thd->open_tables || thd->derived_tables))
- close_thread_tables(thd);
- DBUG_RETURN(res);
+
+ return res;
}
void
@@ -1250,32 +1369,6 @@ sp_instr_set::print(String *str)
m_value->print(str);
}
-//
-// sp_instr_set_user_var
-//
-int
-sp_instr_set_user_var::execute(THD *thd, uint *nextp)
-{
- int res= 0;
-
- DBUG_ENTER("sp_instr_set_user_var::execute");
- /*
- It is ok to pass 0 as 3rd argument to fix_fields() since
- Item_func_set_user_var::fix_fields() won't use it.
- QQ: Still unsure what should we return in case of error 1 or -1 ?
- */
- if (!m_set_var_item.fixed && m_set_var_item.fix_fields(thd, 0, 0) ||
- m_set_var_item.check() || m_set_var_item.update())
- res= -1;
- *nextp= m_ip + 1;
- DBUG_RETURN(res);
-}
-
-void
-sp_instr_set_user_var::print(String *str)
-{
- m_set_var_item.print_as_stmt(str);
-}
//
// sp_instr_set_trigger_field
@@ -1368,19 +1461,21 @@ sp_instr_jump::opt_move(uint dst, List<sp_instr> *bp)
//
// sp_instr_jump_if
//
+
int
sp_instr_jump_if::execute(THD *thd, uint *nextp)
{
DBUG_ENTER("sp_instr_jump_if::execute");
DBUG_PRINT("info", ("destination: %u", m_dest));
+ DBUG_RETURN(m_lex_keeper.reset_lex_and_exec_core(thd, nextp, TRUE, this));
+}
+
+int
+sp_instr_jump_if::exec_core(THD *thd, uint *nextp)
+{
Item *it;
int res;
- if (tables &&
- ((res= check_table_access(thd, SELECT_ACL, tables, 0)) ||
- (res= open_and_lock_tables(thd, tables))))
- DBUG_RETURN(res);
-
it= sp_eval_func_item(thd, m_expr, MYSQL_TYPE_TINY);
if (!it)
res= -1;
@@ -1392,9 +1487,8 @@ sp_instr_jump_if::execute(THD *thd, uint *nextp)
else
*nextp = m_ip+1;
}
- if (tables && (thd->lock || thd->open_tables || thd->derived_tables))
- close_thread_tables(thd);
- DBUG_RETURN(res);
+
+ return res;
}
void
@@ -1430,14 +1524,16 @@ sp_instr_jump_if_not::execute(THD *thd, uint *nextp)
{
DBUG_ENTER("sp_instr_jump_if_not::execute");
DBUG_PRINT("info", ("destination: %u", m_dest));
+ DBUG_RETURN(m_lex_keeper.reset_lex_and_exec_core(thd, nextp, TRUE, this));
+}
+
+
+int
+sp_instr_jump_if_not::exec_core(THD *thd, uint *nextp)
+{
Item *it;
int res;
- if (tables &&
- ((res= check_table_access(thd, SELECT_ACL, tables, 0)) ||
- (res= open_and_lock_tables(thd, tables))))
- DBUG_RETURN(res);
-
it= sp_eval_func_item(thd, m_expr, MYSQL_TYPE_TINY);
if (! it)
res= -1;
@@ -1449,9 +1545,8 @@ sp_instr_jump_if_not::execute(THD *thd, uint *nextp)
else
*nextp = m_ip+1;
}
- if (tables && (thd->lock || thd->open_tables || thd->derived_tables))
- close_thread_tables(thd);
- DBUG_RETURN(res);
+
+ return res;
}
void
@@ -1482,18 +1577,21 @@ sp_instr_jump_if_not::opt_mark(sp_head *sp)
//
// sp_instr_freturn
//
+
int
sp_instr_freturn::execute(THD *thd, uint *nextp)
{
DBUG_ENTER("sp_instr_freturn::execute");
+ DBUG_RETURN(m_lex_keeper.reset_lex_and_exec_core(thd, nextp, TRUE, this));
+}
+
+
+int
+sp_instr_freturn::exec_core(THD *thd, uint *nextp)
+{
Item *it;
int res;
- if (tables &&
- ((res= check_table_access(thd, SELECT_ACL, tables, 0)) ||
- (res= open_and_lock_tables(thd, tables))))
- DBUG_RETURN(res);
-
it= sp_eval_func_item(thd, m_value, m_type);
if (! it)
res= -1;
@@ -1503,7 +1601,8 @@ sp_instr_freturn::execute(THD *thd, uint *nextp)
thd->spcont->set_result(it);
}
*nextp= UINT_MAX;
- DBUG_RETURN(res);
+
+ return res;
}
void
@@ -1637,17 +1736,11 @@ int
sp_instr_cpush::execute(THD *thd, uint *nextp)
{
DBUG_ENTER("sp_instr_cpush::execute");
- thd->spcont->push_cursor(m_lex);
+ thd->spcont->push_cursor(&m_lex_keeper);
*nextp= m_ip+1;
DBUG_RETURN(0);
}
-sp_instr_cpush::~sp_instr_cpush()
-{
- if (m_lex)
- delete m_lex;
-}
-
void
sp_instr_cpush::print(String *str)
{
@@ -1694,19 +1787,30 @@ sp_instr_copen::execute(THD *thd, uint *nextp)
res= -1;
else
{
- LEX *lex= c->pre_open(thd);
+ sp_lex_keeper *lex_keeper= c->pre_open(thd);
- if (! lex)
+ if (!lex_keeper)
+ {
res= -1;
+ *nextp= m_ip+1;
+ }
else
- res= exec_stmt(thd, lex);
- c->post_open(thd, (lex ? TRUE : FALSE));
+ res= lex_keeper->reset_lex_and_exec_core(thd, nextp, FALSE, this);
+
+ c->post_open(thd, (lex_keeper ? TRUE : FALSE));
}
- *nextp= m_ip+1;
DBUG_RETURN(res);
}
+int
+sp_instr_copen::exec_core(THD *thd, uint *nextp)
+{
+ int res= mysql_execute_command(thd);
+ *nextp= m_ip+1;
+ return res;
+}
+
void
sp_instr_copen::print(String *str)
{
@@ -1858,14 +1962,17 @@ sp_restore_security_context(THD *thd, sp_head *sp, st_sp_security_context *ctxp)
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
/*
- * Table merge hash table
- *
- */
+ Structure that represent all instances of one table
+ in optimized multi-set of tables used by routine.
+*/
+
typedef struct st_sp_table
{
LEX_STRING qname;
bool temp;
TABLE_LIST *table;
+ uint lock_count;
+ uint query_lock_count;
} SP_TABLE;
byte *
@@ -1876,23 +1983,47 @@ sp_table_key(const byte *ptr, uint *plen, my_bool first)
return (byte *)tab->qname.str;
}
+
/*
- * Merge the table list into the hash table.
- * If the optional lex is provided, it's used to check and set
- * the flag for creation of a temporary table.
- */
+ Merge the list of tables used by some query into the multi-set of
+ tables used by routine.
+
+ SYNOPSIS
+ merge_table_list()
+ thd - thread context
+ table - table list
+ lex_for_tmp_check - LEX of the query for which we are merging
+ table list.
+
+ NOTE
+ This method will use LEX provided to check whenever we are creating
+ temporary table and mark it as such in target multi-set.
+
+ RETURN VALUE
+ TRUE - Success
+ FALSE - Error
+*/
+
bool
-sp_merge_table_list(THD *thd, HASH *h, TABLE_LIST *table,
- LEX *lex_for_tmp_check)
+sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check)
{
+ SP_TABLE *tab;
+
+ if (lex_for_tmp_check->sql_command == SQLCOM_DROP_TABLE &&
+ lex_for_tmp_check->drop_temporary)
+ return TRUE;
+
+ for (uint i= 0 ; i < m_sptabs.records ; i++)
+ {
+ tab= (SP_TABLE *)hash_element(&m_sptabs, i);
+ tab->query_lock_count= 0;
+ }
+
for (; table ; table= table->next_global)
- if (!table->derived &&
- (!table->select_lex ||
- !(table->select_lex->options & OPTION_SCHEMA_TABLE)))
+ if (!table->derived && !table->schema_table)
{
char tname[64+1+64+1+64+1]; // db.table.alias\0
uint tlen, alen;
- SP_TABLE *tab;
tlen= table->db_length;
memcpy(tname, table->db, tlen);
@@ -1905,10 +2036,17 @@ sp_merge_table_list(THD *thd, HASH *h, TABLE_LIST *table,
tlen+= alen;
tname[tlen]= '\0';
- if ((tab= (SP_TABLE *)hash_search(h, (byte *)tname, tlen)))
+ /*
+ It is safe to store pointer to table list elements in hash,
+ since they are supposed to have the same lifetime.
+ */
+ if ((tab= (SP_TABLE *)hash_search(&m_sptabs, (byte *)tname, tlen)))
{
if (tab->table->lock_type < table->lock_type)
tab->table= table; // Use the table with the highest lock type
+ tab->query_lock_count++;
+ if (tab->query_lock_count > tab->lock_count)
+ tab->lock_count++;
}
else
{
@@ -1918,152 +2056,102 @@ sp_merge_table_list(THD *thd, HASH *h, TABLE_LIST *table,
tab->qname.str= (char *)thd->strmake(tname, tab->qname.length);
if (!tab->qname.str)
return FALSE;
- if (lex_for_tmp_check &&
- lex_for_tmp_check->sql_command == SQLCOM_CREATE_TABLE &&
+ if (lex_for_tmp_check->sql_command == SQLCOM_CREATE_TABLE &&
lex_for_tmp_check->query_tables == table &&
lex_for_tmp_check->create_info.options & HA_LEX_CREATE_TMP_TABLE)
tab->temp= TRUE;
tab->table= table;
- my_hash_insert(h, (byte *)tab);
+ tab->lock_count= tab->query_lock_count= 1;
+ my_hash_insert(&m_sptabs, (byte *)tab);
}
}
return TRUE;
}
-void
-sp_merge_routine_tables(THD *thd, LEX *lex)
-{
- uint i;
- for (i= 0 ; i < lex->spfuns.records ; i++)
- {
- sp_head *sp;
- LEX_STRING *ls= (LEX_STRING *)hash_element(&lex->spfuns, i);
- sp_name name(*ls);
+/*
+ Add tables used by routine to the table list.
- name.m_qname= *ls;
- if ((sp= sp_cache_lookup(&thd->sp_func_cache, &name)))
- sp_merge_table_hash(&lex->sptabs, &sp->m_sptabs);
- }
- for (i= 0 ; i < lex->spprocs.records ; i++)
- {
- sp_head *sp;
- LEX_STRING *ls= (LEX_STRING *)hash_element(&lex->spprocs, i);
- sp_name name(*ls);
+ SYNOPSIS
+ add_used_tables_to_table_list()
+ thd - thread context
+ query_tables_last_ptr - (in/out) pointer the next_global member of last
+ element of the list where tables will be added
+ (or to its root).
- name.m_qname= *ls;
- if ((sp= sp_cache_lookup(&thd->sp_proc_cache, &name)))
- sp_merge_table_hash(&lex->sptabs, &sp->m_sptabs);
- }
-}
+ DESCRIPTION
+ Converts multi-set of tables used by this routine to table list and adds
+ this list to the end of table list specified by 'query_tables_last_ptr'.
-void
-sp_merge_table_hash(HASH *hdst, HASH *hsrc)
-{
- for (uint i=0 ; i < hsrc->records ; i++)
- {
- SP_TABLE *tabdst;
- SP_TABLE *tabsrc= (SP_TABLE *)hash_element(hsrc, i);
+ Elements of list will be allocated in PS memroot, so this list will be
+ persistent between PS executions.
- if (! (tabdst= (SP_TABLE *)hash_search(hdst,
- (byte *) tabsrc->qname.str,
- tabsrc->qname.length)))
- {
- my_hash_insert(hdst, (byte *)tabsrc);
- }
- else
- {
- if (tabdst->table->lock_type < tabsrc->table->lock_type)
- tabdst->table= tabsrc->table; // Use the highest lock type
- }
- }
-}
+ RETURN VALUE
+ TRUE - if some elements were added, FALSE - otherwise.
+*/
-TABLE_LIST *
-sp_hash_to_table_list(THD *thd, HASH *h)
+bool
+sp_head::add_used_tables_to_table_list(THD *thd,
+ TABLE_LIST ***query_tables_last_ptr)
{
uint i;
- TABLE_LIST *tables= NULL;
- DBUG_ENTER("sp_hash_to_table_list");
+ Item_arena *arena, backup;
+ bool result= FALSE;
+ DBUG_ENTER("sp_head::add_used_tables_to_table_list");
+
+ /*
+ Use persistent arena for table list allocation to be PS friendly.
+ */
+ arena= thd->change_arena_if_needed(&backup);
- for (i=0 ; i < h->records ; i++)
+ for (i=0 ; i < m_sptabs.records ; i++)
{
- SP_TABLE *stab= (SP_TABLE *)hash_element(h, i);
+ char *tab_buff;
+ TABLE_LIST *table, *otable;
+ SP_TABLE *stab= (SP_TABLE *)hash_element(&m_sptabs, i);
if (stab->temp)
continue;
- TABLE_LIST *table, *otable= stab->table;
-
- if (! (table= (TABLE_LIST *)thd->calloc(sizeof(TABLE_LIST))))
- return NULL;
- table->db= otable->db;
- table->db_length= otable->db_length;
- table->alias= otable->alias;
- table->table_name= otable->table_name;
- table->table_name_length= otable->table_name_length;
- table->lock_type= otable->lock_type;
- table->updating= otable->updating;
- table->force_index= otable->force_index;
- table->ignore_leaves= otable->ignore_leaves;
- table->derived= otable->derived;
- table->schema_table= otable->schema_table;
- table->select_lex= otable->select_lex;
- table->cacheable_table= otable->cacheable_table;
- table->use_index= otable->use_index;
- table->ignore_index= otable->ignore_index;
- table->option= otable->option;
-
- table->next_global= tables;
- tables= table;
- }
- DBUG_RETURN(tables);
-}
-bool
-sp_open_and_lock_tables(THD *thd, TABLE_LIST *tables)
-{
- DBUG_ENTER("sp_open_and_lock_tables");
- bool ret;
+ otable= stab->table;
- thd->in_lock_tables= 1;
- thd->options|= OPTION_TABLE_LOCK;
- if (simple_open_n_lock_tables(thd, tables))
- {
- thd->options&= ~(ulong)(OPTION_TABLE_LOCK);
- ret= FALSE;
- }
- else
- {
-#if 0
- // QQ What about this?
-#ifdef HAVE_QUERY_CACHE
- if (thd->variables.query_cache_wlock_invalidate)
- query_cache.invalidate_locked_for_write(first_table); // QQ first_table?
-#endif /* HAVE_QUERY_CACHE */
-#endif
- thd->locked_tables= thd->lock;
- thd->lock= 0;
- ret= TRUE;
- }
- thd->in_lock_tables= 0;
- DBUG_RETURN(ret);
-}
+ if (!(tab_buff= (char *)thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST)) *
+ stab->lock_count)))
+ DBUG_RETURN(FALSE);
-void
-sp_unlock_tables(THD *thd)
-{
- thd->lock= thd->locked_tables;
- thd->locked_tables= 0;
- close_thread_tables(thd); // Free tables
- if (thd->options & OPTION_TABLE_LOCK)
- {
-#if 0
- // QQ What about this?
- end_active_trans(thd);
-#endif
- thd->options&= ~(ulong)(OPTION_TABLE_LOCK);
+ for (uint j= 0; j < stab->lock_count; j++)
+ {
+ table= (TABLE_LIST *)tab_buff;
+
+ /*
+ It's enough to just copy the pointers as the data will not change
+ during the lifetime of the SP. If the SP is used by PS, we assume
+ that the PS will be invalidated if the functions is deleted or
+ changed.
+ */
+ table->db= otable->db;
+ table->db_length= otable->db_length;
+ table->alias= otable->alias;
+ table->table_name= otable->table_name;
+ table->table_name_length= otable->table_name_length;
+ table->lock_type= otable->lock_type;
+ table->cacheable_table= 1;
+ table->prelocking_placeholder= 1;
+
+ /* Everyting else should be zeroed */
+
+ **query_tables_last_ptr= table;
+ table->prev_global= *query_tables_last_ptr;
+ *query_tables_last_ptr= &table->next_global;
+
+ tab_buff+= ALIGN_SIZE(sizeof(TABLE_LIST));
+ result= TRUE;
+ }
}
- if (thd->global_read_lock)
- unlock_global_read_lock(thd);
+
+ if (arena)
+ thd->restore_backup_item_arena(arena, &backup);
+
+ DBUG_RETURN(result);
}
/*
@@ -2095,3 +2183,73 @@ sp_add_to_query_tables(THD *thd, LEX *lex,
lex->add_to_query_tables(table);
return table;
}
+
+
+/*
+ Auxilary function for adding tables used by routines used in query
+ to table lists.
+
+ SYNOPSIS
+ sp_add_sp_tables_to_table_list_aux()
+ thd - thread context
+ lex - LEX to which table list tables will be added
+ func_hash - routines for which tables should be added
+ func_cache- SP cache in which this routines should be looked up
+
+ NOTE
+ See sp_add_sp_tables_to_table_list() for more info.
+
+ RETURN VALUE
+ TRUE - some tables were added
+ FALSE - no tables were added.
+*/
+
+static bool
+sp_add_sp_tables_to_table_list_aux(THD *thd, LEX *lex, HASH *func_hash,
+ sp_cache **func_cache)
+{
+ uint i;
+ bool result= FALSE;
+
+ for (i= 0 ; i < func_hash->records ; i++)
+ {
+ sp_head *sp;
+ LEX_STRING *ls= (LEX_STRING *)hash_element(func_hash, i);
+ sp_name name(*ls);
+
+ name.m_qname= *ls;
+ if ((sp= sp_cache_lookup(func_cache, &name)))
+ result|= sp->add_used_tables_to_table_list(thd, &lex->query_tables_last);
+ }
+
+ return result;
+}
+
+
+/*
+ Add tables used by routines used in query to table list.
+
+ SYNOPSIS
+ sp_add_sp_tables_to_table_list()
+ thd - thread context
+ lex - LEX to which table list tables will be added
+ func_lex - LEX for which functions we get tables
+ (useful for adding tables used by view routines)
+
+ NOTE
+ Elements of list will be allocated in PS memroot, so this
+ list will be persistent between PS execetutions.
+
+ RETURN VALUE
+ TRUE - some tables were added
+ FALSE - no tables were added.
+*/
+
+bool
+sp_add_sp_tables_to_table_list(THD *thd, LEX *lex, LEX *func_lex)
+{
+ return (sp_add_sp_tables_to_table_list_aux(thd, lex, &func_lex->spfuns,
+ &thd->sp_func_cache) |
+ sp_add_sp_tables_to_table_list_aux(thd, lex, &func_lex->spprocs,
+ &thd->sp_proc_cache));
+}
diff --git a/sql/sp_head.h b/sql/sp_head.h
index 5df9c753048..60979a438cb 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -103,7 +103,14 @@ public:
LEX_STRING m_definer_host;
longlong m_created;
longlong m_modified;
- HASH m_sptabs; /* Merged table lists */
+ /*
+ Sets containing names of SP and SF used by this routine.
+
+ TODO Probably we should combine these two hashes in one. It will
+ decrease memory overhead ans simplify algorithms using them. The
+ same applies to similar hashes in LEX.
+ */
+ HASH m_spfuns, m_spprocs;
// Pointers set during parsing
uchar *m_param_begin, *m_param_end, *m_returns_begin, *m_returns_end,
*m_body_begin;
@@ -225,6 +232,10 @@ public:
return ip;
}
+ /* Add tables used by routine to the table list. */
+ bool add_used_tables_to_table_list(THD *thd,
+ TABLE_LIST ***query_tables_last_ptr);
+
private:
MEM_ROOT *m_thd_root; // Temp. store for thd's mem_root
@@ -240,10 +251,20 @@ private:
sp_instr *instr;
} bp_t;
List<bp_t> m_backpatch; // Instructions needing backpatching
+ /*
+ Multi-set representing optimized list of tables to be locked by this
+ routine. Does not include tables which are used by invoked routines.
+ */
+ HASH m_sptabs;
int
execute(THD *thd);
+ /*
+ Merge the list of tables used by query into the multi-set of tables used
+ by routine.
+ */
+ bool merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check);
}; // class sp_head : public Sql_alloc
@@ -277,6 +298,17 @@ public:
// Returns 0 on success, non-zero if some error occured.
virtual int execute(THD *thd, uint *nextp) = 0;
+ /*
+ Execute core function of instruction after all preparations (e.g.
+ setting of proper LEX, saving part of the thread context have been
+ done).
+
+ Should be implemented for instructions using expressions or whole
+ statements (thus having to have own LEX). Used in concert with
+ sp_lex_keeper class and its descendants.
+ */
+ virtual int exec_core(THD *thd, uint *nextp);
+
virtual void print(String *str) = 0;
virtual void backpatch(uint dest, sp_pcontext *dst_ctx)
@@ -301,6 +333,60 @@ public:
}; // class sp_instr : public Sql_alloc
+/*
+ Auxilary class to which instructions delegate responsibility
+ for handling LEX and preparations before executing statement
+ or calculating complex expression.
+
+ Exist mainly to avoid having double hierarchy between instruction
+ classes.
+
+ TODO: Add ability to not store LEX and do any preparations if
+ expression used is simple.
+*/
+
+class sp_lex_keeper
+{
+ /* Prevent use of these */
+ sp_lex_keeper(const sp_lex_keeper &);
+ void operator=(sp_lex_keeper &);
+public:
+
+ sp_lex_keeper(LEX *lex, bool lex_resp)
+ : m_lex(lex), m_lex_resp(lex_resp)
+ {
+ lex->sp_lex_in_use= TRUE;
+ }
+ virtual ~sp_lex_keeper()
+ {
+ if (m_lex_resp)
+ delete m_lex;
+ }
+
+ /*
+ Prepare execution of instruction using LEX, if requested check whenever
+ we have read access to tables used and open/lock them, call instruction's
+ exec_core() method, perform cleanup afterwards.
+ */
+ int reset_lex_and_exec_core(THD *thd, uint *nextp, bool open_tables,
+ sp_instr* instr);
+
+ inline uint sql_command() const
+ {
+ return (uint)m_lex->sql_command;
+ }
+
+private:
+
+ LEX *m_lex;
+ /*
+ Indicates whenever this sp_lex_keeper instance responsible
+ for LEX deletion.
+ */
+ bool m_lex_resp;
+};
+
+
//
// Call out to some prepared SQL statement.
//
@@ -313,38 +399,25 @@ public:
LEX_STRING m_query; // For thd->query
- sp_instr_stmt(uint ip, sp_pcontext *ctx)
- : sp_instr(ip, ctx), m_lex(NULL)
+ sp_instr_stmt(uint ip, sp_pcontext *ctx, LEX *lex)
+ : sp_instr(ip, ctx), m_lex_keeper(lex, TRUE)
{
m_query.str= 0;
m_query.length= 0;
}
- virtual ~sp_instr_stmt();
+ virtual ~sp_instr_stmt()
+ {};
virtual int execute(THD *thd, uint *nextp);
- virtual void print(String *str);
-
- inline void
- set_lex(LEX *lex)
- {
- m_lex= lex;
- }
-
- inline LEX *
- get_lex()
- {
- return m_lex;
- }
+ virtual int exec_core(THD *thd, uint *nextp);
-protected:
-
- int exec_stmt(THD *thd, LEX *lex); // Execute a statement
+ virtual void print(String *str);
private:
- LEX *m_lex; // My own lex
+ sp_lex_keeper m_lex_keeper;
}; // class sp_instr_stmt : public sp_instr
@@ -356,12 +429,11 @@ class sp_instr_set : public sp_instr
public:
- TABLE_LIST *tables;
-
sp_instr_set(uint ip, sp_pcontext *ctx,
- uint offset, Item *val, enum enum_field_types type)
- : sp_instr(ip, ctx),
- tables(NULL), m_offset(offset), m_value(val), m_type(type)
+ uint offset, Item *val, enum enum_field_types type,
+ LEX *lex, bool lex_resp)
+ : sp_instr(ip, ctx), m_offset(offset), m_value(val), m_type(type),
+ m_lex_keeper(lex, lex_resp)
{}
virtual ~sp_instr_set()
@@ -369,6 +441,8 @@ public:
virtual int execute(THD *thd, uint *nextp);
+ virtual int exec_core(THD *thd, uint *nextp);
+
virtual void print(String *str);
private:
@@ -376,42 +450,12 @@ private:
uint m_offset; // Frame offset
Item *m_value;
enum enum_field_types m_type; // The declared type
+ sp_lex_keeper m_lex_keeper;
}; // class sp_instr_set : public sp_instr
/*
- Set user variable instruction.
- Used in functions and triggers to set user variables because we don't
- want use sp_instr_stmt + "SET @a:=..." statement in this case since
- latter will close all tables and thus will ruin execution of statement
- calling/invoking this function/trigger.
-*/
-class sp_instr_set_user_var : public sp_instr
-{
- sp_instr_set_user_var(const sp_instr_set_user_var &);
- void operator=(sp_instr_set_user_var &);
-
-public:
-
- sp_instr_set_user_var(uint ip, sp_pcontext *ctx, LEX_STRING var, Item *val)
- : sp_instr(ip, ctx), m_set_var_item(var, val)
- {}
-
- virtual ~sp_instr_set_user_var()
- {}
-
- virtual int execute(THD *thd, uint *nextp);
-
- virtual void print(String *str);
-
-private:
-
- Item_func_set_user_var m_set_var_item;
-}; // class sp_instr_set_user_var : public sp_instr
-
-
-/*
Set NEW/OLD row field value instruction. Used in triggers.
*/
class sp_instr_set_trigger_field : public sp_instr
@@ -492,14 +536,12 @@ class sp_instr_jump_if : public sp_instr_jump
public:
- TABLE_LIST *tables;
-
- sp_instr_jump_if(uint ip, sp_pcontext *ctx, Item *i)
- : sp_instr_jump(ip, ctx), tables(NULL), m_expr(i)
+ sp_instr_jump_if(uint ip, sp_pcontext *ctx, Item *i, LEX *lex)
+ : sp_instr_jump(ip, ctx), m_expr(i), m_lex_keeper(lex, TRUE)
{}
- sp_instr_jump_if(uint ip, sp_pcontext *ctx, Item *i, uint dest)
- : sp_instr_jump(ip, ctx, dest), tables(NULL), m_expr(i)
+ sp_instr_jump_if(uint ip, sp_pcontext *ctx, Item *i, uint dest, LEX *lex)
+ : sp_instr_jump(ip, ctx, dest), m_expr(i), m_lex_keeper(lex, TRUE)
{}
virtual ~sp_instr_jump_if()
@@ -507,6 +549,8 @@ public:
virtual int execute(THD *thd, uint *nextp);
+ virtual int exec_core(THD *thd, uint *nextp);
+
virtual void print(String *str);
virtual uint opt_mark(sp_head *sp);
@@ -519,6 +563,7 @@ public:
private:
Item *m_expr; // The condition
+ sp_lex_keeper m_lex_keeper;
}; // class sp_instr_jump_if : public sp_instr_jump
@@ -530,14 +575,12 @@ class sp_instr_jump_if_not : public sp_instr_jump
public:
- TABLE_LIST *tables;
-
- sp_instr_jump_if_not(uint ip, sp_pcontext *ctx, Item *i)
- : sp_instr_jump(ip, ctx), tables(NULL), m_expr(i)
+ sp_instr_jump_if_not(uint ip, sp_pcontext *ctx, Item *i, LEX *lex)
+ : sp_instr_jump(ip, ctx), m_expr(i), m_lex_keeper(lex, TRUE)
{}
- sp_instr_jump_if_not(uint ip, sp_pcontext *ctx, Item *i, uint dest)
- : sp_instr_jump(ip, ctx, dest), tables(NULL), m_expr(i)
+ sp_instr_jump_if_not(uint ip, sp_pcontext *ctx, Item *i, uint dest, LEX *lex)
+ : sp_instr_jump(ip, ctx, dest), m_expr(i), m_lex_keeper(lex, TRUE)
{}
virtual ~sp_instr_jump_if_not()
@@ -545,6 +588,8 @@ public:
virtual int execute(THD *thd, uint *nextp);
+ virtual int exec_core(THD *thd, uint *nextp);
+
virtual void print(String *str);
virtual uint opt_mark(sp_head *sp);
@@ -557,6 +602,7 @@ public:
private:
Item *m_expr; // The condition
+ sp_lex_keeper m_lex_keeper;
}; // class sp_instr_jump_if_not : public sp_instr_jump
@@ -568,11 +614,9 @@ class sp_instr_freturn : public sp_instr
public:
- TABLE_LIST *tables;
-
sp_instr_freturn(uint ip, sp_pcontext *ctx,
- Item *val, enum enum_field_types type)
- : sp_instr(ip, ctx), tables(NULL), m_value(val), m_type(type)
+ Item *val, enum enum_field_types type, LEX *lex)
+ : sp_instr(ip, ctx), m_value(val), m_type(type), m_lex_keeper(lex, TRUE)
{}
virtual ~sp_instr_freturn()
@@ -580,6 +624,8 @@ public:
virtual int execute(THD *thd, uint *nextp);
+ virtual int exec_core(THD *thd, uint *nextp);
+
virtual void print(String *str);
virtual uint opt_mark(sp_head *sp)
@@ -592,6 +638,7 @@ protected:
Item *m_value;
enum enum_field_types m_type;
+ sp_lex_keeper m_lex_keeper;
}; // class sp_instr_freturn : public sp_instr
@@ -710,10 +757,11 @@ class sp_instr_cpush : public sp_instr
public:
sp_instr_cpush(uint ip, sp_pcontext *ctx, LEX *lex)
- : sp_instr(ip, ctx), m_lex(lex)
+ : sp_instr(ip, ctx), m_lex_keeper(lex, TRUE)
{}
- virtual ~sp_instr_cpush();
+ virtual ~sp_instr_cpush()
+ {}
virtual int execute(THD *thd, uint *nextp);
@@ -721,7 +769,7 @@ public:
private:
- LEX *m_lex;
+ sp_lex_keeper m_lex_keeper;
}; // class sp_instr_cpush : public sp_instr
@@ -760,7 +808,7 @@ private:
}; // class sp_instr_cpop : public sp_instr
-class sp_instr_copen : public sp_instr_stmt
+class sp_instr_copen : public sp_instr
{
sp_instr_copen(const sp_instr_copen &); /* Prevent use of these */
void operator=(sp_instr_copen &);
@@ -768,7 +816,7 @@ class sp_instr_copen : public sp_instr_stmt
public:
sp_instr_copen(uint ip, sp_pcontext *ctx, uint c)
- : sp_instr_stmt(ip, ctx), m_cursor(c)
+ : sp_instr(ip, ctx), m_cursor(c)
{}
virtual ~sp_instr_copen()
@@ -776,6 +824,8 @@ public:
virtual int execute(THD *thd, uint *nextp);
+ virtual int exec_core(THD *thd, uint *nextp);
+
virtual void print(String *str);
private:
@@ -893,22 +943,11 @@ void
sp_restore_security_context(THD *thd, sp_head *sp,st_sp_security_context *ctxp);
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
-bool
-sp_merge_table_list(THD *thd, HASH *h, TABLE_LIST *table,
- LEX *lex_for_tmp_check = 0);
-void
-sp_merge_routine_tables(THD *thd, LEX *lex);
-void
-sp_merge_table_hash(HASH *hdst, HASH *hsrc);
-TABLE_LIST *
-sp_hash_to_table_list(THD *thd, HASH *h);
-bool
-sp_open_and_lock_tables(THD *thd, TABLE_LIST *tables);
-void
-sp_unlock_tables(THD *thd);
TABLE_LIST *
sp_add_to_query_tables(THD *thd, LEX *lex,
const char *db, const char *name,
thr_lock_type locktype);
+bool
+sp_add_sp_tables_to_table_list(THD *thd, LEX *lex, LEX *func_lex);
#endif /* _SP_HEAD_H_ */
diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc
index 5b177650726..9b29c173856 100644
--- a/sql/sp_rcontext.cc
+++ b/sql/sp_rcontext.cc
@@ -125,9 +125,9 @@ sp_rcontext::restore_variables(uint fp)
}
void
-sp_rcontext::push_cursor(LEX *lex)
+sp_rcontext::push_cursor(sp_lex_keeper *lex_keeper)
{
- m_cstack[m_ccount++]= new sp_cursor(lex);
+ m_cstack[m_ccount++]= new sp_cursor(lex_keeper);
}
void
@@ -148,7 +148,7 @@ sp_rcontext::pop_cursors(uint count)
// We have split this in two to make it easy for sp_instr_copen
// to reuse the sp_instr::exec_stmt() code.
-LEX *
+sp_lex_keeper*
sp_cursor::pre_open(THD *thd)
{
if (m_isopen)
@@ -168,7 +168,7 @@ sp_cursor::pre_open(THD *thd)
m_nseof= thd->net.no_send_eof;
thd->net.no_send_eof= TRUE;
- return m_lex;
+ return m_lex_keeper;
}
void
diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h
index 8e818ab76d1..37d718048a0 100644
--- a/sql/sp_rcontext.h
+++ b/sql/sp_rcontext.h
@@ -25,6 +25,7 @@
struct sp_cond_type;
class sp_cursor;
struct sp_pvar;
+class sp_lex_keeper;
#define SP_HANDLER_NONE 0
#define SP_HANDLER_EXIT 1
@@ -164,7 +165,7 @@ class sp_rcontext : public Sql_alloc
restore_variables(uint fp);
void
- push_cursor(LEX *lex);
+ push_cursor(sp_lex_keeper *lex_keeper);
void
pop_cursors(uint count);
@@ -207,8 +208,8 @@ class sp_cursor : public Sql_alloc
{
public:
- sp_cursor(LEX *lex)
- : m_lex(lex), m_prot(NULL), m_isopen(0), m_current_row(NULL)
+ sp_cursor(sp_lex_keeper *lex_keeper)
+ : m_lex_keeper(lex_keeper), m_prot(NULL), m_isopen(0), m_current_row(NULL)
{
/* Empty */
}
@@ -220,7 +221,7 @@ public:
// We have split this in two to make it easy for sp_instr_copen
// to reuse the sp_instr::exec_stmt() code.
- LEX *
+ sp_lex_keeper *
pre_open(THD *thd);
void
post_open(THD *thd, my_bool was_opened);
@@ -240,7 +241,7 @@ public:
private:
MEM_ROOT m_mem_root; // My own mem_root
- LEX *m_lex;
+ sp_lex_keeper *m_lex_keeper;
Protocol_cursor *m_prot;
my_bool m_isopen;
my_bool m_nseof; // Original no_send_eof
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 87c83771ec8..041154c96cc 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -138,7 +138,6 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
TABLE_LIST tables[3];
TABLE *table;
READ_RECORD read_record_info;
- MYSQL_LOCK *lock;
my_bool return_val=1;
bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
char tmp_name[NAME_LEN+1];
@@ -176,20 +175,9 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ;
tables[0].db=tables[1].db=tables[2].db=thd->db;
- uint counter;
- if (open_tables(thd, tables, &counter))
- {
- sql_print_error("Fatal error: Can't open privilege tables: %s",
- thd->net.last_error);
- goto end;
- }
- TABLE *ptr[3]; // Lock tables for quick update
- ptr[0]= tables[0].table;
- ptr[1]= tables[1].table;
- ptr[2]= tables[2].table;
- if (!(lock=mysql_lock_tables(thd,ptr,3)))
+ if (simple_open_n_lock_tables(thd, tables))
{
- sql_print_error("Fatal error: Can't lock privilege tables: %s",
+ sql_print_error("Fatal error: Can't open and lock privilege tables: %s",
thd->net.last_error);
goto end;
}
@@ -459,7 +447,6 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
freeze_size(&acl_dbs);
init_check_host();
- mysql_unlock_tables(thd, lock);
initialized=1;
thd->version--; // Force close to free memory
return_val=0;
@@ -3112,7 +3099,6 @@ my_bool grant_init(THD *org_thd)
{
THD *thd;
TABLE_LIST tables[3];
- MYSQL_LOCK *lock;
MEM_ROOT *memex_ptr;
my_bool return_val= 1;
TABLE *t_table, *c_table, *p_table;
@@ -3146,15 +3132,7 @@ my_bool grant_init(THD *org_thd)
tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ;
tables[0].db=tables[1].db=tables[2].db=thd->db;
- uint counter;
- if (open_tables(thd, tables, &counter))
- goto end;
-
- TABLE *ptr[3]; // Lock tables for quick update
- ptr[0]= tables[0].table;
- ptr[1]= tables[1].table;
- ptr[2]= tables[2].table;
- if (!(lock=mysql_lock_tables(thd,ptr,3)))
+ if (simple_open_n_lock_tables(thd, tables))
goto end;
t_table = tables[0].table; c_table = tables[1].table;
@@ -3244,7 +3222,6 @@ my_bool grant_init(THD *org_thd)
end_unlock:
t_table->file->ha_index_end();
p_table->file->ha_index_end();
- mysql_unlock_tables(thd, lock);
thd->version--; // Force close to free memory
end:
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 530ef567aaa..4fd943c08f4 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -20,6 +20,7 @@
#include "mysql_priv.h"
#include "sql_select.h"
#include "sp_head.h"
+#include "sp.h"
#include "sql_trigger.h"
#include <m_ctype.h>
#include <my_dir.h>
@@ -359,7 +360,30 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh,
/*
- Close all tables used by thread
+ Mark all tables in the list which were used by current substatement
+ as free for reuse.
+
+ SYNOPSIS
+ mark_used_tables_as_free_for_reuse()
+ thd - thread context
+ table - head of the list of tables
+
+ DESCRIPTION
+ Marks all tables in the list which were used by current substatement
+ (they are marked by its query_id) as free for reuse.
+*/
+
+static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table)
+{
+ for (; table ; table= table->next)
+ if (table->query_id == thd->query_id)
+ table->query_id= 0;
+}
+
+
+/*
+ Close all tables used by the current substatement, or all tables
+ used by this thread if we are on the upper level.
SYNOPSIS
close_thread_tables()
@@ -372,14 +396,31 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh,
IMPLEMENTATION
Unlocks tables and frees derived tables.
Put all normal tables used by thread in free list.
+
+ When in prelocked mode it will only close/mark as free for reuse
+ tables opened by this substatement, it will also check if we are
+ closing tables after execution of complete query (i.e. we are on
+ upper level) and will leave prelocked mode if needed.
*/
void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived,
TABLE *stopper)
{
bool found_old_table;
+ prelocked_mode_type prelocked_mode= thd->prelocked_mode;
DBUG_ENTER("close_thread_tables");
+ /*
+ We are assuming here that thd->derived_tables contains ONLY derived
+ tables for this substatement. i.e. instead of approach which uses
+ query_id matching for determining which of the derived tables belong
+ to this substatement we rely on the ability of substatements to
+ save/restore thd->derived_tables during their execution.
+
+ TODO: Probably even better approach is to simply associate list of
+ derived tables with (sub-)statement instead of thread and destroy
+ them at the end of its execution.
+ */
if (thd->derived_tables && !skip_derived)
{
TABLE *table, *next;
@@ -394,10 +435,50 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived,
}
thd->derived_tables= 0;
}
- if (thd->locked_tables)
+
+ if (prelocked_mode)
{
- ha_commit_stmt(thd); // If select statement
- DBUG_VOID_RETURN; // LOCK TABLES in use
+ /*
+ Mark all temporary tables used by this substatement as free for reuse.
+ */
+ mark_used_tables_as_free_for_reuse(thd, thd->temporary_tables);
+ }
+
+ if (thd->locked_tables || prelocked_mode)
+ {
+ /*
+ TODO: It is not 100% clear whenever we should do ha_commit_stmt() for
+ sub-statements. This issue needs additional investigation.
+ */
+ ha_commit_stmt(thd);
+
+ /* We are under simple LOCK TABLES so should not do anything else. */
+ if (!prelocked_mode)
+ DBUG_VOID_RETURN;
+
+ if (!thd->lex->requires_prelocking())
+ {
+ /*
+ If we are executing one of substatements we have to mark
+ all tables which it used as free for reuse.
+ */
+ mark_used_tables_as_free_for_reuse(thd, thd->open_tables);
+ DBUG_VOID_RETURN;
+ }
+
+ DBUG_ASSERT(prelocked_mode);
+ /*
+ We are in prelocked mode, so we have to leave it now with doing
+ implicit UNLOCK TABLES if need.
+ */
+ thd->prelocked_mode= NON_PRELOCKED;
+
+ if (prelocked_mode == PRELOCKED_UNDER_LOCK_TABLES)
+ DBUG_VOID_RETURN;
+
+ thd->lock= thd->locked_tables;
+ thd->locked_tables= 0;
+ /* Fallthrough */
}
if (thd->lock)
@@ -441,6 +522,17 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived,
if (!lock_in_use)
VOID(pthread_mutex_unlock(&LOCK_open));
/* VOID(pthread_sigmask(SIG_SETMASK,&thd->signals,NULL)); */
+
+ if (prelocked_mode == PRELOCKED)
+ {
+ /*
+ If we are here then we are leaving normal prelocked mode, so it is
+ good idea to turn off OPTION_TABLE_LOCK flag.
+ */
+ DBUG_ASSERT(thd->lex->requires_prelocking());
+ thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
+ }
+
DBUG_VOID_RETURN;
}
@@ -910,7 +1002,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
!memcmp(table->s->table_cache_key, key,
key_length + TMP_TABLE_KEY_EXTRA))
{
- if (table->query_id == thd->query_id)
+ if (table->query_id == thd->query_id ||
+ thd->prelocked_mode && table->query_id)
{
my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias);
DBUG_RETURN(0);
@@ -924,16 +1017,17 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
}
}
- if (thd->locked_tables)
+ if (thd->locked_tables || thd->prelocked_mode)
{ // Using table locks
for (table=thd->open_tables; table ; table=table->next)
{
if (table->s->key_length == key_length &&
- !memcmp(table->s->table_cache_key,key,key_length) &&
- !my_strcasecmp(system_charset_info, table->alias, alias))
+ !memcmp(table->s->table_cache_key, key, key_length) &&
+ !my_strcasecmp(system_charset_info, table->alias, alias) &&
+ table->query_id != thd->query_id && /* skip tables already used by this query */
+ !(thd->prelocked_mode && table->query_id))
{
- if (table->query_id != thd->query_id)
- table->query_id=thd->query_id;
+ table->query_id= thd->query_id;
DBUG_PRINT("info",("Using locked table"));
goto reset;
}
@@ -1625,21 +1719,34 @@ err:
SYNOPSIS
open_tables()
thd - thread handler
- start - list of tables
+ start - list of tables in/out
counter - number of opened tables will be return using this parameter
+ NOTE
+ Unless we are already in prelocked mode, this function will also precache
+ all SP/SFs explicitly or implicitly (via views and triggers) used by the
+ query and add tables needed for their execution to table list. If resulting
+ tables list will be non empty it will mark query as requiring precaching.
+ Prelocked mode will be enabled for such query during lock_tables() call.
+
+ If query for which we are opening tables is already marked as requiring
+ prelocking it won't do such precaching and will simply reuse table list
+ which is already built.
+
RETURN
0 - OK
-1 - error
*/
-int open_tables(THD *thd, TABLE_LIST *start, uint *counter)
+int open_tables(THD *thd, TABLE_LIST **start, uint *counter)
{
TABLE_LIST *tables;
bool refresh;
int result=0;
- DBUG_ENTER("open_tables");
MEM_ROOT new_frm_mem;
+ /* Also used for indicating that prelocking is need */
+ TABLE_LIST **query_tables_last_own;
+ DBUG_ENTER("open_tables");
/*
temporary mem_root for new .frm parsing.
TODO: variables for size
@@ -1649,8 +1756,51 @@ int open_tables(THD *thd, TABLE_LIST *start, uint *counter)
thd->current_tablenr= 0;
restart:
*counter= 0;
+ query_tables_last_own= 0;
thd->proc_info="Opening tables";
- for (tables= start; tables ;tables= tables->next_global)
+
+ /*
+ If we are not already executing prelocked statement and don't have
+ statement for which table list for prelocking is already built, let
+ us cache routines and try to build such table list.
+
+ NOTE: If we want queries with functions to work under explicit
+ LOCK TABLES we have to additionaly lock mysql.proc table in it.
+ At least until Monty will fix SP loading :)
+
+ NOTE: We can't delay prelocking until we will met some sub-statement
+ which really uses tables, since this will imply that we have to restore
+ its table list to be able execute it in some other context.
+ And current views implementation assumes that view tables are added to
+ global table list only once during PS preparing/first SP execution.
+ Also locking at earlier stage is probably faster altough may decrease
+ concurrency a bit.
+
+ NOTE: We will mark statement as requiring prelocking only if we will
+ have non empty table list. But this does not guarantee that in prelocked
+ mode we will have some locked tables, because queries which use only
+ derived/information schema tables and views possible. Thus "counter"
+ may be still zero for prelocked statement...
+ */
+ if (!thd->prelocked_mode && !thd->lex->requires_prelocking() &&
+ (thd->lex->spfuns.records || thd->lex->spprocs.records))
+ {
+ TABLE_LIST **save_query_tables_last;
+
+ sp_cache_routines(thd, thd->lex);
+ save_query_tables_last= thd->lex->query_tables_last;
+
+ DBUG_ASSERT(thd->lex->query_tables == *start);
+
+ if (sp_add_sp_tables_to_table_list(thd, thd->lex, thd->lex) ||
+ *start)
+ {
+ query_tables_last_own= save_query_tables_last;
+ *start= thd->lex->query_tables;
+ }
+ }
+
+ for (tables= *start; tables ;tables= tables->next_global)
{
/*
Ignore placeholders for derived tables. After derived tables
@@ -1671,8 +1821,27 @@ int open_tables(THD *thd, TABLE_LIST *start, uint *counter)
free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC));
if (tables->view)
{
+ /* VIEW placeholder */
(*counter)--;
- continue; //VIEW placeholder
+ /*
+ Again if needed we have to get cache all routines used by this view
+ and add tables used by them to table list.
+ */
+ if (!thd->prelocked_mode && !thd->lex->requires_prelocking() &&
+ (tables->view->spfuns.records || tables->view->spprocs.records))
+ {
+ // FIXME We should catch recursion for both views and funcs here
+ sp_cache_routines(thd, tables->view);
+
+ /* We have at least one table in TL here */
+ if (!query_tables_last_own)
+ query_tables_last_own= thd->lex->query_tables_last;
+ sp_add_sp_tables_to_table_list(thd, thd->lex, tables->view);
+ }
+ /* Cleanup hashes because destructo for this LEX is never called */
+ hash_free(&tables->view->spfuns);
+ hash_free(&tables->view->spprocs);
+ continue;
}
if (refresh) // Refresh in progress
@@ -1684,7 +1853,12 @@ int open_tables(THD *thd, TABLE_LIST *start, uint *counter)
thd->version=refresh_version;
TABLE **prev_table= &thd->open_tables;
bool found=0;
- for (TABLE_LIST *tmp= start; tmp; tmp= tmp->next_global)
+ /*
+ QQ: What we should do if we have started building of table list
+ for prelocking ??? Probably throw it away ? But before we should
+ mark all temporary tables as free? How about locked ?
+ */
+ for (TABLE_LIST *tmp= *start; tmp; tmp= tmp->next_global)
{
/* Close normal (not temporary) changed tables */
if (tmp->table && ! tmp->table->s->tmp_table)
@@ -1713,7 +1887,27 @@ int open_tables(THD *thd, TABLE_LIST *start, uint *counter)
break;
}
else
+ {
+ /*
+ If we are not already in prelocked mode and extended table list is not
+ yet built and we have trigger for table being opened then we should
+ cache all routines used by its triggers and add their tables to
+ prelocking list.
+ If we lock table for reading we won't update it so there is no need to
+ process its triggers since they never will be activated.
+
+ FIXME Now we are simply turning on prelocking. Proper integration
+ and testing is to be done later.
+ */
+ if (!thd->prelocked_mode && !thd->lex->requires_prelocking() &&
+ tables->table->triggers &&
+ tables->lock_type >= TL_WRITE_ALLOW_WRITE)
+ {
+ if (!query_tables_last_own)
+ query_tables_last_own= thd->lex->query_tables_last;
+ }
free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC));
+ }
if (tables->lock_type != TL_UNLOCK && ! thd->locked_tables)
tables->table->reginfo.lock_type=tables->lock_type;
@@ -1721,6 +1915,10 @@ int open_tables(THD *thd, TABLE_LIST *start, uint *counter)
}
thd->proc_info=0;
free_root(&new_frm_mem, MYF(0)); // Free pre-alloced block
+
+ if (query_tables_last_own)
+ thd->lex->mark_as_requiring_prelocking(query_tables_last_own);
+
DBUG_RETURN(result);
}
@@ -1769,6 +1967,11 @@ static bool check_lock_and_start_stmt(THD *thd, TABLE *table,
table_list Table to open is first table in this list
lock_type Lock to use for open
+ NOTE
+ This function don't do anything like SP/SF/views/triggers analysis done
+ in open_tables(). It is intended for opening of only one concrete table.
+ And used only in special contexts.
+
RETURN VALUES
table Opened table
0 Error
@@ -1843,7 +2046,7 @@ int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables)
{
DBUG_ENTER("simple_open_n_lock_tables");
uint counter;
- if (open_tables(thd, tables, &counter) || lock_tables(thd, tables, counter))
+ if (open_tables(thd, &tables, &counter) || lock_tables(thd, tables, counter))
DBUG_RETURN(-1); /* purecov: inspected */
DBUG_RETURN(0);
}
@@ -1870,7 +2073,7 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables)
{
uint counter;
DBUG_ENTER("open_and_lock_tables");
- if (open_tables(thd, tables, &counter) ||
+ if (open_tables(thd, &tables, &counter) ||
lock_tables(thd, tables, counter) ||
mysql_handle_derived(thd->lex, &mysql_derived_prepare) ||
(thd->fill_derived_tables() &&
@@ -1903,7 +2106,7 @@ bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables)
uint counter;
DBUG_ENTER("open_normal_and_derived_tables");
DBUG_ASSERT(!thd->fill_derived_tables());
- if (open_tables(thd, tables, &counter) ||
+ if (open_tables(thd, &tables, &counter) ||
mysql_handle_derived(thd->lex, &mysql_derived_prepare))
DBUG_RETURN(TRUE); /* purecov: inspected */
relink_tables_for_multidelete(thd); // Not really needed, but
@@ -1937,6 +2140,27 @@ static void relink_tables_for_multidelete(THD *thd)
/*
+ Mark all real tables in the list as free for reuse.
+
+ SYNOPSIS
+ mark_real_tables_as_free_for_reuse()
+ thd - thread context
+ table - head of the list of tables
+
+ DESCRIPTION
+ Marks all real tables in the list (i.e. not views, derived
+ or schema tables) as free for reuse.
+*/
+
+static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table)
+{
+ for (; table; table= table->next_global)
+ if (!table->placeholder() && !table->schema_table)
+ table->table->query_id= 0;
+}
+
+
+/*
Lock all tables in list
SYNOPSIS
@@ -1950,6 +2174,10 @@ static void relink_tables_for_multidelete(THD *thd)
handling thr_lock gives us. You most always get all needed locks at
once.
+ If query for which we are calling this function marked as requring
+ prelocking, this function will do implicit LOCK TABLES and change
+ thd::prelocked_mode accordingly.
+
RETURN VALUES
0 ok
-1 Error
@@ -1958,36 +2186,125 @@ static void relink_tables_for_multidelete(THD *thd)
int lock_tables(THD *thd, TABLE_LIST *tables, uint count)
{
TABLE_LIST *table;
+
+ DBUG_ENTER("lock_tables");
+ /*
+ We can't meet statement requiring prelocking if we already
+ in prelocked mode.
+ */
+ DBUG_ASSERT(!thd->prelocked_mode || !thd->lex->requires_prelocking());
+ /*
+ If statement requires prelocking then it has non-empty table list.
+ So it is safe to shortcut.
+ */
+ DBUG_ASSERT(!thd->lex->requires_prelocking() || tables);
+
if (!tables)
- return 0;
+ DBUG_RETURN(0);
- if (!thd->locked_tables)
+ /*
+ We need this extra check for thd->prelocked_mode because we want to avoid
+ attempts to lock tables in substatements. Checking for thd->locked_tables
+ is not enough in some situations. For example for SP containing
+ "drop table t3; create temporary t3 ..; insert into t3 ...;"
+ thd->locked_tables may be 0 after drop tables, and without this extra
+ check insert will try to lock temporary table t3, that will lead
+ to memory leak...
+ */
+ if (!thd->locked_tables && !thd->prelocked_mode)
{
DBUG_ASSERT(thd->lock == 0); // You must lock everything at once
TABLE **start,**ptr;
+
if (!(ptr=start=(TABLE**) thd->alloc(sizeof(TABLE*)*count)))
- return -1;
+ DBUG_RETURN(-1);
for (table= tables; table; table= table->next_global)
{
if (!table->placeholder() && !table->schema_table)
*(ptr++)= table->table;
}
+
+ /* We have to emulate LOCK TABLES if we are statement needs prelocking. */
+ if (thd->lex->requires_prelocking())
+ {
+ thd->in_lock_tables=1;
+ thd->options|= OPTION_TABLE_LOCK;
+ }
+
if (!(thd->lock=mysql_lock_tables(thd,start, (uint) (ptr - start))))
- return -1; /* purecov: inspected */
+ {
+ if (thd->lex->requires_prelocking())
+ {
+ thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
+ thd->in_lock_tables=0;
+ }
+ DBUG_RETURN(-1);
+ }
+ if (thd->lex->requires_prelocking() &&
+ thd->lex->sql_command != SQLCOM_LOCK_TABLES)
+ {
+ TABLE_LIST *first_not_own= thd->lex->first_not_own_table();
+ /*
+ We just have done implicit LOCK TABLES, and now we have
+ to emulate first open_and_lock_tables() after it.
+
+ Note that "LOCK TABLES" can also be marked as requiring prelocking
+ (e.g. if one locks view which uses functions). We should not emulate
+ such open_and_lock_tables() in this case. We also should not set
+ THD::prelocked_mode or first close_thread_tables() call will do
+ "UNLOCK TABLES".
+ */
+ thd->locked_tables= thd->lock;
+ thd->lock= 0;
+ thd->in_lock_tables=0;
+
+ for (table= tables; table != first_not_own; table= table->next_global)
+ {
+ if (!table->placeholder() && !table->schema_table)
+ {
+ table->table->query_id= thd->query_id;
+ if (check_lock_and_start_stmt(thd, table->table, table->lock_type))
+ {
+ ha_rollback_stmt(thd);
+ mysql_unlock_tables(thd, thd->locked_tables);
+ thd->locked_tables= 0;
+ thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
+ DBUG_RETURN(-1);
+ }
+ }
+ }
+ /*
+ Let us mark all tables which don't belong to the statement itself,
+ and was marked as occupied during open_tables() as free for reuse.
+ */
+ mark_real_tables_as_free_for_reuse(first_not_own);
+ thd->prelocked_mode= PRELOCKED;
+ }
}
else
{
- for (table= tables; table; table= table->next_global)
+ TABLE_LIST *first_not_own= thd->lex->first_not_own_table();
+ for (table= tables; table != first_not_own; table= table->next_global)
{
- if (!table->placeholder() &&
+ if (!table->placeholder() && !table->schema_table &&
check_lock_and_start_stmt(thd, table->table, table->lock_type))
{
ha_rollback_stmt(thd);
- return -1;
+ DBUG_RETURN(-1);
}
}
+ /*
+ If we are under explicit LOCK TABLES and our statement requires
+ prelocking, we should mark all "additional" tables as free for use
+ and enter prelocked mode.
+ */
+ if (thd->lex->requires_prelocking())
+ {
+ mark_real_tables_as_free_for_reuse(first_not_own);
+ thd->prelocked_mode= PRELOCKED_UNDER_LOCK_TABLES;
+ }
}
- return 0;
+ DBUG_RETURN(0);
}
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 527f349038c..bac3e42ed62 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -251,8 +251,9 @@ THD::THD()
protocol_prep.init(this);
tablespace_op=FALSE;
- ulong tmp=sql_rnd_with_mutex();
- randominit(&rand, tmp + (ulong) &rand, tmp + (ulong) ::query_id);
+ ulong tmp=sql_rnd_with_mutex();
+ randominit(&rand, tmp + (ulong) &rand, tmp + (ulong) ::query_id);
+ prelocked_mode= NON_PRELOCKED;
}
diff --git a/sql/sql_class.h b/sql/sql_class.h
index e88205568c2..fa2bcc4d478 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -926,6 +926,15 @@ typedef I_List<Item_change_record> Item_change_list;
/*
+ Type of prelocked mode.
+ See comment for THD::prelocked_mode for complete description.
+*/
+
+enum prelocked_mode_type {NON_PRELOCKED= 0, PRELOCKED= 1,
+ PRELOCKED_UNDER_LOCK_TABLES= 2};
+
+
+/*
For each client connection we create a separate thread with THD serving as
a thread/connection descriptor
*/
@@ -1025,7 +1034,13 @@ public:
See also lock_tables() for details.
*/
MYSQL_LOCK *lock; /* Current locks */
- MYSQL_LOCK *locked_tables; /* Tables locked with LOCK */
+ /*
+ Tables that were locked with explicit or implicit LOCK TABLES.
+ (Implicit LOCK TABLES happens when we are prelocking tables for
+ execution of statement which uses stored routines. See description
+ THD::prelocked_mode for more info.)
+ */
+ MYSQL_LOCK *locked_tables;
HASH handler_tables_hash;
/*
One thread can hold up to one named user-level lock. This variable
@@ -1192,8 +1207,6 @@ public:
sp_rcontext *spcont; // SP runtime context
sp_cache *sp_proc_cache;
sp_cache *sp_func_cache;
- bool shortcut_make_view; /* Don't do full mysql_make_view()
- during pre-opening of tables. */
/*
If we do a purge of binary logs, log index info of the threads
@@ -1209,6 +1222,31 @@ public:
long long_value;
} sys_var_tmp;
+ /*
+ prelocked_mode_type enum and prelocked_mode member are used for
+ indicating whenever "prelocked mode" is on, and what type of
+ "prelocked mode" is it.
+
+ Prelocked mode is used for execution of queries which explicitly
+ or implicitly (via views or triggers) use functions, thus may need
+ some additional tables (mentioned in query table list) for their
+ execution.
+
+ First open_tables() call for such query will analyse all functions
+ used by it and add all additional tables to table its list. It will
+ also mark this query as requiring prelocking. After that lock_tables()
+ will issue implicit LOCK TABLES for the whole table list and change
+ thd::prelocked_mode to non-0. All queries called in functions invoked
+ by the main query will use prelocked tables. Non-0 prelocked_mode
+ will also surpress mentioned analysys in those queries thus saving
+ cycles. Prelocked mode will be turned off once close_thread_tables()
+ for the main query will be called.
+
+ Note: Since not all "tables" present in table list are really locked
+ thd::relocked_mode does not imply thd::locked_tables.
+ */
+ prelocked_mode_type prelocked_mode;
+
THD();
~THD();
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index dd2ac3c013b..bb48b7ada77 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -187,7 +187,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
/* for now HANDLER can be used only for real TABLES */
tables->required_type= FRMTYPE_TABLE;
- error= open_tables(thd, tables, &counter);
+ error= open_tables(thd, &tables, &counter);
HANDLER_TABLES_HACK(thd);
if (error)
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 738a6e0dbbd..91c4dc40c01 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -169,13 +169,12 @@ void lex_start(THD *thd, uchar *buf,uint length)
lex->sphead= NULL;
lex->spcont= NULL;
lex->proc_list.first= 0;
+ lex->query_tables_own_last= 0;
if (lex->spfuns.records)
my_hash_reset(&lex->spfuns);
if (lex->spprocs.records)
my_hash_reset(&lex->spprocs);
- if (lex->sptabs.records)
- my_hash_reset(&lex->sptabs);
DBUG_VOID_RETURN;
}
@@ -1868,6 +1867,8 @@ TABLE_LIST *st_lex::unlink_first_table(bool *link_to_local)
*/
if ((query_tables= query_tables->next_global))
query_tables->prev_global= &query_tables;
+ else
+ query_tables_last= &query_tables;
first->next_global= 0;
/*
@@ -1973,6 +1974,8 @@ void st_lex::link_first_table_back(TABLE_LIST *first,
{
if ((first->next_global= query_tables))
query_tables->prev_global= &first->next_global;
+ else
+ query_tables_last= &first->next_global;
query_tables= first;
if (link_to_local)
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index a0145dcaccf..e50c4edabb4 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -784,7 +784,6 @@ typedef struct st_lex
sp_pcontext *spcont;
HASH spfuns; /* Called functions */
HASH spprocs; /* Called procedures */
- HASH sptabs; /* Merged table lists */
st_sp_chistics sp_chistics;
bool only_view; /* used for SHOW CREATE TABLE/VIEW */
/*
@@ -803,23 +802,25 @@ typedef struct st_lex
*/
SQL_LIST trg_table_fields;
- st_lex() :result(0), sql_command(SQLCOM_END)
+ /*
+ If non-0 then indicates that query requires prelocking and points to
+ next_global member of last own element in query table list (i.e. last
+ table which was not added to it as part of preparation to prelocking).
+ 0 - indicates that this query does not need prelocking.
+ */
+ TABLE_LIST **query_tables_own_last;
+
+ st_lex() :result(0), sql_command(SQLCOM_END), query_tables_own_last(0)
{
extern byte *sp_lex_sp_key(const byte *ptr, uint *plen, my_bool first);
- extern byte *sp_table_key(const byte *ptr, uint *plen, my_bool first);
hash_init(&spfuns, system_charset_info, 0, 0, 0, sp_lex_sp_key, 0, 0);
hash_init(&spprocs, system_charset_info, 0, 0, 0, sp_lex_sp_key, 0, 0);
- hash_init(&sptabs, system_charset_info, 0, 0, 0, sp_table_key, 0, 0);
}
~st_lex()
{
- if (spfuns.array.buffer)
- hash_free(&spfuns);
- if (spprocs.array.buffer)
- hash_free(&spprocs);
- if (sptabs.array.buffer)
- hash_free(&sptabs);
+ hash_free(&spfuns);
+ hash_free(&spprocs);
}
inline void uncacheable(uint8 cause)
@@ -856,6 +857,21 @@ typedef struct st_lex
bool can_not_use_merged();
bool only_view_structure();
bool need_correct_ident();
+
+ inline bool requires_prelocking()
+ {
+ return query_tables_own_last;
+ }
+ inline void mark_as_requiring_prelocking(TABLE_LIST **tables_own_last)
+ {
+ query_tables_own_last= tables_own_last;
+ }
+ /* Return pointer to first not-own table in query-tables or 0 */
+ TABLE_LIST* first_not_own_table()
+ {
+ return ( query_tables_own_last ? *query_tables_own_last : 0);
+ }
+
} LEX;
struct st_lex_local: public st_lex
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 2f38f96c976..e5c29f56305 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -1453,105 +1453,6 @@ bool do_command(THD *thd)
}
#endif /* EMBEDDED_LIBRARY */
-static void release_local_lock(THD *thd, TABLE_LIST *locked_tables,
- bool old_innodb_table_locks)
-{
- if (locked_tables)
- {
-#ifdef HAVE_INNOBASE_DB
- thd->variables.innodb_table_locks= old_innodb_table_locks;
-#endif
- if (thd->locked_tables)
- sp_unlock_tables(thd);
- }
-
-}
-
-static bool process_nested_sp(THD *thd, LEX *lex, TABLE_LIST** locked_tables)
-{
- DBUG_ENTER("process_nested_sp");
- while (1)
- {
- if (sp_cache_routines(thd, lex, TYPE_ENUM_FUNCTION))
- DBUG_RETURN(TRUE);
- if (sp_cache_routines(thd, lex, TYPE_ENUM_PROCEDURE))
- DBUG_RETURN(TRUE);
- if (!thd->locked_tables &&
- lex->sql_command != SQLCOM_CREATE_TABLE &&
- lex->sql_command != SQLCOM_CREATE_VIEW)
- {
- MEM_ROOT *thdmemroot= NULL;
-
- sp_merge_routine_tables(thd, lex);
- // QQ Preopen tables to find views and triggers.
- // This means we open, close and open again, which sucks, but
- // right now it's the easiest way to get it to work. A better
- // solution will hopefully be found soon...
- if (lex->sptabs.records || lex->query_tables)
- {
- uint procs, funs, tabs;
-
- if (thd->mem_root != thd->current_arena->mem_root)
- {
- thdmemroot= thd->mem_root;
- thd->mem_root= thd->current_arena->mem_root;
- }
- if (!sp_merge_table_list(thd, &lex->sptabs, lex->query_tables))
- DBUG_RETURN(TRUE);
- procs= lex->spprocs.records;
- funs= lex->spfuns.records;
- tabs= lex->sptabs.records;
-
- if (((*locked_tables)= sp_hash_to_table_list(thd, &lex->sptabs)))
- {
- // We don't want these updated now
- uint ctmpdtabs= thd->status_var.created_tmp_disk_tables;
- uint ctmptabs= thd->status_var.created_tmp_tables;
- uint count;
-
- thd->shortcut_make_view= TRUE;
- open_tables(thd, *locked_tables, &count);
- thd->shortcut_make_view= FALSE;
- close_thread_tables(thd);
- thd->status_var.created_tmp_disk_tables= ctmpdtabs;
- thd->status_var.created_tmp_tables= ctmptabs;
- thd->clear_error();
- mysql_reset_errors(thd);
- (*locked_tables)= NULL;
- }
- // A kludge: Decrease all temp. table's query ids to allow a
- // second opening.
- for (TABLE *table= thd->temporary_tables; table ; table=table->next)
- table->query_id-= 1;
- if (procs < lex->spprocs.records ||
- funs < lex->spfuns.records ||
- tabs < lex->sptabs.records)
- {
- if (thdmemroot)
- thd->mem_root= thdmemroot;
- continue; // Found more SPs or tabs, try again
- }
- }
- if (lex->sptabs.records &&
- (lex->spfuns.records || lex->spprocs.records) &&
- sp_merge_table_list(thd, &lex->sptabs, lex->query_tables))
- {
- if (((*locked_tables)= sp_hash_to_table_list(thd, &lex->sptabs)))
- {
-#ifdef HAVE_INNOBASE_DB
- thd->variables.innodb_table_locks= FALSE;
-#endif
- sp_open_and_lock_tables(thd, *locked_tables);
- }
- }
- if (thdmemroot)
- thd->mem_root= thdmemroot;
- }
- break;
- } // while (1)
- DBUG_RETURN(FALSE);
-}
-
/*
Perform one connection-level (COM_XXXX) command.
@@ -1752,7 +1653,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
in embedded server - just store them to be executed later
*/
#ifndef EMBEDDED_LIBRARY
- if (thd->lock || thd->open_tables || thd->derived_tables)
+ if (thd->lock || thd->open_tables || thd->derived_tables ||
+ thd->prelocked_mode)
close_thread_tables(thd);
#endif
ulong length= (ulong)(packet_end-packet);
@@ -1855,19 +1757,12 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
select_lex.table_list.link_in_list((byte*) &table_list,
(byte**) &table_list.next_local);
thd->lex->query_tables= &table_list;
- thd->shortcut_make_view= 0;
- process_nested_sp(thd, thd->lex, &locked_tables);
/* switch on VIEW optimisation: do not fill temporary tables */
thd->lex->sql_command= SQLCOM_SHOW_FIELDS;
mysqld_list_fields(thd,&table_list,fields);
thd->lex->unit.cleanup();
thd->cleanup_after_query();
-#ifdef HAVE_INNOBASE_DB
- release_local_lock(thd, locked_tables, old_innodb_table_locks);
-#else
- release_local_lock(thd, locked_tables, false);
-#endif
break;
}
#endif
@@ -2089,7 +1984,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
break;
}
- if (thd->lock || thd->open_tables || thd->derived_tables)
+ if (thd->lock || thd->open_tables || thd->derived_tables ||
+ thd->prelocked_mode)
{
thd->proc_info="closing tables";
close_thread_tables(thd); /* Free tables */
@@ -2334,11 +2230,7 @@ mysql_execute_command(THD *thd)
TABLE_LIST *all_tables;
/* most outer SELECT_LEX_UNIT of query */
SELECT_LEX_UNIT *unit= &lex->unit;
- /* Locked closure of all tables */
- TABLE_LIST *locked_tables= NULL;
/* Saved variable value */
- my_bool old_innodb_table_locks=
- IF_INNOBASE_DB(thd->variables.innodb_table_locks, FALSE);
DBUG_ENTER("mysql_execute_command");
thd->net.no_send_error= 0;
@@ -2361,26 +2253,14 @@ mysql_execute_command(THD *thd)
/* should be assigned after making first tables same */
all_tables= lex->query_tables;
- thd->shortcut_make_view= 0;
- if (lex->sql_command != SQLCOM_CREATE_PROCEDURE &&
- lex->sql_command != SQLCOM_CREATE_SPFUNCTION &&
- lex->sql_command != SQLCOM_LOCK_TABLES &&
- lex->sql_command != SQLCOM_UNLOCK_TABLES)
- {
- thd->no_warnings_for_error= 1;
- res= process_nested_sp(thd, lex, &locked_tables);
- thd->no_warnings_for_error= 0;
- if (res)
- DBUG_RETURN(TRUE);
- }
-
/*
Reset warning count for each query that uses tables
A better approach would be to reset this for any commands
that is not a SHOW command or a select that only access local
variables, but for now this is probably good enough.
*/
- if (all_tables || &lex->select_lex != lex->all_selects_list)
+ if (all_tables || &lex->select_lex != lex->all_selects_list ||
+ lex->spfuns.records || lex->spprocs.records)
mysql_reset_errors(thd);
#ifdef HAVE_REPLICATION
@@ -2614,9 +2494,8 @@ mysql_execute_command(THD *thd)
break;
}
case SQLCOM_DO:
- if (all_tables &&
- (check_table_access(thd, SELECT_ACL, all_tables, 0) ||
- open_and_lock_tables(thd, all_tables)))
+ if (check_table_access(thd, SELECT_ACL, all_tables, 0) ||
+ open_and_lock_tables(thd, all_tables))
goto error;
res= mysql_do(thd, *lex->insert_list);
@@ -3485,8 +3364,7 @@ unsent_create_error:
case SQLCOM_SET_OPTION:
{
List<set_var_base> *lex_var_list= &lex->var_list;
- if (all_tables &&
- (check_table_access(thd, SELECT_ACL, all_tables, 0) ||
+ if ((check_table_access(thd, SELECT_ACL, all_tables, 0) ||
open_and_lock_tables(thd, all_tables)))
goto error;
if (lex->one_shot_set && not_all_support_one_shot(lex_var_list))
@@ -3532,7 +3410,7 @@ unsent_create_error:
thd->in_lock_tables=1;
thd->options|= OPTION_TABLE_LOCK;
- if (!(res= open_and_lock_tables(thd, all_tables)))
+ if (!(res= simple_open_n_lock_tables(thd, all_tables)))
{
#ifdef HAVE_QUERY_CACHE
if (thd->variables.query_cache_wlock_invalidate)
@@ -4115,7 +3993,20 @@ unsent_create_error:
{
sp_head *sp;
- if (!(sp= sp_find_procedure(thd, lex->spname)))
+ /*
+ 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, 0) ||
+ open_and_lock_tables(thd, all_tables))
+ goto error;
+
+ /*
+ By this moment all needed SPs should be in cache so no need
+ to look into DB. Moreover we may be unable to do it becuase
+ we may don't have read lock on mysql.proc
+ */
+ if (!(sp= sp_find_procedure(thd, lex->spname, TRUE)))
{
my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PROCEDURE",
lex->spname->m_qname.str);
@@ -4130,12 +4021,6 @@ unsent_create_error:
/* bits that should be cleared in thd->server_status */
uint bits_to_be_cleared= 0;
- /* In case the arguments are subselects... */
- if (all_tables &&
- (check_table_access(thd, SELECT_ACL, all_tables, 0) ||
- open_and_lock_tables(thd, all_tables)))
- goto error;
-
#ifndef EMBEDDED_LIBRARY
my_bool nsok= thd->net.no_send_ok;
thd->net.no_send_ok= TRUE;
@@ -4606,11 +4491,6 @@ cleanup:
if (thd->lock == thd->locked_tables)
thd->lock= 0;
}
-#ifdef HAVE_INNOBASE_DB
- release_local_lock(thd, locked_tables, old_innodb_table_locks);
-#else
- release_local_lock(thd, locked_tables, false);
-#endif
DBUG_RETURN(res || thd->net.report_error);
}
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index d1594edc3f9..cf988654b22 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -1004,7 +1004,7 @@ static int mysql_test_update(Prepared_statement *stmt,
if (update_precheck(thd, table_list))
DBUG_RETURN(1);
- if (!open_tables(thd, table_list, &table_count))
+ if (!open_tables(thd, &table_list, &table_count))
{
if (table_list->ancestor && table_list->ancestor->next_local)
{
@@ -1545,22 +1545,6 @@ static int check_prepared_statement(Prepared_statement *stmt,
lex->first_lists_tables_same();
tables= lex->query_tables;
- /*
- Preopen 'proc' system table and cache all functions used in this
- statement. We must do that before we open ordinary tables to avoid
- deadlocks. We can't open and lock any table once query tables were
- opened.
- */
- if (lex->sql_command != SQLCOM_CREATE_PROCEDURE &&
- lex->sql_command != SQLCOM_CREATE_SPFUNCTION)
- {
- /* The error is printed inside */
- if (sp_cache_routines(thd, lex, TYPE_ENUM_FUNCTION))
- DBUG_RETURN(-1);
- if (sp_cache_routines(thd, lex, TYPE_ENUM_PROCEDURE))
- DBUG_RETURN(-1);
- }
-
switch (sql_command) {
case SQLCOM_REPLACE:
case SQLCOM_INSERT:
@@ -1793,9 +1777,9 @@ bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
thd->lex->sphead= NULL;
}
lex_end(lex);
+ close_thread_tables(thd);
thd->restore_backup_statement(stmt, &thd->stmt_backup);
cleanup_items(stmt->free_list);
- close_thread_tables(thd);
thd->rollback_item_tree_changes();
thd->cleanup_after_query();
thd->current_arena= thd;
@@ -1885,6 +1869,11 @@ void reset_stmt_for_execute(THD *thd, LEX *lex)
to indicate the table is altered, and re-do the setup_*
and open the tables back.
*/
+ /*
+ NOTE: We should reset whole table list here including all tables added
+ by prelocking algorithm (it is not a problem for substatements since
+ they have their own table list).
+ */
for (TABLE_LIST *tables= lex->query_tables;
tables;
tables= tables->next_global)
diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h
index 82e7c1ce023..7dd6734eb89 100644
--- a/sql/sql_trigger.h
+++ b/sql/sql_trigger.h
@@ -42,14 +42,21 @@ public:
if (bodies[event][time_type])
{
- /*
- Similar to function invocation we don't need to surpress sending of
- ok packets here because don't allow execute statements from trigger.
+#ifndef EMBEDDED_LIBRARY
+ /* Surpress OK packets in case if we will execute statements */
+ my_bool nsok= thd->net.no_send_ok;
+ thd->net.no_send_ok= TRUE;
+#endif
+ /*
FIXME: We should juggle with security context here (because trigger
should be invoked with creator rights).
*/
res= bodies[event][time_type]->execute_function(thd, 0, 0, 0);
+
+#ifndef EMBEDDED_LIBRARY
+ thd->net.no_send_ok= nsok;
+#endif
}
return res;
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 54a976fe2b0..477a283448a 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -136,7 +136,7 @@ int mysql_update(THD *thd,
LINT_INIT(timestamp_query_id);
- if (open_tables(thd, table_list, &table_count))
+ if (open_tables(thd, &table_list, &table_count))
DBUG_RETURN(1);
if (table_list->ancestor && table_list->ancestor->next_local)
@@ -635,7 +635,7 @@ bool mysql_multi_update_prepare(THD *thd)
thd->lex->sql_command= SQLCOM_UPDATE_MULTI;
/* open tables and create derived ones, but do not lock and fill them */
- if ((original_multiupdate && open_tables(thd, table_list, & table_count)) ||
+ if ((original_multiupdate && open_tables(thd, &table_list, & table_count)) ||
mysql_handle_derived(lex, &mysql_derived_prepare))
DBUG_RETURN(TRUE);
/*
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 96a0dc2f636..0c7bf2632ec 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -19,6 +19,7 @@
#include "sql_select.h"
#include "parse_file.h"
#include "sp.h"
+#include "sp_head.h"
#define MD5_BUFF_LENGTH 33
@@ -615,10 +616,7 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
table->view= lex= thd->lex= (LEX*) new(thd->mem_root) st_lex_local;
lex_start(thd, (uchar*)table->query.str, table->query.length);
view_select= &lex->select_lex;
- /* Only if we're not in the pre-open phase */
- if (!thd->shortcut_make_view)
- view_select->select_number= ++thd->select_number;
- old_lex->derived_tables|= DERIVED_VIEW;
+ view_select->select_number= ++thd->select_number;
{
ulong options= thd->options;
/* switch off modes which can prevent normal parsing of VIEW
@@ -662,35 +660,13 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
TABLE_LIST *view_tables_tail= 0;
TABLE_LIST *tbl;
- /* move SP to main LEX */
- if (lex->spfuns.records)
- sp_merge_hash(&old_lex->spfuns, &lex->spfuns);
-
- /* cleanup LEX */
- if (lex->spfuns.array.buffer)
- hash_free(&lex->spfuns);
- if (lex->spprocs.array.buffer)
- hash_free(&lex->spprocs);
- if (lex->sptabs.array.buffer)
- hash_free(&lex->sptabs);
-
- /* If we're pre-opening tables to find SPs and tables we need
- not go any further; doing so will cause an infinite loop. */
- if (thd->shortcut_make_view)
- {
- extern bool
- sp_merge_table_list(THD *thd, HASH *h, TABLE_LIST *table,
- LEX *lex_for_tmp_check = 0);
-
- sp_merge_table_list(thd, &old_lex->sptabs, view_tables);
- goto ok;
- }
-
/*
- check rights to run commands (EXPLAIN SELECT & SHOW CREATE) which show
- underlying tables
+ Check rights to run commands (EXPLAIN SELECT & SHOW CREATE) which show
+ underlying tables.
+ Skip this step if we are opening view for prelocking only.
*/
- if ((old_lex->sql_command == SQLCOM_SELECT && old_lex->describe))
+ if (!table->prelocking_placeholder &&
+ (old_lex->sql_command == SQLCOM_SELECT && old_lex->describe))
{
if (check_table_access(thd, SELECT_ACL, view_tables, 1) &&
check_table_access(thd, SHOW_VIEW_ACL, table, 1))
@@ -699,7 +675,8 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
goto err;
}
}
- else if (old_lex->sql_command == SQLCOM_SHOW_CREATE)
+ else if (!table->prelocking_placeholder &&
+ old_lex->sql_command == SQLCOM_SHOW_CREATE)
{
if (check_table_access(thd, SHOW_VIEW_ACL, table, 0))
goto err;
@@ -717,13 +694,6 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
tbl->belong_to_view= top_view;
}
- /* move SQL_NO_CACHE & Co to whole query */
- old_lex->safe_to_cache_query= (old_lex->safe_to_cache_query &&
- lex->safe_to_cache_query);
- /* move SQL_CACHE to whole query */
- if (view_select->options & OPTION_TO_QUERY_CACHE)
- old_lex->select_lex.options|= OPTION_TO_QUERY_CACHE;
-
/*
Put tables of VIEW after VIEW TABLE_LIST
@@ -740,13 +710,30 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
}
else
{
- lex->query_tables_last= &view_tables_tail->next_global;
+ old_lex->query_tables_last= &view_tables_tail->next_global;
}
view_tables->prev_global= &table->next_global;
table->next_global= view_tables;
}
/*
+ If we are opening this view as part of implicit LOCK TABLES, then
+ this view serves as simple placeholder and we should not continue
+ further processing.
+ */
+ if (table->prelocking_placeholder)
+ goto ok2;
+
+ old_lex->derived_tables|= DERIVED_VIEW;
+
+ /* move SQL_NO_CACHE & Co to whole query */
+ old_lex->safe_to_cache_query= (old_lex->safe_to_cache_query &&
+ lex->safe_to_cache_query);
+ /* move SQL_CACHE to whole query */
+ if (view_select->options & OPTION_TO_QUERY_CACHE)
+ old_lex->select_lex.options|= OPTION_TO_QUERY_CACHE;
+
+ /*
check MERGE algorithm ability
- algorithm is not explicit TEMPORARY TABLE
- VIEW SELECT allow merging
@@ -850,8 +837,6 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
goto err;
ok:
- if (arena)
- thd->restore_backup_item_arena(arena, &backup);
/* global SELECT list linking */
end= view_select; // primary SELECT_LEX is always last
end->link_next= old_lex->all_selects_list;
@@ -860,6 +845,9 @@ ok:
lex->all_selects_list->link_prev=
(st_select_lex_node**)&old_lex->all_selects_list;
+ok2:
+ if (arena)
+ thd->restore_backup_item_arena(arena, &backup);
thd->lex= old_lex;
DBUG_RETURN(0);
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index a69b6a96982..fae01502a8d 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -1548,12 +1548,12 @@ sp_opt_inout:
sp_proc_stmts:
/* Empty */ {}
- | sp_proc_stmts { Lex->query_tables= 0; } sp_proc_stmt ';'
+ | sp_proc_stmts sp_proc_stmt ';'
;
sp_proc_stmts1:
sp_proc_stmt ';' {}
- | sp_proc_stmts1 { Lex->query_tables= 0; } sp_proc_stmt ';'
+ | sp_proc_stmts1 sp_proc_stmt ';'
;
sp_decls:
@@ -1587,13 +1587,15 @@ sp_decls:
;
sp_decl:
- DECLARE_SYM sp_decl_idents type sp_opt_default
+ DECLARE_SYM sp_decl_idents type
+ { Lex->sphead->reset_lex(YYTHD); }
+ sp_opt_default
{
LEX *lex= Lex;
sp_pcontext *ctx= lex->spcont;
uint max= ctx->context_pvars();
enum enum_field_types type= (enum enum_field_types)$3;
- Item *it= $4;
+ Item *it= $5;
for (uint i = max-$2 ; i < max ; i++)
{
@@ -1605,15 +1607,19 @@ sp_decl:
sp_instr_set *in= new sp_instr_set(lex->sphead->instructions(),
ctx,
ctx->pvar_context2index(i),
- it, type);
+ it, type, lex,
+ (i == max - 1));
- in->tables= lex->query_tables;
- lex->query_tables= 0;
+ /*
+ The last instruction is assigned to be responsible for
+ freeing LEX.
+ */
lex->sphead->add_instr(in);
ctx->set_isset(i, TRUE);
ctx->set_default(i, it);
}
}
+ lex->sphead->restore_lex(YYTHD);
$$.vars= $2;
$$.conds= $$.hndlrs= $$.curs= 0;
}
@@ -1865,36 +1871,39 @@ sp_proc_stmt:
my_message(ER_SP_NO_USE, ER(ER_SP_NO_USE), MYF(0));
YYABORT;
}
- /* Don't add an instruction for empty SET statements.
- ** (This happens if the SET only contained local variables,
- ** which get their set instructions generated separately.)
+ /*
+ Don't add an instruction for SET statements, since all
+ instructions for them were already added during processing
+ of "set" rule.
*/
- if (lex->sql_command != SQLCOM_SET_OPTION ||
- ! lex->var_list.is_empty())
+ DBUG_ASSERT(lex->sql_command != SQLCOM_SET_OPTION ||
+ lex->var_list.is_empty());
+ if (lex->sql_command != SQLCOM_SET_OPTION)
{
- sp_instr_stmt *i=new sp_instr_stmt(sp->instructions(),
- lex->spcont);
-
- /* Extract the query statement from the tokenizer:
- The end is either lex->tok_end or tok->ptr. */
- if (lex->ptr - lex->tok_end > 1)
- i->m_query.length= lex->ptr - sp->m_tmp_query;
- else
- i->m_query.length= lex->tok_end - sp->m_tmp_query;
- i->m_query.str= strmake_root(YYTHD->mem_root,
- (char *)sp->m_tmp_query,
- i->m_query.length);
- i->set_lex(lex);
- sp->add_instr(i);
- lex->sp_lex_in_use= TRUE;
- }
+ sp_instr_stmt *i=new sp_instr_stmt(sp->instructions(),
+ lex->spcont, lex);
+
+ /* Extract the query statement from the tokenizer:
+ The end is either lex->tok_end or tok->ptr. */
+ if (lex->ptr - lex->tok_end > 1)
+ i->m_query.length= lex->ptr - sp->m_tmp_query;
+ else
+ i->m_query.length= lex->tok_end - sp->m_tmp_query;
+ i->m_query.str= strmake_root(YYTHD->mem_root,
+ (char *)sp->m_tmp_query,
+ i->m_query.length);
+ sp->add_instr(i);
+ }
sp->restore_lex(YYTHD);
}
- | RETURN_SYM expr
+ | RETURN_SYM
+ { Lex->sphead->reset_lex(YYTHD); }
+ expr
{
LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
- if (lex->sphead->m_type == TYPE_ENUM_PROCEDURE)
+ if (sp->m_type == TYPE_ENUM_PROCEDURE)
{
my_message(ER_SP_BADRETURN, ER(ER_SP_BADRETURN), MYF(0));
YYABORT;
@@ -1903,12 +1912,12 @@ sp_proc_stmt:
{
sp_instr_freturn *i;
- i= new sp_instr_freturn(lex->sphead->instructions(),
- lex->spcont,
- $2, lex->sphead->m_returns);
- lex->sphead->add_instr(i);
- lex->sphead->m_has_return= TRUE;
+ i= new sp_instr_freturn(sp->instructions(), lex->spcont,
+ $3, sp->m_returns, lex);
+ sp->add_instr(i);
+ sp->m_has_return= TRUE;
}
+ sp->restore_lex(YYTHD);
}
| IF sp_if END IF {}
| CASE_SYM WHEN_SYM
@@ -1916,7 +1925,9 @@ sp_proc_stmt:
Lex->sphead->m_simple_case= FALSE;
}
sp_case END CASE_SYM {}
- | CASE_SYM expr WHEN_SYM
+ | CASE_SYM
+ { Lex->sphead->reset_lex(YYTHD); }
+ expr WHEN_SYM
{
/* We "fake" this by using an anonymous variable which we
set to the expression. Note that all WHENs are evaluate
@@ -1925,15 +1936,14 @@ sp_proc_stmt:
LEX *lex= Lex;
uint offset= lex->spcont->current_pvars();
sp_instr_set *i = new sp_instr_set(lex->sphead->instructions(),
- lex->spcont,
- offset, $2, MYSQL_TYPE_STRING);
+ lex->spcont, offset, $3,
+ MYSQL_TYPE_STRING, lex, TRUE);
LEX_STRING dummy={(char*)"", 0};
lex->spcont->push_pvar(&dummy, MYSQL_TYPE_STRING, sp_param_in);
- i->tables= lex->query_tables;
- lex->query_tables= 0;
lex->sphead->add_instr(i);
lex->sphead->m_simple_case= TRUE;
+ lex->sphead->restore_lex(YYTHD);
}
sp_case END CASE_SYM
{
@@ -2187,18 +2197,19 @@ sp_fetch_list:
;
sp_if:
- expr THEN_SYM
+ { Lex->sphead->reset_lex(YYTHD); }
+ expr THEN_SYM
{
LEX *lex= Lex;
sp_head *sp= lex->sphead;
sp_pcontext *ctx= lex->spcont;
uint ip= sp->instructions();
- sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, ctx, $1);
+ sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, ctx,
+ $2, lex);
- i->tables= lex->query_tables;
- lex->query_tables= 0;
sp->push_backpatch(i, ctx->push_label((char *)"", 0));
sp->add_instr(i);
+ sp->restore_lex(YYTHD);
}
sp_proc_stmts1
{
@@ -2226,7 +2237,8 @@ sp_elseifs:
;
sp_case:
- expr THEN_SYM
+ { Lex->sphead->reset_lex(YYTHD); }
+ expr THEN_SYM
{
LEX *lex= Lex;
sp_head *sp= lex->sphead;
@@ -2235,7 +2247,7 @@ sp_case:
sp_instr_jump_if_not *i;
if (! sp->m_simple_case)
- i= new sp_instr_jump_if_not(ip, ctx, $1);
+ i= new sp_instr_jump_if_not(ip, ctx, $2, lex);
else
{ /* Simple case: <caseval> = <whenval> */
LEX_STRING ivar;
@@ -2244,15 +2256,14 @@ sp_case:
ivar.length= 5;
Item *var= (Item*) new Item_splocal(ivar,
ctx->current_pvars()-1);
- Item *expr= new Item_func_eq(var, $1);
+ Item *expr= new Item_func_eq(var, $2);
- i= new sp_instr_jump_if_not(ip, ctx, expr);
+ i= new sp_instr_jump_if_not(ip, ctx, expr, lex);
lex->variables_used= 1;
}
sp->push_backpatch(i, ctx->push_label((char *)"", 0));
- i->tables= lex->query_tables;
- lex->query_tables= 0;
sp->add_instr(i);
+ sp->restore_lex(YYTHD);
}
sp_proc_stmts1
{
@@ -2368,19 +2379,20 @@ sp_unlabeled_control:
lex->sphead->add_instr(i);
}
- | WHILE_SYM expr DO_SYM
+ | WHILE_SYM
+ { Lex->sphead->reset_lex(YYTHD); }
+ expr DO_SYM
{
LEX *lex= Lex;
sp_head *sp= lex->sphead;
uint ip= sp->instructions();
sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, lex->spcont,
- $2);
+ $3, lex);
/* Jumping forward */
sp->push_backpatch(i, lex->spcont->last_label());
- i->tables= lex->query_tables;
- lex->query_tables= 0;
sp->add_instr(i);
+ sp->restore_lex(YYTHD);
}
sp_proc_stmts1 END WHILE_SYM
{
@@ -2391,17 +2403,18 @@ sp_unlabeled_control:
lex->sphead->add_instr(i);
}
- | REPEAT_SYM sp_proc_stmts1 UNTIL_SYM expr END REPEAT_SYM
+ | REPEAT_SYM sp_proc_stmts1 UNTIL_SYM
+ { Lex->sphead->reset_lex(YYTHD); }
+ expr END REPEAT_SYM
{
LEX *lex= Lex;
uint ip= lex->sphead->instructions();
sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */
sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, lex->spcont,
- $4, lab->ip);
-
- i->tables= lex->query_tables;
- lex->query_tables= 0;
+ $5, lab->ip,
+ lex);
lex->sphead->add_instr(i);
+ lex->sphead->restore_lex(YYTHD);
}
;
@@ -7168,8 +7181,75 @@ opt_option:
| OPTION {};
option_value_list:
+ option_type_value
+ | option_value_list ',' option_type_value;
+
+option_type_value:
+ {
+ if (Lex->sphead)
+ {
+ /*
+ If we are in SP we want have own LEX for each assignment.
+ This is mostly because it is hard for several sp_instr_set
+ and sp_instr_set_trigger instructions share one LEX.
+ (Well, it is theoretically possible but adds some extra
+ overhead on preparation for execution stage and IMO less
+ robust).
+
+ QQ: May be we should simply prohibit group assignments in SP?
+ */
+ LEX *lex;
+ Lex->sphead->reset_lex(YYTHD);
+ lex= Lex;
+
+ /* Set new LEX as if we at start of set rule. */
+ lex->sql_command= SQLCOM_SET_OPTION;
+ mysql_init_select(lex);
+ lex->option_type=OPT_SESSION;
+ lex->var_list.empty();
+ lex->one_shot_set= 0;
+ lex->sphead->m_tmp_query= lex->tok_start;
+ }
+ }
option_type option_value
- | option_value_list ',' option_type option_value;
+ {
+ LEX *lex= Lex;
+
+ if (lex->sphead)
+ {
+ sp_head *sp= lex->sphead;
+
+ if (!lex->var_list.is_empty())
+ {
+ /*
+ We have assignment to user or system variable or
+ option setting, so we should construct sp_instr_stmt
+ for it.
+ */
+ LEX_STRING qbuff;
+ sp_instr_stmt *i;
+
+ if (!(i= new sp_instr_stmt(sp->instructions(), lex->spcont,
+ lex)))
+ YYABORT;
+
+ if (lex->ptr - lex->tok_end > 1)
+ qbuff.length= lex->ptr - sp->m_tmp_query;
+ else
+ qbuff.length= lex->tok_end - sp->m_tmp_query;
+
+ if (!(qbuff.str= alloc_root(YYTHD->mem_root, qbuff.length + 5)))
+ YYABORT;
+
+ strmake(strmake(qbuff.str, "SET ", 4), (char *)sp->m_tmp_query,
+ qbuff.length);
+ qbuff.length+= 4;
+ i->m_query= qbuff;
+ sp->add_instr(i);
+ }
+ lex->sphead->restore_lex(YYTHD);
+ }
+ };
option_type:
/* empty */ {}
@@ -7196,31 +7276,7 @@ opt_var_ident_type:
option_value:
'@' ident_or_text equal expr
{
- LEX *lex= Lex;
-
- if (lex->sphead && lex->sphead->m_type != TYPE_ENUM_PROCEDURE)
- {
- /*
- We have to use special instruction in functions and triggers
- because sp_instr_stmt will close all tables and thus ruin
- execution of statement invoking function or trigger.
-
- We also do not want to allow expression with subselects in
- this case.
- */
- if (lex->query_tables)
- {
- my_message(ER_SP_SUBSELECT_NYI, ER(ER_SP_SUBSELECT_NYI),
- MYF(0));
- YYABORT;
- }
- sp_instr_set_user_var *i=
- new sp_instr_set_user_var(lex->sphead->instructions(),
- lex->spcont, $2, $4);
- lex->sphead->add_instr(i);
- }
- else
- lex->var_list.push_back(new set_var_user(new Item_func_set_user_var($2,$4)));
+ Lex->var_list.push_back(new set_var_user(new Item_func_set_user_var($2,$4)));
}
| internal_variable_name equal set_expr_or_default
{
@@ -7281,9 +7337,7 @@ option_value:
else
it= new Item_null();
i= new sp_instr_set(lex->sphead->instructions(), ctx,
- spv->offset, it, spv->type);
- i->tables= lex->query_tables;
- lex->query_tables= 0;
+ spv->offset, it, spv->type, lex, TRUE);
lex->sphead->add_instr(i);
spv->isset= TRUE;
}
diff --git a/sql/table.h b/sql/table.h
index 4306d3733c4..49ead2cb0b7 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -429,6 +429,11 @@ typedef struct st_table_list
/* FRMTYPE_ERROR if any type is acceptable */
enum frm_type_enum required_type;
char timestamp_buffer[20]; /* buffer for timestamp (19+1) */
+ /*
+ This TABLE_LIST object is just placeholder for prelocking, it will be
+ used for implicit LOCK TABLES only and won't be used in real statement.
+ */
+ bool prelocking_placeholder;
void calc_md5(char *buffer);
void set_ancestor();
diff --git a/sql/tztime.cc b/sql/tztime.cc
index b2b3576e221..bd9d49f0ab0 100644
--- a/sql/tztime.cc
+++ b/sql/tztime.cc
@@ -1524,7 +1524,6 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
TZ_NAMES_ENTRY *tmp_tzname;
my_bool return_val= 1;
int res;
- uint counter;
DBUG_ENTER("my_tz_init");
/*
@@ -1593,8 +1592,7 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
last_global_next_ptr= &(tables_buff[0].next_global);
tz_init_table_list(tables_buff + 1, &last_global_next_ptr);
- if (open_tables(thd, tables_buff, &counter) ||
- lock_tables(thd, tables_buff, counter))
+ if (simple_open_n_lock_tables(thd, tables_buff))
{
sql_print_warning("Can't open and lock time zone table: %s "
"trying to live without them", thd->net.last_error);