Manual merge of patch for Bug#11749345 (formerly known as bug#38813)
from 5.1->5.5.
This commit is contained in:
commit
34d731ef78
338
sql/sql_show.cc
338
sql/sql_show.cc
@ -2407,12 +2407,11 @@ bool schema_table_store_record(THD *thd, TABLE *table)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int make_table_list(THD *thd, SELECT_LEX *sel,
|
static int make_table_list(THD *thd, SELECT_LEX *sel,
|
||||||
LEX_STRING *db_name, LEX_STRING *table_name)
|
LEX_STRING *db_name, LEX_STRING *table_name)
|
||||||
{
|
{
|
||||||
Table_ident *table_ident;
|
Table_ident *table_ident;
|
||||||
table_ident= new Table_ident(thd, *db_name, *table_name, 1);
|
table_ident= new Table_ident(thd, *db_name, *table_name, 1);
|
||||||
sel->init_query();
|
|
||||||
if (!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ, MDL_SHARED_READ))
|
if (!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ, MDL_SHARED_READ))
|
||||||
return 1;
|
return 1;
|
||||||
return 0;
|
return 0;
|
||||||
@ -2982,91 +2981,190 @@ make_table_name_list(THD *thd, List<LEX_STRING> *table_names, LEX *lex,
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief Fill I_S table for SHOW COLUMNS|INDEX commands
|
Fill I_S table with data obtained by performing full-blown table open.
|
||||||
|
|
||||||
@param[in] thd thread handler
|
@param thd Thread handler.
|
||||||
@param[in] tables TABLE_LIST for I_S table
|
@param is_show_fields_or_keys Indicates whether it is a legacy SHOW
|
||||||
@param[in] schema_table pointer to I_S structure
|
COLUMNS or SHOW KEYS statement.
|
||||||
@param[in] can_deadlock Indicates that deadlocks are possible
|
@param table TABLE object for I_S table to be filled.
|
||||||
due to metadata locks, so to avoid
|
@param schema_table I_S table description structure.
|
||||||
them we should not wait in case if
|
@param orig_db_name Database name.
|
||||||
conflicting lock is present.
|
@param orig_table_name Table name.
|
||||||
@param[in] open_tables_state_backup pointer to Open_tables_backup object
|
@param open_tables_state_backup Open_tables_state object which is used
|
||||||
which is used to save|restore original
|
to save/restore original status of
|
||||||
status of variables related to
|
variables related to open tables state.
|
||||||
open tables state
|
@param can_deadlock Indicates that deadlocks are possible
|
||||||
|
due to metadata locks, so to avoid
|
||||||
|
them we should not wait in case if
|
||||||
|
conflicting lock is present.
|
||||||
|
|
||||||
@return Operation status
|
@retval FALSE - Success.
|
||||||
@retval 0 success
|
@retval TRUE - Failure.
|
||||||
@retval 1 error
|
|
||||||
*/
|
*/
|
||||||
|
static bool
|
||||||
static int
|
fill_schema_table_by_open(THD *thd, bool is_show_fields_or_keys,
|
||||||
fill_schema_show_cols_or_idxs(THD *thd, TABLE_LIST *tables,
|
TABLE *table, ST_SCHEMA_TABLE *schema_table,
|
||||||
ST_SCHEMA_TABLE *schema_table,
|
LEX_STRING *orig_db_name,
|
||||||
bool can_deadlock,
|
LEX_STRING *orig_table_name,
|
||||||
Open_tables_backup *open_tables_state_backup)
|
Open_tables_backup *open_tables_state_backup,
|
||||||
|
bool can_deadlock)
|
||||||
{
|
{
|
||||||
LEX *lex= thd->lex;
|
Query_arena i_s_arena(thd->mem_root,
|
||||||
bool res;
|
Query_arena::STMT_CONVENTIONAL_EXECUTION),
|
||||||
LEX_STRING tmp_lex_string, tmp_lex_string1, *db_name, *table_name;
|
backup_arena, *old_arena;
|
||||||
enum_sql_command save_sql_command= lex->sql_command;
|
LEX *old_lex= thd->lex, temp_lex, *lex;
|
||||||
TABLE_LIST *show_table_list= tables->schema_select_lex->table_list.first;
|
LEX_STRING db_name, table_name;
|
||||||
TABLE *table= tables->table;
|
TABLE_LIST *table_list;
|
||||||
int error= 1;
|
bool result= true;
|
||||||
DBUG_ENTER("fill_schema_show");
|
|
||||||
|
|
||||||
lex->all_selects_list= tables->schema_select_lex;
|
DBUG_ENTER("fill_schema_table_by_open");
|
||||||
/*
|
/*
|
||||||
Restore thd->temporary_tables to be able to process
|
When a view is opened its structures are allocated on a permanent
|
||||||
temporary tables(only for 'show index' & 'show columns').
|
statement arena and linked into the LEX tree for the current statement
|
||||||
This should be changed when processing of temporary tables for
|
(this happens even in cases when view is handled through TEMPTABLE
|
||||||
I_S tables will be done.
|
algorithm).
|
||||||
|
|
||||||
|
To prevent this process from unnecessary hogging of memory in the permanent
|
||||||
|
arena of our I_S query and to avoid damaging its LEX we use temporary
|
||||||
|
arena and LEX for table/view opening.
|
||||||
|
|
||||||
|
Use temporary arena instead of statement permanent arena. Also make
|
||||||
|
it active arena and save original one for successive restoring.
|
||||||
*/
|
*/
|
||||||
thd->temporary_tables= open_tables_state_backup->temporary_tables;
|
old_arena= thd->stmt_arena;
|
||||||
|
thd->stmt_arena= &i_s_arena;
|
||||||
|
thd->set_n_backup_active_arena(&i_s_arena, &backup_arena);
|
||||||
|
|
||||||
|
/* Prepare temporary LEX. */
|
||||||
|
thd->lex= lex= &temp_lex;
|
||||||
|
lex_start(thd);
|
||||||
|
|
||||||
|
/* Disable constant subquery evaluation as we won't be locking tables. */
|
||||||
|
lex->context_analysis_only= CONTEXT_ANALYSIS_ONLY_VIEW;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Some of process_table() functions rely on wildcard being passed from
|
||||||
|
old LEX (or at least being initialized).
|
||||||
|
*/
|
||||||
|
lex->wild= old_lex->wild;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Since make_table_list() might change database and table name passed
|
||||||
|
to it we create copies of orig_db_name and orig_table_name here.
|
||||||
|
These copies are used for make_table_list() while unaltered values
|
||||||
|
are passed to process_table() functions.
|
||||||
|
*/
|
||||||
|
if (!thd->make_lex_string(&db_name, orig_db_name->str,
|
||||||
|
orig_db_name->length, FALSE) ||
|
||||||
|
!thd->make_lex_string(&table_name, orig_table_name->str,
|
||||||
|
orig_table_name->length, FALSE))
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Create table list element for table to be open. Link it with the
|
||||||
|
temporary LEX. The latter is required to correctly open views and
|
||||||
|
produce table describing their structure.
|
||||||
|
*/
|
||||||
|
if (make_table_list(thd, &lex->select_lex, &db_name, &table_name))
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
table_list= lex->select_lex.table_list.first;
|
||||||
|
|
||||||
|
if (is_show_fields_or_keys)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Restore thd->temporary_tables to be able to process
|
||||||
|
temporary tables (only for 'show index' & 'show columns').
|
||||||
|
This should be changed when processing of temporary tables for
|
||||||
|
I_S tables will be done.
|
||||||
|
*/
|
||||||
|
thd->temporary_tables= open_tables_state_backup->temporary_tables;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Apply optimization flags for table opening which are relevant for
|
||||||
|
this I_S table. We can't do this for SHOW COLUMNS/KEYS because of
|
||||||
|
backward compatibility.
|
||||||
|
*/
|
||||||
|
table_list->i_s_requested_object= schema_table->i_s_requested_object;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Let us set fake sql_command so views won't try to merge
|
Let us set fake sql_command so views won't try to merge
|
||||||
themselves into main statement. If we don't do this,
|
themselves into main statement. If we don't do this,
|
||||||
SELECT * from information_schema.xxxx will cause problems.
|
SELECT * from information_schema.xxxx will cause problems.
|
||||||
SQLCOM_SHOW_FIELDS is used because it satisfies 'only_view_structure()'
|
SQLCOM_SHOW_FIELDS is used because it satisfies
|
||||||
|
'only_view_structure()'.
|
||||||
*/
|
*/
|
||||||
lex->sql_command= SQLCOM_SHOW_FIELDS;
|
lex->sql_command= SQLCOM_SHOW_FIELDS;
|
||||||
res= open_normal_and_derived_tables(thd, show_table_list,
|
result= open_normal_and_derived_tables(thd, table_list,
|
||||||
(MYSQL_OPEN_IGNORE_FLUSH |
|
(MYSQL_OPEN_IGNORE_FLUSH |
|
||||||
MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL |
|
MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL |
|
||||||
(can_deadlock ?
|
(can_deadlock ?
|
||||||
MYSQL_OPEN_FAIL_ON_MDL_CONFLICT : 0)));
|
MYSQL_OPEN_FAIL_ON_MDL_CONFLICT : 0)));
|
||||||
lex->sql_command= save_sql_command;
|
/*
|
||||||
|
Restore old value of sql_command back as it is being looked at in
|
||||||
|
process_table() function.
|
||||||
|
*/
|
||||||
|
lex->sql_command= old_lex->sql_command;
|
||||||
|
|
||||||
DEBUG_SYNC(thd, "after_open_table_ignore_flush");
|
DEBUG_SYNC(thd, "after_open_table_ignore_flush");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
get_all_tables() returns 1 on failure and 0 on success thus
|
XXX: show_table_list has a flag i_is_requested,
|
||||||
return only these and not the result code of ::process_table()
|
and when it's set, open_normal_and_derived_tables()
|
||||||
|
can return an error without setting an error message
|
||||||
|
in THD, which is a hack. This is why we have to
|
||||||
|
check for res, then for thd->is_error() and only then
|
||||||
|
for thd->main_da.sql_errno().
|
||||||
|
|
||||||
We should use show_table_list->alias instead of
|
Again we don't do this for SHOW COLUMNS/KEYS because
|
||||||
show_table_list->table_name because table_name
|
of backward compatibility.
|
||||||
could be changed during opening of I_S tables. It's safe
|
|
||||||
to use alias because alias contains original table name
|
|
||||||
in this case(this part of code is used only for
|
|
||||||
'show columns' & 'show statistics' commands).
|
|
||||||
*/
|
*/
|
||||||
table_name= thd->make_lex_string(&tmp_lex_string1, show_table_list->alias,
|
if (!is_show_fields_or_keys && result && thd->is_error() &&
|
||||||
strlen(show_table_list->alias), FALSE);
|
thd->stmt_da->sql_errno() == ER_NO_SUCH_TABLE)
|
||||||
if (!show_table_list->view)
|
{
|
||||||
db_name= thd->make_lex_string(&tmp_lex_string, show_table_list->db,
|
/*
|
||||||
show_table_list->db_length, FALSE);
|
Hide error for a non-existing table.
|
||||||
else
|
For example, this error can occur when we use a where condition
|
||||||
db_name= &show_table_list->view_db;
|
with a db name and table, but the table does not exist.
|
||||||
|
*/
|
||||||
|
result= false;
|
||||||
|
thd->clear_error();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result= schema_table->process_table(thd, table_list,
|
||||||
|
table, result,
|
||||||
|
orig_db_name,
|
||||||
|
orig_table_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
error= test(schema_table->process_table(thd, show_table_list,
|
end:
|
||||||
table, res, db_name,
|
lex->unit.cleanup();
|
||||||
table_name));
|
|
||||||
thd->temporary_tables= 0;
|
/* Restore original LEX value, statement's arena and THD arena values. */
|
||||||
close_tables_for_reopen(thd, &show_table_list,
|
lex_end(thd->lex);
|
||||||
open_tables_state_backup->mdl_system_tables_svp);
|
|
||||||
DBUG_RETURN(error);
|
if (i_s_arena.free_list)
|
||||||
|
i_s_arena.free_items();
|
||||||
|
|
||||||
|
/*
|
||||||
|
For safety reset list of open temporary tables before closing
|
||||||
|
all tables open within this Open_tables_state.
|
||||||
|
*/
|
||||||
|
thd->temporary_tables= NULL;
|
||||||
|
close_thread_tables(thd);
|
||||||
|
thd->mdl_context.rollback_to_savepoint(open_tables_state_backup->mdl_system_tables_svp);
|
||||||
|
|
||||||
|
thd->lex= old_lex;
|
||||||
|
|
||||||
|
thd->stmt_arena= old_arena;
|
||||||
|
thd->restore_active_arena(&i_s_arena, &backup_arena);
|
||||||
|
|
||||||
|
DBUG_RETURN(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -3477,10 +3575,8 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
|
|||||||
{
|
{
|
||||||
LEX *lex= thd->lex;
|
LEX *lex= thd->lex;
|
||||||
TABLE *table= tables->table;
|
TABLE *table= tables->table;
|
||||||
SELECT_LEX *old_all_select_lex= lex->all_selects_list;
|
|
||||||
SELECT_LEX *lsel= tables->schema_select_lex;
|
SELECT_LEX *lsel= tables->schema_select_lex;
|
||||||
ST_SCHEMA_TABLE *schema_table= tables->schema_table;
|
ST_SCHEMA_TABLE *schema_table= tables->schema_table;
|
||||||
SELECT_LEX sel;
|
|
||||||
LOOKUP_FIELD_VALUES lookup_field_vals;
|
LOOKUP_FIELD_VALUES lookup_field_vals;
|
||||||
LEX_STRING *db_name, *table_name;
|
LEX_STRING *db_name, *table_name;
|
||||||
bool with_i_schema;
|
bool with_i_schema;
|
||||||
@ -3488,11 +3584,8 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
|
|||||||
List<LEX_STRING> db_names;
|
List<LEX_STRING> db_names;
|
||||||
List_iterator_fast<LEX_STRING> it(db_names);
|
List_iterator_fast<LEX_STRING> it(db_names);
|
||||||
COND *partial_cond= 0;
|
COND *partial_cond= 0;
|
||||||
uint derived_tables= lex->derived_tables;
|
|
||||||
int error= 1;
|
int error= 1;
|
||||||
Open_tables_backup open_tables_state_backup;
|
Open_tables_backup open_tables_state_backup;
|
||||||
uint8 save_context_analysis_only= lex->context_analysis_only;
|
|
||||||
Query_tables_list query_tables_list_backup;
|
|
||||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||||
Security_context *sctx= thd->security_ctx;
|
Security_context *sctx= thd->security_ctx;
|
||||||
#endif
|
#endif
|
||||||
@ -3511,15 +3604,6 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
|
|||||||
*/
|
*/
|
||||||
can_deadlock= thd->mdl_context.has_locks();
|
can_deadlock= thd->mdl_context.has_locks();
|
||||||
|
|
||||||
lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_VIEW;
|
|
||||||
lex->reset_n_backup_query_tables_list(&query_tables_list_backup);
|
|
||||||
/*
|
|
||||||
Restore Query_tables_list::sql_command value, which was reset
|
|
||||||
above, as ST_SCHEMA_TABLE::process_table() functions often rely
|
|
||||||
that this value reflects which SHOW statement is executed.
|
|
||||||
*/
|
|
||||||
lex->sql_command= query_tables_list_backup.sql_command;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
We should not introduce deadlocks even if we already have some
|
We should not introduce deadlocks even if we already have some
|
||||||
tables open and locked, since we won't lock tables which we will
|
tables open and locked, since we won't lock tables which we will
|
||||||
@ -3539,9 +3623,19 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
|
|||||||
*/
|
*/
|
||||||
if (lsel && lsel->table_list.first)
|
if (lsel && lsel->table_list.first)
|
||||||
{
|
{
|
||||||
error= fill_schema_show_cols_or_idxs(thd, tables, schema_table,
|
LEX_STRING db_name, table_name;
|
||||||
can_deadlock,
|
|
||||||
&open_tables_state_backup);
|
db_name.str= lsel->table_list.first->db;
|
||||||
|
db_name.length= lsel->table_list.first->db_length;
|
||||||
|
|
||||||
|
table_name.str= lsel->table_list.first->table_name;
|
||||||
|
table_name.length= lsel->table_list.first->table_name_length;
|
||||||
|
|
||||||
|
error= fill_schema_table_by_open(thd, TRUE,
|
||||||
|
table, schema_table,
|
||||||
|
&db_name, &table_name,
|
||||||
|
&open_tables_state_backup,
|
||||||
|
can_deadlock);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3595,12 +3689,6 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
|
|||||||
it.rewind(); /* To get access to new elements in basis list */
|
it.rewind(); /* To get access to new elements in basis list */
|
||||||
while ((db_name= it++))
|
while ((db_name= it++))
|
||||||
{
|
{
|
||||||
LEX_STRING orig_db_name;
|
|
||||||
|
|
||||||
/* db_name can be changed in make_table_list() func */
|
|
||||||
if (!thd->make_lex_string(&orig_db_name, db_name->str,
|
|
||||||
db_name->length, FALSE))
|
|
||||||
goto err;
|
|
||||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||||
if (!(check_access(thd, SELECT_ACL, db_name->str,
|
if (!(check_access(thd, SELECT_ACL, db_name->str,
|
||||||
&thd->col_access, NULL, 0, 1) ||
|
&thd->col_access, NULL, 0, 1) ||
|
||||||
@ -3678,66 +3766,11 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int res;
|
if (fill_schema_table_by_open(thd, FALSE,
|
||||||
LEX_STRING tmp_lex_string;
|
table, schema_table,
|
||||||
/*
|
db_name, table_name,
|
||||||
Set the parent lex of 'sel' because it is needed by
|
&open_tables_state_backup,
|
||||||
sel.init_query() which is called inside make_table_list.
|
can_deadlock))
|
||||||
*/
|
|
||||||
sel.parent_lex= lex;
|
|
||||||
if (make_table_list(thd, &sel, db_name, table_name))
|
|
||||||
goto err;
|
|
||||||
TABLE_LIST *show_table_list= sel.table_list.first;
|
|
||||||
lex->all_selects_list= &sel;
|
|
||||||
lex->derived_tables= 0;
|
|
||||||
lex->sql_command= SQLCOM_SHOW_FIELDS;
|
|
||||||
show_table_list->i_s_requested_object=
|
|
||||||
schema_table->i_s_requested_object;
|
|
||||||
DEBUG_SYNC(thd, "before_open_in_get_all_tables");
|
|
||||||
res= open_normal_and_derived_tables(thd, show_table_list,
|
|
||||||
(MYSQL_OPEN_IGNORE_FLUSH |
|
|
||||||
MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL |
|
|
||||||
(can_deadlock ? MYSQL_OPEN_FAIL_ON_MDL_CONFLICT : 0)));
|
|
||||||
lex->sql_command= query_tables_list_backup.sql_command;
|
|
||||||
/*
|
|
||||||
XXX: show_table_list has a flag i_is_requested,
|
|
||||||
and when it's set, open_normal_and_derived_tables()
|
|
||||||
can return an error without setting an error message
|
|
||||||
in THD, which is a hack. This is why we have to
|
|
||||||
check for res, then for thd->is_error() only then
|
|
||||||
for thd->stmt_da->sql_errno().
|
|
||||||
*/
|
|
||||||
if (res && thd->is_error() &&
|
|
||||||
thd->stmt_da->sql_errno() == ER_NO_SUCH_TABLE)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
Hide error for not existing table.
|
|
||||||
This error can occur for example when we use
|
|
||||||
where condition with db name and table name and this
|
|
||||||
table does not exist.
|
|
||||||
*/
|
|
||||||
res= 0;
|
|
||||||
thd->clear_error();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
We should use show_table_list->alias instead of
|
|
||||||
show_table_list->table_name because table_name
|
|
||||||
could be changed during opening of I_S tables. It's safe
|
|
||||||
to use alias because alias contains original table name
|
|
||||||
in this case.
|
|
||||||
*/
|
|
||||||
thd->make_lex_string(&tmp_lex_string, show_table_list->alias,
|
|
||||||
strlen(show_table_list->alias), FALSE);
|
|
||||||
res= schema_table->process_table(thd, show_table_list, table,
|
|
||||||
res, &orig_db_name,
|
|
||||||
&tmp_lex_string);
|
|
||||||
close_tables_for_reopen(thd, &show_table_list,
|
|
||||||
open_tables_state_backup.mdl_system_tables_svp);
|
|
||||||
}
|
|
||||||
DBUG_ASSERT(!lex->query_tables_own_last);
|
|
||||||
if (res)
|
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3753,10 +3786,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
|
|||||||
error= 0;
|
error= 0;
|
||||||
err:
|
err:
|
||||||
thd->restore_backup_open_tables_state(&open_tables_state_backup);
|
thd->restore_backup_open_tables_state(&open_tables_state_backup);
|
||||||
lex->restore_backup_query_tables_list(&query_tables_list_backup);
|
|
||||||
lex->derived_tables= derived_tables;
|
|
||||||
lex->all_selects_list= old_all_select_lex;
|
|
||||||
lex->context_analysis_only= save_context_analysis_only;
|
|
||||||
DBUG_RETURN(error);
|
DBUG_RETURN(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user