Commit fcad8201 authored by Konstantin Osipov's avatar Konstantin Osipov

Backport of:

----------------------------------------------------------
revno: 2617.69.24
committer: Konstantin Osipov <kostja@sun.com>
branch nick: 5.4-42546
timestamp: Fri 2009-08-14 19:22:05 +0400
message:
  A pre-requisite for a fix for Bug#42546 "Backup: RESTORE fails, thinking it
  finds an existing table"
  Back-port from WL 148 "Foreign keys" feature tree a patch
  that introduced Prelocking_strategy class -- a way to parameterize
  open_tables() behaviour, implemented by Dmitry Lenev.

(Part of WL#4284).

sql/sql_base.cc:
  Implement different prelocking strategies. Use an instance of
  prelocking_strategy in open_tables().
sql/sql_class.h:
  Add declarations for class Prelocking_strategy.
sql/sql_lex.h:
  Add a helper method to access last table of the global table list
  (lex->query_tables).
sql/sql_parse.cc:
  Use a special prelocking strategy when locking tables for LOCK TABLES.
sql/sql_table.cc:
  Use normal open_and_lock_tables_derived() in ALTER TABLE.
sql/sql_yacc.yy:
  Modify the grammar to not pollute the global table list with tables
  that should not be opened.
parent c8f4e972
......@@ -1474,10 +1474,26 @@ int setup_ftfuncs(SELECT_LEX* select);
int init_ftfuncs(THD *thd, SELECT_LEX* select, bool no_order);
void wait_for_condition(THD *thd, pthread_mutex_t *mutex,
pthread_cond_t *cond);
int open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags);
int open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags,
Prelocking_strategy *prelocking_strategy);
inline int open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags)
{
DML_prelocking_strategy prelocking_strategy;
return open_tables(thd, tables, counter, flags, &prelocking_strategy);
}
/* open_and_lock_tables with optional derived handling */
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);
inline int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables,
bool derived, uint flags)
{
DML_prelocking_strategy prelocking_strategy;
return open_and_lock_tables_derived(thd, tables, derived, flags,
&prelocking_strategy);
}
/* simple open_and_lock_tables without derived handling */
inline int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables)
{
......
This diff is collapsed.
......@@ -42,6 +42,9 @@ sp_head *
sp_find_routine(THD *thd, int type, sp_name *name,
sp_cache **cp, bool cache_only);
int
sp_cache_routine(THD *thd, int type, sp_name *name, sp_head **sp);
bool
sp_exist_routines(THD *thd, TABLE_LIST *procs, bool any);
......@@ -60,22 +63,45 @@ sp_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics);
int
sp_drop_routine(THD *thd, int type, sp_name *name);
/**
Structure that represents element in the set of stored routines
used by statement or routine.
*/
struct Sroutine_hash_entry
{
/**
Set key consisting of one-byte routine type and quoted routine name.
*/
LEX_STRING key;
/**
Next element in list linking all routines in set. See also comments
for LEX::sroutine/sroutine_list and sp_head::m_sroutines.
*/
Sroutine_hash_entry *next;
/**
Uppermost view which directly or indirectly uses this routine.
0 if routine is not used in view. Note that it also can be 0 if
statement uses routine both via view and directly.
*/
TABLE_LIST *belong_to_view;
};
/*
Procedures for pre-caching of stored routines and building table list
for prelocking.
Procedures for handling sets of stored routines used by statement or routine.
*/
void sp_get_prelocking_info(THD *thd, bool *need_prelocking,
bool *first_no_prelocking);
void sp_add_used_routine(LEX *lex, Query_arena *arena,
void sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
sp_name *rt, char rt_type);
void sp_remove_not_own_routines(LEX *lex);
bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
const LEX_STRING *key, TABLE_LIST *belong_to_view);
void sp_remove_not_own_routines(Query_tables_list *prelocking_ctx);
void sp_update_sp_used_routines(HASH *dst, HASH *src);
int sp_cache_routines_and_add_tables(THD *thd, LEX *lex,
bool first_no_prelock);
int sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex,
TABLE_LIST *view);
int sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
TABLE_LIST *table);
void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx,
HASH *src, TABLE_LIST *belong_to_view);
void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx,
SQL_LIST *src, TABLE_LIST *belong_to_view);
extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen,
my_bool first);
......
......@@ -454,10 +454,10 @@ class sp_head :private Query_arena
/*
This method is intended for attributes of a routine which need
to propagate upwards to the LEX of the caller (when a property of a
sp_head needs to "taint" the caller).
to propagate upwards to the Query_tables_list of the caller (when
a property of a sp_head needs to "taint" the calling statement).
*/
void propagate_attributes(LEX *lex)
void propagate_attributes(Query_tables_list *prelocking_ctx)
{
/*
If this routine needs row-based binary logging, the entire top statement
......@@ -466,7 +466,7 @@ class sp_head :private Query_arena
the substatements not).
*/
if (m_flags & BINLOG_ROW_BASED_IF_MIXED)
lex->set_stmt_unsafe();
prelocking_ctx->set_stmt_unsafe();
}
......
This diff is collapsed.
......@@ -38,6 +38,7 @@ class sp_rcontext;
class sp_cache;
class Parser_state;
class Rows_log_event;
class Sroutine_hash_entry;
enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE };
enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY, RNEXT_SAME };
......@@ -1181,6 +1182,89 @@ class Drop_table_error_handler : public Internal_error_handler
};
/**
An abstract class for a strategy specifying how the prelocking
algorithm should extend the prelocking set while processing
already existing elements in the set.
*/
class Prelocking_strategy
{
public:
virtual ~Prelocking_strategy() { }
virtual bool handle_routine(THD *thd, Query_tables_list *prelocking_ctx,
Sroutine_hash_entry *rt, sp_head *sp,
bool *need_prelocking) = 0;
virtual bool handle_table(THD *thd, Query_tables_list *prelocking_ctx,
TABLE_LIST *table_list, bool *need_prelocking) = 0;
virtual bool handle_view(THD *thd, Query_tables_list *prelocking_ctx,
TABLE_LIST *table_list, bool *need_prelocking)= 0;
};
/**
A Strategy for prelocking algorithm suitable for DML statements.
Ensures that all tables used by all statement's SF/SP/triggers and
required for foreign key checks are prelocked and SF/SPs used are
cached.
*/
class DML_prelocking_strategy : public Prelocking_strategy
{
public:
virtual bool handle_routine(THD *thd, Query_tables_list *prelocking_ctx,
Sroutine_hash_entry *rt, sp_head *sp,
bool *need_prelocking);
virtual bool handle_table(THD *thd, Query_tables_list *prelocking_ctx,
TABLE_LIST *table_list, bool *need_prelocking);
virtual bool handle_view(THD *thd, Query_tables_list *prelocking_ctx,
TABLE_LIST *table_list, bool *need_prelocking);
};
/**
A strategy for prelocking algorithm to be used for LOCK TABLES
statement.
*/
class Lock_tables_prelocking_strategy : public DML_prelocking_strategy
{
virtual bool handle_table(THD *thd, Query_tables_list *prelocking_ctx,
TABLE_LIST *table_list, bool *need_prelocking);
};
/**
Strategy for prelocking algorithm to be used for ALTER TABLE statements.
Unlike DML or LOCK TABLES strategy, it doesn't
prelock triggers, views or stored routines, since they are not
used during ALTER.
*/
class Alter_table_prelocking_strategy : public Prelocking_strategy
{
public:
Alter_table_prelocking_strategy(Alter_info *alter_info)
: m_alter_info(alter_info)
{}
virtual bool handle_routine(THD *thd, Query_tables_list *prelocking_ctx,
Sroutine_hash_entry *rt, sp_head *sp,
bool *need_prelocking);
virtual bool handle_table(THD *thd, Query_tables_list *prelocking_ctx,
TABLE_LIST *table_list, bool *need_prelocking);
virtual bool handle_view(THD *thd, Query_tables_list *prelocking_ctx,
TABLE_LIST *table_list, bool *need_prelocking);
private:
Alter_info *m_alter_info;
};
/**
A context of open_tables() function, used to recover
from a failed open_table() attempt.
......
......@@ -1052,6 +1052,17 @@ class Query_tables_list
}
}
/** Return a pointer to the last element in query table list. */
TABLE_LIST *last_table()
{
/* Don't use offsetof() macro in order to avoid warnings. */
return query_tables ?
(TABLE_LIST*) ((char*) query_tables_last -
((char*) &(query_tables->next_global) -
(char*) query_tables)) :
0;
}
/**
Has the parser/scanner detected that this statement is unsafe?
*/
......@@ -1751,6 +1762,12 @@ struct LEX: public Query_tables_list
bool subqueries, ignore;
st_parsing_options parsing_options;
Alter_info alter_info;
/*
For CREATE TABLE statement last element of table list which is not
part of SELECT or LIKE part (i.e. either element for table we are
creating or last of tables referenced by foreign keys).
*/
TABLE_LIST *create_last_non_select_table;
/* Prepared statements SQL syntax:*/
LEX_STRING prepared_stmt_name; /* Statement name (in all queries) */
/*
......
......@@ -3342,9 +3342,14 @@ case SQLCOM_PREPARE:
thd->options|= OPTION_TABLE_LOCK;
thd->in_lock_tables=1;
res= (open_and_lock_tables_derived(thd, all_tables, FALSE,
MYSQL_OPEN_TAKE_UPGRADABLE_MDL) ||
thd->locked_tables_list.init_locked_tables(thd));
{
Lock_tables_prelocking_strategy lock_tables_prelocking_strategy;
res= (open_and_lock_tables_derived(thd, all_tables, FALSE,
MYSQL_OPEN_TAKE_UPGRADABLE_MDL,
&lock_tables_prelocking_strategy) ||
thd->locked_tables_list.init_locked_tables(thd));
}
thd->in_lock_tables= 0;
......
......@@ -6560,9 +6560,26 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
DBUG_RETURN(error);
}
if (!(table= open_n_lock_single_table(thd, table_list, TL_WRITE_ALLOW_READ,
MYSQL_OPEN_TAKE_UPGRADABLE_MDL)))
/*
Code below can handle only base tables so ensure that we won't open a view.
Note that RENAME TABLE the only ALTER clause which is supported for views
has been already processed.
*/
table_list->required_type= FRMTYPE_TABLE;
Alter_table_prelocking_strategy alter_prelocking_strategy(alter_info);
error= open_and_lock_tables_derived(thd, table_list, FALSE,
MYSQL_OPEN_TAKE_UPGRADABLE_MDL,
&alter_prelocking_strategy);
if (error)
{
DBUG_RETURN(TRUE);
}
table= table_list->table;
table->use_all_columns();
mdl_ticket= table->mdl_ticket;
......@@ -6572,7 +6589,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
set of tables from the old table or to open a new TABLE object for
an extended list and verify that they belong to locked tables.
*/
if (thd->locked_tables_mode &&
if ((thd->locked_tables_mode == LTM_LOCK_TABLES ||
thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES) &&
(create_info->used_fields & HA_CREATE_USED_UNION) &&
(table->s->tmp_table == NO_TMP_TABLE))
{
......@@ -6806,7 +6824,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
table_list->table= NULL; // For query cache
query_cache_invalidate3(thd, table_list, 0);
if (thd->locked_tables_mode)
if ((thd->locked_tables_mode == LTM_LOCK_TABLES ||
thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES))
{
/*
Under LOCK TABLES we should adjust meta-data locks before finishing
......@@ -7290,7 +7309,9 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
if (table->s->tmp_table != NO_TMP_TABLE)
{
/* Close lock if this is a transactional table */
if (thd->lock && ! thd->locked_tables_mode)
if (thd->lock &&
! (thd->locked_tables_mode == LTM_LOCK_TABLES ||
thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES))
{
mysql_unlock_tables(thd, thd->lock);
thd->lock=0;
......@@ -7492,7 +7513,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
table_list->table=0; // For query cache
query_cache_invalidate3(thd, table_list, 0);
if (thd->locked_tables_mode)
if (thd->locked_tables_mode == LTM_LOCK_TABLES ||
thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES)
{
if ((new_name != table_name || new_db != db))
{
......
......@@ -19,6 +19,7 @@
#include "sp_head.h"
#include "sql_trigger.h"
#include "parse_file.h"
#include "sp.h"
/*************************************************************************/
......@@ -2017,6 +2018,57 @@ bool Table_triggers_list::process_triggers(THD *thd,
}
/**
Add triggers for table to the set of routines used by statement.
Add tables used by them to statement table list. Do the same for
routines used by triggers.
@param thd Thread context.
@param prelocking_ctx Prelocking context of the statement.
@param table_list Table list element for table with trigger.
@retval FALSE Success.
@retval TRUE Failure.
*/
bool
Table_triggers_list::
add_tables_and_routines_for_triggers(THD *thd,
Query_tables_list *prelocking_ctx,
TABLE_LIST *table_list)
{
DBUG_ASSERT(static_cast<int>(table_list->lock_type) >=
static_cast<int>(TL_WRITE_ALLOW_WRITE));
for (int i= 0; i < (int)TRG_EVENT_MAX; i++)
{
if (table_list->trg_event_map &
static_cast<uint8>(1 << static_cast<int>(i)))
{
for (int j= 0; j < (int)TRG_ACTION_MAX; j++)
{
/* We can have only one trigger per action type currently */
sp_head *trigger= table_list->table->triggers->bodies[i][j];
if (trigger && sp_add_used_routine(prelocking_ctx, thd->stmt_arena,
&trigger->m_sroutines_key,
table_list->belong_to_view))
{
trigger->add_used_tables_to_table_list(thd,
&prelocking_ctx->query_tables_last,
table_list->belong_to_view);
sp_update_stmt_used_routines(thd, prelocking_ctx,
&trigger->m_sroutines,
table_list->belong_to_view);
trigger->propagate_attributes(prelocking_ctx);
}
}
}
}
return FALSE;
}
/**
Mark fields of subject table which we read/set in its triggers
as such.
......
......@@ -144,8 +144,10 @@ class Table_triggers_list: public Sql_alloc
void mark_fields_used(trg_event_type event);
friend class Item_trigger_field;
friend int sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
TABLE_LIST *table);
bool add_tables_and_routines_for_triggers(THD *thd,
Query_tables_list *prelocking_ctx,
TABLE_LIST *table_list);
private:
bool prepare_record1_accessors(TABLE *table);
......
......@@ -1766,6 +1766,7 @@ create:
lex->create_info.default_table_charset= NULL;
lex->name.str= 0;
lex->name.length= 0;
lex->create_last_non_select_table= lex->last_table();
}
create2
{
......@@ -1788,7 +1789,8 @@ create:
lex->sql_command= SQLCOM_CREATE_INDEX;
if (!lex->current_select->add_table_to_list(lex->thd, $7,
NULL,
TL_OPTION_UPDATING))
TL_OPTION_UPDATING,
TL_WRITE_ALLOW_READ))
MYSQL_YYABORT;
lex->alter_info.reset();
lex->alter_info.flags= ALTER_ADD_INDEX;
......@@ -3952,7 +3954,7 @@ create2:
;
create2a:
field_list ')' opt_create_table_options
create_field_list ')' opt_create_table_options
opt_partitioning
create3 {}
| opt_partitioning
......@@ -4802,19 +4804,30 @@ create_table_option:
Lex->create_info.row_type= $3;
Lex->create_info.used_fields|= HA_CREATE_USED_ROW_FORMAT;
}
| UNION_SYM opt_equal '(' opt_table_list ')'
| UNION_SYM opt_equal
{
Lex->select_lex.table_list.save_and_clear(&Lex->save_list);
}
'(' opt_table_list ')'
{
/* Move the union list to the merge_list */
/*
Move the union list to the merge_list and exclude its tables
from the global list.
*/
LEX *lex=Lex;
TABLE_LIST *table_list= lex->select_lex.get_table_list();
lex->create_info.merge_list= lex->select_lex.table_list;
lex->create_info.merge_list.elements--;
lex->create_info.merge_list.first=
(uchar*) (table_list->next_local);
lex->select_lex.table_list.elements=1;
lex->select_lex.table_list.next=
(uchar**) &(table_list->next_local);
table_list->next_local= 0;
lex->select_lex.table_list= lex->save_list;
/*
When excluding union list from the global list we assume that
elements of the former immediately follow elements which represent
table being created/altered and parent tables.
*/
TABLE_LIST *last_non_sel_table= lex->create_last_non_select_table;
DBUG_ASSERT(last_non_sel_table->next_global ==
(TABLE_LIST *)lex->create_info.merge_list.first);
last_non_sel_table->next_global= 0;
Lex->query_tables_last= &last_non_sel_table->next_global;
lex->create_info.used_fields|= HA_CREATE_USED_UNION;
}
| default_charset
......@@ -4952,6 +4965,14 @@ udf_type:
| INT_SYM {$$ = (int) INT_RESULT; }
;
create_field_list:
field_list
{
Lex->create_last_non_select_table= Lex->last_table();
}
;
field_list:
field_list_item
| field_list ',' field_list_item
......@@ -5743,7 +5764,8 @@ alter:
lex->sql_command= SQLCOM_ALTER_TABLE;
lex->duplicates= DUP_ERROR;
if (!lex->select_lex.add_table_to_list(thd, $4, NULL,
TL_OPTION_UPDATING))
TL_OPTION_UPDATING,
TL_WRITE_ALLOW_READ))
MYSQL_YYABORT;
lex->col_list.empty();
lex->select_lex.init_order();
......@@ -5756,6 +5778,7 @@ alter:
lex->alter_info.reset();
lex->no_write_to_binlog= 0;
lex->create_info.storage_media= HA_SM_DEFAULT;
lex->create_last_non_select_table= lex->last_table();
}
alter_commands
{}
......@@ -6139,12 +6162,16 @@ add_column:
;
alter_list_item:
add_column column_def opt_place { }
add_column column_def opt_place
{
Lex->create_last_non_select_table= Lex->last_table();
}
| ADD key_def
{
Lex->create_last_non_select_table= Lex->last_table();
Lex->alter_info.flags|= ALTER_ADD_INDEX;
}
| add_column '(' field_list ')'
| add_column '(' create_field_list ')'
{
Lex->alter_info.flags|= ALTER_ADD_COLUMN | ALTER_ADD_INDEX;
}
......@@ -6155,6 +6182,9 @@ alter_list_item:
lex->alter_info.flags|= ALTER_CHANGE_COLUMN;
}
field_spec opt_place
{
Lex->create_last_non_select_table= Lex->last_table();
}
| MODIFY_SYM opt_column field_ident
{
LEX *lex=Lex;
......@@ -6177,6 +6207,9 @@ alter_list_item:
MYSQL_YYABORT;
}
opt_place
{
Lex->create_last_non_select_table= Lex->last_table();
}
| DROP opt_column field_ident opt_restrict
{
LEX *lex=Lex;
......@@ -9607,7 +9640,8 @@ drop:
lex->alter_info.flags= ALTER_DROP_INDEX;
lex->alter_info.drop_list.push_back(ad);
if (!lex->current_select->add_table_to_list(lex->thd, $5, NULL,
TL_OPTION_UPDATING))
TL_OPTION_UPDATING,
TL_WRITE_ALLOW_READ))
MYSQL_YYABORT;
}
| DROP DATABASE if_exists ident
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment