summaryrefslogtreecommitdiff
path: root/sql/sql_base.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_base.cc')
-rw-r--r--sql/sql_base.cc440
1 files changed, 381 insertions, 59 deletions
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 7650d854efb..4be80f4c8a5 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -3714,34 +3714,99 @@ thr_lock_type read_lock_type_for_table(THD *thd, TABLE *table)
/*
+ Perform steps of prelocking algorithm for elements of the
+ prelocking set other than tables. E.g. cache routines and, if
+ prelocking strategy prescribes so, extend the prelocking set with
+ tables and routines used by them.
+
+ @param[in] thd Thread context.
+ @param[in] prelocking_ctx Prelocking context.
+ @param[in] start First element in the list representing
+ subset of the prelocking set to be
+ processed.
+ @param[in] prelocking_strategy Strategy which specifies how the
+ prelocking set should be extended when
+ one of its elements is processed.
+ @param[out] need_prelocking Set to TRUE if it was detected that this
+ statement will require prelocked mode for
+ its execution, not touched otherwise.
+
+ @retval FALSE Success.
+ @retval TRUE Failure (Conflicting metadata lock, OOM, other errors).
+*/
+
+static bool
+open_routines(THD *thd, Query_tables_list *prelocking_ctx,
+ Sroutine_hash_entry *start,
+ Prelocking_strategy *prelocking_strategy,
+ bool *need_prelocking)
+{
+ DBUG_ENTER("open_routines");
+
+ for (Sroutine_hash_entry *rt= start; rt; rt= rt->next)
+ {
+ int type= rt->key.str[0];
+
+ switch (type)
+ {
+ case TYPE_ENUM_FUNCTION:
+ case TYPE_ENUM_PROCEDURE:
+ {
+ sp_name name(thd, rt->key.str, rt->key.length);
+ sp_head *sp;
+
+ if (sp_cache_routine(thd, type, &name, &sp))
+ DBUG_RETURN(TRUE);
+
+ if (sp)
+ {
+ prelocking_strategy->handle_routine(thd, prelocking_ctx, rt, sp,
+ need_prelocking);
+ }
+ }
+ break;
+ case TYPE_ENUM_TRIGGER:
+ break;
+ default:
+ /* Impossible type value. */
+ DBUG_ASSERT(0);
+ }
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
Open all tables in list
- SYNOPSIS
- open_tables()
- thd - thread handler
- start - list of tables in/out
- counter - number of opened tables will be return using this parameter
- flags - bitmap of flags to modify how the tables will be open:
- MYSQL_LOCK_IGNORE_FLUSH - open table even if someone has
- done a flush or namelock on it.
+ @param[in] thd Thread context.
+ @param[in,out] start List of tables to be open (it can be adjusted for
+ statement that uses tables only implicitly, e.g.
+ for "SELECT f1()").
+ @param[out] counter Number of tables which were open.
+ @param[in] flags Bitmap of flags to modify how the tables will be
+ open, see open_table() description for details.
+ @param[in] prelocking_strategy Strategy which specifies how prelocking
+ algorithm should work for this statement.
- 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.
+ @note
+ Unless we are already in prelocked mode and prelocking strategy prescribes
+ so 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. Statement that uses SFs, invokes triggers or
+ requires foreign key checks will be marked as requiring prelocking.
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
+ @retval 0 OK
+ @retval -1 Error.
*/
-int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
+int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags,
+ Prelocking_strategy *prelocking_strategy)
{
TABLE_LIST *tables= NULL;
Open_table_context ot_ctx(thd);
@@ -3786,13 +3851,14 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
!thd->lex->requires_prelocking() &&
thd->lex->uses_stored_routines())
{
- bool first_no_prelocking, need_prelocking;
+ bool need_prelocking= FALSE;
TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last;
DBUG_ASSERT(thd->lex->query_tables == *start);
- sp_get_prelocking_info(thd, &need_prelocking, &first_no_prelocking);
- if (sp_cache_routines_and_add_tables(thd, thd->lex, first_no_prelocking))
+ if (open_routines(thd, thd->lex,
+ (Sroutine_hash_entry *)thd->lex->sroutines_list.first,
+ prelocking_strategy, &need_prelocking))
{
/*
Serious error during reading stored routines from mysql.proc table.
@@ -3973,27 +4039,54 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
/*
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.
+ yet built we might have to build the prelocking set for this statement.
+
+ Since currently no prelocking strategy prescribes doing anything for
+ tables which are only read, we do below checks only if table is going
+ to be changed.
*/
if (thd->locked_tables_mode <= LTM_LOCK_TABLES &&
!thd->lex->requires_prelocking() &&
- tables->trg_event_map && tables->table->triggers &&
tables->lock_type >= TL_WRITE_ALLOW_WRITE)
{
- if (!query_tables_last_own)
- query_tables_last_own= thd->lex->query_tables_last;
- if (sp_cache_routines_and_add_tables_for_triggers(thd, thd->lex,
- tables))
+ bool need_prelocking= FALSE;
+ bool not_used;
+ TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last;
+ Sroutine_hash_entry **sroutines_next=
+ (Sroutine_hash_entry **)thd->lex->sroutines_list.next;
+
+ /*
+ Extend statement's table list and the prelocking set with
+ tables and routines according to the current prelocking
+ strategy.
+
+ For example, for DML statements we need to add tables and routines
+ used by triggers which are going to be invoked for this element of
+ table list and also add tables required for handling of foreign keys.
+ */
+ error= prelocking_strategy->handle_table(thd, thd->lex, tables,
+ &need_prelocking);
+
+ if (need_prelocking && ! query_tables_last_own)
+ query_tables_last_own= save_query_tables_last;
+
+ if (error)
+ {
+ result= -1;
+ goto err;
+ }
+
+ /*
+ Process elements of the prelocking set which were added
+ by the above invocation of Prelocking_strategy method.
+
+ For example, if new element is a routine, cache it and then, if
+ prelocking strategy prescribes so, add tables it uses to the table
+ list and routines it might invoke to the prelocking set.
+ */
+ if (open_routines(thd, thd->lex, *sroutines_next, prelocking_strategy,
+ &not_used))
{
- /*
- Serious error during reading stored routines from mysql.proc table.
- Something's wrong with the table or its contents, and an error has
- been emitted; we must abort.
- */
result= -1;
goto err;
}
@@ -4039,13 +4132,27 @@ process_view_routines:
*/
if (tables->view &&
thd->locked_tables_mode <= LTM_LOCK_TABLES &&
- !thd->lex->requires_prelocking() &&
- tables->view->uses_stored_routines())
+ !thd->lex->requires_prelocking())
{
- /* We have at least one table in TL here. */
- if (!query_tables_last_own)
- query_tables_last_own= thd->lex->query_tables_last;
- if (sp_cache_routines_and_add_tables_for_view(thd, thd->lex, tables))
+ bool need_prelocking= FALSE;
+ bool not_used;
+ TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last;
+ Sroutine_hash_entry **sroutines_next=
+ (Sroutine_hash_entry **)thd->lex->sroutines_list.next;
+
+ error= prelocking_strategy->handle_view(thd, thd->lex, tables,
+ &need_prelocking);
+
+ if (need_prelocking && ! query_tables_last_own)
+ query_tables_last_own= save_query_tables_last;
+
+ if (error)
+ {
+ result= -1;
+ goto err;
+ }
+ if (open_routines(thd, thd->lex, *sroutines_next, prelocking_strategy,
+ &not_used))
{
/*
Serious error during reading stored routines from mysql.proc table.
@@ -4097,6 +4204,220 @@ process_view_routines:
}
+/**
+ Defines how prelocking algorithm for DML statements should handle routines:
+ - For CALL statements we do unrolling (i.e. open and lock tables for each
+ sub-statement individually). So for such statements prelocking is enabled
+ only if stored functions are used in parameter list and only for period
+ during which we calculate values of parameters. Thus in this strategy we
+ ignore procedure which is directly called by such statement and extend
+ the prelocking set only with tables/functions used by SF called from the
+ parameter list.
+ - For any other statement any routine which is directly or indirectly called
+ by statement is going to be executed in prelocked mode. So in this case we
+ simply add all tables and routines used by it to the prelocking set.
+
+ @param[in] thd Thread context.
+ @param[in] prelocking_ctx Prelocking context of the statement.
+ @param[in] rt Prelocking set element describing routine.
+ @param[in] sp Routine body.
+ @param[out] need_prelocking Set to TRUE if method detects that prelocking
+ required, not changed otherwise.
+
+ @retval FALSE Success.
+ @retval TRUE Failure (OOM).
+*/
+
+bool DML_prelocking_strategy::
+handle_routine(THD *thd, Query_tables_list *prelocking_ctx,
+ Sroutine_hash_entry *rt, sp_head *sp, bool *need_prelocking)
+{
+ /*
+ We assume that for any "CALL proc(...)" statement sroutines_list will
+ have 'proc' as first element (it may have several, consider e.g.
+ "proc(sp_func(...)))". This property is currently guaranted by the
+ parser.
+ */
+
+ if (rt != (Sroutine_hash_entry*)prelocking_ctx->sroutines_list.first ||
+ rt->key.str[0] != TYPE_ENUM_PROCEDURE)
+ {
+ *need_prelocking= TRUE;
+ sp_update_stmt_used_routines(thd, prelocking_ctx, &sp->m_sroutines,
+ rt->belong_to_view);
+ (void)sp->add_used_tables_to_table_list(thd,
+ &prelocking_ctx->query_tables_last,
+ rt->belong_to_view);
+ }
+ sp->propagate_attributes(prelocking_ctx);
+ return FALSE;
+}
+
+
+/**
+ Defines how prelocking algorithm for DML statements should handle table list
+ elements:
+ - If table has triggers we should add all tables and routines
+ used by them to the prelocking set.
+
+ We do not need to acquire metadata locks on trigger names
+ in DML statements, since all DDL statements
+ that change trigger metadata always lock their
+ subject tables.
+
+ @param[in] thd Thread context.
+ @param[in] prelocking_ctx Prelocking context of the statement.
+ @param[in] table_list Table list element for table.
+ @param[in] sp Routine body.
+ @param[out] need_prelocking Set to TRUE if method detects that prelocking
+ required, not changed otherwise.
+
+ @retval FALSE Success.
+ @retval TRUE Failure (OOM).
+*/
+
+bool DML_prelocking_strategy::
+handle_table(THD *thd, Query_tables_list *prelocking_ctx,
+ TABLE_LIST *table_list, bool *need_prelocking)
+{
+ /* We rely on a caller to check that table is going to be changed. */
+ DBUG_ASSERT(table_list->lock_type >= TL_WRITE_ALLOW_WRITE);
+
+ if (table_list->trg_event_map)
+ {
+ if (table_list->table->triggers)
+ {
+ *need_prelocking= TRUE;
+
+ if (table_list->table->triggers->
+ add_tables_and_routines_for_triggers(thd, prelocking_ctx, table_list))
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Defines how prelocking algorithm for DML statements should handle view -
+ all view routines should be added to the prelocking set.
+
+ @param[in] thd Thread context.
+ @param[in] prelocking_ctx Prelocking context of the statement.
+ @param[in] table_list Table list element for view.
+ @param[in] sp Routine body.
+ @param[out] need_prelocking Set to TRUE if method detects that prelocking
+ required, not changed otherwise.
+
+ @retval FALSE Success.
+ @retval TRUE Failure (OOM).
+*/
+
+bool DML_prelocking_strategy::
+handle_view(THD *thd, Query_tables_list *prelocking_ctx,
+ TABLE_LIST *table_list, bool *need_prelocking)
+{
+ if (table_list->view->uses_stored_routines())
+ {
+ *need_prelocking= TRUE;
+
+ sp_update_stmt_used_routines(thd, prelocking_ctx,
+ &table_list->view->sroutines_list,
+ table_list->top_table());
+ }
+ return FALSE;
+}
+
+
+/**
+ Defines how prelocking algorithm for LOCK TABLES statement should handle
+ table list elements.
+
+ @param[in] thd Thread context.
+ @param[in] prelocking_ctx Prelocking context of the statement.
+ @param[in] table_list Table list element for table.
+ @param[in] sp Routine body.
+ @param[out] need_prelocking Set to TRUE if method detects that prelocking
+ required, not changed otherwise.
+
+ @retval FALSE Success.
+ @retval TRUE Failure (OOM).
+*/
+
+bool Lock_tables_prelocking_strategy::
+handle_table(THD *thd, Query_tables_list *prelocking_ctx,
+ TABLE_LIST *table_list, bool *need_prelocking)
+{
+ if (DML_prelocking_strategy::handle_table(thd, prelocking_ctx, table_list,
+ need_prelocking))
+ return TRUE;
+
+ /* We rely on a caller to check that table is going to be changed. */
+ DBUG_ASSERT(table_list->lock_type >= TL_WRITE_ALLOW_WRITE);
+
+ return FALSE;
+}
+
+
+/**
+ Defines how prelocking algorithm for ALTER TABLE statement should handle
+ routines - do nothing as this statement is not supposed to call routines.
+
+ We still can end up in this method when someone tries
+ to define a foreign key referencing a view, and not just
+ a simple view, but one that uses stored routines.
+*/
+
+bool Alter_table_prelocking_strategy::
+handle_routine(THD *thd, Query_tables_list *prelocking_ctx,
+ Sroutine_hash_entry *rt, sp_head *sp, bool *need_prelocking)
+{
+ return FALSE;
+}
+
+
+/**
+ Defines how prelocking algorithm for ALTER TABLE statement should handle
+ table list elements.
+
+ Unlike in DML, we do not process triggers here.
+
+ @param[in] thd Thread context.
+ @param[in] prelocking_ctx Prelocking context of the statement.
+ @param[in] table_list Table list element for table.
+ @param[in] sp Routine body.
+ @param[out] need_prelocking Set to TRUE if method detects that prelocking
+ required, not changed otherwise.
+
+
+ @retval FALSE Success.
+ @retval TRUE Failure (OOM).
+*/
+
+bool Alter_table_prelocking_strategy::
+handle_table(THD *thd, Query_tables_list *prelocking_ctx,
+ TABLE_LIST *table_list, bool *need_prelocking)
+{
+ return FALSE;
+}
+
+
+/**
+ Defines how prelocking algorithm for ALTER TABLE statement
+ should handle view - do nothing. We don't need to add view
+ routines to the prelocking set in this case as view is not going
+ to be materialized.
+*/
+
+bool Alter_table_prelocking_strategy::
+handle_view(THD *thd, Query_tables_list *prelocking_ctx,
+ TABLE_LIST *table_list, bool *need_prelocking)
+{
+ return FALSE;
+}
+
+
/*
Check that lock is ok for tables; Call start stmt if ok
@@ -4312,34 +4633,35 @@ end:
}
-/*
+/**
Open all tables in list, locks them and optionally process derived tables.
- SYNOPSIS
- open_and_lock_tables_derived()
- thd - thread handler
- tables - list of tables for open&locking
- flags - set of options to be used to open and lock tables (see
- open_tables() and mysql_lock_tables() for details).
- derived - if to handle derived tables
-
- RETURN
- FALSE - ok
- TRUE - error
+ @param thd Thread context.
+ @param tables List of tables for open and locking.
+ @param derived If to handle derived tables.
+ @param flags Bitmap of options to be used to open and lock
+ tables (see open_tables() and mysql_lock_tables()
+ for details).
+ @param prelocking_strategy Strategy which specifies how prelocking algorithm
+ should work for this statement.
- NOTE
+ @note
The lock will automaticaly be freed by close_thread_tables()
- NOTE
- There are two convenience functions:
+ @note
+ There are several convenience functions, e.g. :
- simple_open_n_lock_tables(thd, tables) without derived handling
- open_and_lock_tables(thd, tables) with derived handling
Both inline functions call open_and_lock_tables_derived() with
the third argument set appropriately.
+
+ @retval FALSE OK.
+ @retval TRUE Error
*/
-int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived,
- uint flags)
+int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables,
+ bool derived, uint flags,
+ Prelocking_strategy *prelocking_strategy)
{
uint counter;
bool need_reopen;
@@ -4349,7 +4671,7 @@ int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived,
for ( ; ; )
{
- if (open_tables(thd, &tables, &counter, flags))
+ if (open_tables(thd, &tables, &counter, flags, prelocking_strategy))
DBUG_RETURN(-1);
DBUG_EXECUTE_IF("sleep_open_and_lock_after_open", {