Commit 615baa9f authored by dlenev@mysql.com's avatar dlenev@mysql.com

Fix for bug #10055 "Using stored function with information_schema causes empty

result set".

To enable full access to contents of I_S tables from stored functions
or statements that use them, we manipulate with thread's open tables
state and ensure that we won't cause deadlock when we open tables by
ignoring flushes and name-locks.
Building of contents of I_S.TABLES no longer requires locking of tables
since we use use handler::info() method with HA_STATUS_AUTO flag instead
of handler::update_auto_increment() for obtaining information about
auto-increment values. But this also means that handlers have to implement
support for HA_STATUS_AUTO flag (particularly InnoDB needs it).
parent 6fd6fa51
...@@ -314,7 +314,7 @@ INSERT INTO t1 VALUES ('localhost','root'),('localhost',''),('games','monty'); ...@@ -314,7 +314,7 @@ INSERT INTO t1 VALUES ('localhost','root'),('localhost',''),('games','monty');
SHOW INDEX FROM t1; SHOW INDEX FROM t1;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment
t1 0 PRIMARY 1 Host A NULL NULL NULL BTREE t1 0 PRIMARY 1 Host A NULL NULL NULL BTREE
t1 0 PRIMARY 2 User A 3 NULL NULL BTREE t1 0 PRIMARY 2 User A 0 NULL NULL BTREE
ALTER TABLE t1 ENABLE KEYS; ALTER TABLE t1 ENABLE KEYS;
UNLOCK TABLES; UNLOCK TABLES;
CHECK TABLES t1; CHECK TABLES t1;
...@@ -338,7 +338,7 @@ INSERT INTO t1 VALUES ('localhost','root'),('localhost',''); ...@@ -338,7 +338,7 @@ INSERT INTO t1 VALUES ('localhost','root'),('localhost','');
SHOW INDEX FROM t1; SHOW INDEX FROM t1;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment
t1 0 PRIMARY 1 Host A NULL NULL NULL BTREE t1 0 PRIMARY 1 Host A NULL NULL NULL BTREE
t1 0 PRIMARY 2 User A 2 NULL NULL BTREE t1 0 PRIMARY 2 User A 0 NULL NULL BTREE
t1 1 Host 1 Host A NULL NULL NULL BTREE disabled t1 1 Host 1 Host A NULL NULL NULL BTREE disabled
ALTER TABLE t1 ENABLE KEYS; ALTER TABLE t1 ENABLE KEYS;
SHOW INDEX FROM t1; SHOW INDEX FROM t1;
......
DROP TABLE IF EXISTS t0,t1,t2; DROP TABLE IF EXISTS t0,t1,t2,t3,t5;
show variables where variable_name like "skip_show_database"; show variables where variable_name like "skip_show_database";
Variable_name Value Variable_name Value
skip_show_database OFF skip_show_database OFF
...@@ -30,6 +30,8 @@ create table mysqltest.t1(a int, b VARCHAR(30), KEY string_data (b)); ...@@ -30,6 +30,8 @@ create table mysqltest.t1(a int, b VARCHAR(30), KEY string_data (b));
create table test.t2(a int); create table test.t2(a int);
create table t3(a int, KEY a_data (a)); create table t3(a int, KEY a_data (a));
create table mysqltest.t4(a int); create table mysqltest.t4(a int);
create table t5 (id int auto_increment primary key);
insert into t5 values (10);
create view v1 (c) as select table_name from information_schema.TABLES; create view v1 (c) as select table_name from information_schema.TABLES;
select * from v1; select * from v1;
c c
...@@ -69,6 +71,7 @@ t1 ...@@ -69,6 +71,7 @@ t1
t4 t4
t2 t2
t3 t3
t5
v1 v1
select c,table_name from v1 select c,table_name from v1
inner join information_schema.TABLES v2 on (v1.c=v2.table_name) inner join information_schema.TABLES v2 on (v1.c=v2.table_name)
...@@ -87,6 +90,7 @@ t1 t1 ...@@ -87,6 +90,7 @@ t1 t1
t4 t4 t4 t4
t2 t2 t2 t2
t3 t3 t3 t3
t5 t5
select c,table_name from v1 select c,table_name from v1
left join information_schema.TABLES v2 on (v1.c=v2.table_name) left join information_schema.TABLES v2 on (v1.c=v2.table_name)
where v1.c like "t%"; where v1.c like "t%";
...@@ -104,6 +108,7 @@ t1 t1 ...@@ -104,6 +108,7 @@ t1 t1
t4 t4 t4 t4
t2 t2 t2 t2
t3 t3 t3 t3
t5 t5
select c, v2.table_name from v1 select c, v2.table_name from v1
right join information_schema.TABLES v2 on (v1.c=v2.table_name) right join information_schema.TABLES v2 on (v1.c=v2.table_name)
where v1.c like "t%"; where v1.c like "t%";
...@@ -121,6 +126,7 @@ t1 t1 ...@@ -121,6 +126,7 @@ t1 t1
t4 t4 t4 t4
t2 t2 t2 t2
t3 t3 t3 t3
t5 t5
select table_name from information_schema.TABLES select table_name from information_schema.TABLES
where table_schema = "mysqltest" and table_name like "t%"; where table_schema = "mysqltest" and table_name like "t%";
table_name table_name
...@@ -136,10 +142,12 @@ show tables like 't%'; ...@@ -136,10 +142,12 @@ show tables like 't%';
Tables_in_test (t%) Tables_in_test (t%)
t2 t2
t3 t3
t5
show table status; show table status;
Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment
t2 MyISAM 10 Fixed 0 0 0 # 1024 0 NULL # # NULL latin1_swedish_ci NULL t2 MyISAM 10 Fixed 0 0 0 # 1024 0 NULL # # NULL latin1_swedish_ci NULL
t3 MyISAM 10 Fixed 0 0 0 # 1024 0 NULL # # NULL latin1_swedish_ci NULL t3 MyISAM 10 Fixed 0 0 0 # 1024 0 NULL # # NULL latin1_swedish_ci NULL
t5 MyISAM 10 Fixed 1 7 7 # 2048 0 11 # # NULL latin1_swedish_ci NULL
v1 NULL NULL NULL NULL NULL NULL # NULL NULL NULL # # NULL NULL NULL NULL view v1 NULL NULL NULL NULL NULL NULL # NULL NULL NULL # # NULL NULL NULL NULL view
show full columns from t3 like "a%"; show full columns from t3 like "a%";
Field Type Collation Null Key Default Extra Privileges Comment Field Type Collation Null Key Default Extra Privileges Comment
...@@ -173,7 +181,7 @@ where table_schema = 'mysqltest' and table_name = 'v1'; ...@@ -173,7 +181,7 @@ where table_schema = 'mysqltest' and table_name = 'v1';
table_name column_name privileges table_name column_name privileges
v1 c select v1 c select
drop view v1, mysqltest.v1; drop view v1, mysqltest.v1;
drop tables mysqltest.t4, mysqltest.t1, t2, t3; drop tables mysqltest.t4, mysqltest.t1, t2, t3, t5;
drop database mysqltest; drop database mysqltest;
select * from information_schema.CHARACTER_SETS select * from information_schema.CHARACTER_SETS
where CHARACTER_SET_NAME like 'latin1%'; where CHARACTER_SET_NAME like 'latin1%';
......
...@@ -3062,4 +3062,13 @@ l ...@@ -3062,4 +3062,13 @@ l
drop procedure bug6063| drop procedure bug6063|
drop procedure bug7088_1| drop procedure bug7088_1|
drop procedure bug7088_2| drop procedure bug7088_2|
drop function if exists bug10055|
create function bug10055(v char(255)) returns char(255) return lower(v)|
select t.column_name, bug10055(t.column_name)
from information_schema.columns as t
where t.table_schema = 'test' and t.table_name = 't1'|
column_name bug10055(t.column_name)
id id
data data
drop function bug10055|
drop table t1,t2; drop table t1,t2;
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
# show databases # show databases
--disable_warnings --disable_warnings
DROP TABLE IF EXISTS t0,t1,t2; DROP TABLE IF EXISTS t0,t1,t2,t3,t5;
--enable_warnings --enable_warnings
...@@ -30,6 +30,8 @@ create table mysqltest.t1(a int, b VARCHAR(30), KEY string_data (b)); ...@@ -30,6 +30,8 @@ create table mysqltest.t1(a int, b VARCHAR(30), KEY string_data (b));
create table test.t2(a int); create table test.t2(a int);
create table t3(a int, KEY a_data (a)); create table t3(a int, KEY a_data (a));
create table mysqltest.t4(a int); create table mysqltest.t4(a int);
create table t5 (id int auto_increment primary key);
insert into t5 values (10);
create view v1 (c) as select table_name from information_schema.TABLES; create view v1 (c) as select table_name from information_schema.TABLES;
select * from v1; select * from v1;
...@@ -76,7 +78,7 @@ where table_schema = 'mysqltest' and table_name = 'v1'; ...@@ -76,7 +78,7 @@ where table_schema = 'mysqltest' and table_name = 'v1';
connection default; connection default;
drop view v1, mysqltest.v1; drop view v1, mysqltest.v1;
drop tables mysqltest.t4, mysqltest.t1, t2, t3; drop tables mysqltest.t4, mysqltest.t1, t2, t3, t5;
drop database mysqltest; drop database mysqltest;
# Test for information_schema.CHARACTER_SETS & # Test for information_schema.CHARACTER_SETS &
......
...@@ -3833,6 +3833,21 @@ drop procedure bug7088_1| ...@@ -3833,6 +3833,21 @@ drop procedure bug7088_1|
drop procedure bug7088_2| drop procedure bug7088_2|
#
# Bug#10055 "Using stored function with information_schema causes empty
# result set"
#
--disable_warnings
drop function if exists bug10055|
--enable_warnings
create function bug10055(v char(255)) returns char(255) return lower(v)|
# This select should not crash server and should return all fields in t1
select t.column_name, bug10055(t.column_name)
from information_schema.columns as t
where t.table_schema = 'test' and t.table_name = 't1'|
drop function bug10055|
# #
# BUG#NNNN: New bug synopsis # BUG#NNNN: New bug synopsis
# #
......
...@@ -482,8 +482,7 @@ typedef Comp_creator* (*chooser_compare_func_creator)(bool invert); ...@@ -482,8 +482,7 @@ typedef Comp_creator* (*chooser_compare_func_creator)(bool invert);
void free_items(Item *item); void free_items(Item *item);
void cleanup_items(Item *item); void cleanup_items(Item *item);
class THD; class THD;
void close_thread_tables(THD *thd, bool locked=0, bool skip_derived=0, void close_thread_tables(THD *thd, bool locked=0, bool skip_derived=0);
TABLE *stopper= 0);
bool check_one_table_access(THD *thd, ulong privilege, bool check_one_table_access(THD *thd, ulong privilege,
TABLE_LIST *tables); TABLE_LIST *tables);
bool check_routine_access(THD *thd,ulong want_access,char *db,char *name, bool check_routine_access(THD *thd,ulong want_access,char *db,char *name,
...@@ -924,10 +923,10 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, ...@@ -924,10 +923,10 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves,
int setup_ftfuncs(SELECT_LEX* select); int setup_ftfuncs(SELECT_LEX* select);
int init_ftfuncs(THD *thd, SELECT_LEX* select, bool no_order); int init_ftfuncs(THD *thd, SELECT_LEX* select, bool no_order);
void wait_for_refresh(THD *thd); 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, uint flags);
int simple_open_n_lock_tables(THD *thd,TABLE_LIST *tables); int simple_open_n_lock_tables(THD *thd,TABLE_LIST *tables);
bool open_and_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); bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags);
int lock_tables(THD *thd, TABLE_LIST *tables, uint counter); int lock_tables(THD *thd, TABLE_LIST *tables, uint counter);
TABLE *open_temporary_table(THD *thd, const char *path, const char *db, TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
const char *table_name, bool link_in_list); const char *table_name, bool link_in_list);
......
...@@ -69,12 +69,15 @@ bool mysql_proc_table_exists= 1; ...@@ -69,12 +69,15 @@ bool mysql_proc_table_exists= 1;
SYNOPSIS SYNOPSIS
close_proc_table() close_proc_table()
thd Thread context thd Thread context
backup Pointer to Open_tables_state instance which holds
information about tables which were open before we
decided to access mysql.proc.
*/ */
static void close_proc_table(THD *thd) void close_proc_table(THD *thd, Open_tables_state *backup)
{ {
close_thread_tables(thd); close_thread_tables(thd);
thd->pop_open_tables_state(); thd->restore_backup_open_tables_state(backup);
} }
...@@ -84,6 +87,9 @@ static void close_proc_table(THD *thd) ...@@ -84,6 +87,9 @@ static void close_proc_table(THD *thd)
SYNOPSIS SYNOPSIS
open_proc_table_for_read() open_proc_table_for_read()
thd Thread context thd Thread context
backup Pointer to Open_tables_state instance where information about
currently open tables will be saved, and from which will be
restored when we will end work with mysql.proc.
NOTES NOTES
Thanks to restrictions which we put on opening and locking of Thanks to restrictions which we put on opening and locking of
...@@ -97,11 +103,10 @@ static void close_proc_table(THD *thd) ...@@ -97,11 +103,10 @@ static void close_proc_table(THD *thd)
# Pointer to TABLE object of mysql.proc # Pointer to TABLE object of mysql.proc
*/ */
static TABLE *open_proc_table_for_read(THD *thd) TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup)
{ {
TABLE_LIST tables; TABLE_LIST tables;
TABLE *table; TABLE *table;
bool old_open_tables= thd->open_tables != 0;
bool refresh; bool refresh;
DBUG_ENTER("open_proc_table"); DBUG_ENTER("open_proc_table");
...@@ -112,8 +117,7 @@ static TABLE *open_proc_table_for_read(THD *thd) ...@@ -112,8 +117,7 @@ static TABLE *open_proc_table_for_read(THD *thd)
if (!mysql_proc_table_exists) if (!mysql_proc_table_exists)
DBUG_RETURN(0); DBUG_RETURN(0);
if (thd->push_open_tables_state()) thd->reset_n_backup_open_tables_state(backup);
DBUG_RETURN(0);
bzero((char*) &tables, sizeof(tables)); bzero((char*) &tables, sizeof(tables));
tables.db= (char*) "mysql"; tables.db= (char*) "mysql";
...@@ -121,7 +125,7 @@ static TABLE *open_proc_table_for_read(THD *thd) ...@@ -121,7 +125,7 @@ static TABLE *open_proc_table_for_read(THD *thd)
if (!(table= open_table(thd, &tables, thd->mem_root, &refresh, if (!(table= open_table(thd, &tables, thd->mem_root, &refresh,
MYSQL_LOCK_IGNORE_FLUSH))) MYSQL_LOCK_IGNORE_FLUSH)))
{ {
thd->pop_open_tables_state(); thd->restore_backup_open_tables_state(backup);
mysql_proc_table_exists= 0; mysql_proc_table_exists= 0;
DBUG_RETURN(0); DBUG_RETURN(0);
} }
...@@ -130,15 +134,13 @@ static TABLE *open_proc_table_for_read(THD *thd) ...@@ -130,15 +134,13 @@ static TABLE *open_proc_table_for_read(THD *thd)
table->reginfo.lock_type= TL_READ; table->reginfo.lock_type= TL_READ;
/* /*
If we have other tables opened, we have to ensure we are not blocked We have to ensure we are not blocked by a flush tables, as this
by a flush tables or global read lock, as this could lead to a deadlock could lead to a deadlock if we have other tables opened.
*/ */
if (!(thd->lock= mysql_lock_tables(thd, &table, 1, if (!(thd->lock= mysql_lock_tables(thd, &table, 1,
old_open_tables ? MYSQL_LOCK_IGNORE_FLUSH)))
(MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK |
MYSQL_LOCK_IGNORE_FLUSH) : 0)))
{ {
close_proc_table(thd); close_proc_table(thd, backup);
DBUG_RETURN(0); DBUG_RETURN(0);
} }
DBUG_RETURN(table); DBUG_RETURN(table);
...@@ -271,12 +273,13 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) ...@@ -271,12 +273,13 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp)
char buff[65]; char buff[65];
String str(buff, sizeof(buff), &my_charset_bin); String str(buff, sizeof(buff), &my_charset_bin);
ulong sql_mode; ulong sql_mode;
Open_tables_state open_tables_state_backup;
DBUG_ENTER("db_find_routine"); DBUG_ENTER("db_find_routine");
DBUG_PRINT("enter", ("type: %d name: %*s", DBUG_PRINT("enter", ("type: %d name: %*s",
type, name->m_name.length, name->m_name.str)); type, name->m_name.length, name->m_name.str));
*sphp= 0; // In case of errors *sphp= 0; // In case of errors
if (!(table= open_proc_table_for_read(thd))) if (!(table= open_proc_table_for_read(thd, &open_tables_state_backup)))
DBUG_RETURN(SP_OPEN_TABLE_FAILED); DBUG_RETURN(SP_OPEN_TABLE_FAILED);
if ((ret= db_find_routine_aux(thd, type, name, table)) != SP_OK) if ((ret= db_find_routine_aux(thd, type, name, table)) != SP_OK)
...@@ -371,7 +374,7 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) ...@@ -371,7 +374,7 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp)
chistics.comment.str= ptr; chistics.comment.str= ptr;
chistics.comment.length= length; chistics.comment.length= length;
close_proc_table(thd); close_proc_table(thd, &open_tables_state_backup);
table= 0; table= 0;
{ {
...@@ -449,7 +452,7 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) ...@@ -449,7 +452,7 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp)
done: done:
if (table) if (table)
close_proc_table(thd); close_proc_table(thd, &open_tables_state_backup);
DBUG_RETURN(ret); DBUG_RETURN(ret);
} }
......
...@@ -90,6 +90,13 @@ void sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex, ...@@ -90,6 +90,13 @@ void sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
extern "C" byte* sp_sroutine_key(const byte *ptr, uint *plen, my_bool first); extern "C" byte* sp_sroutine_key(const byte *ptr, uint *plen, my_bool first);
/*
Routines which allow open/lock and close mysql.proc table even when
we already have some tables open and locked.
*/
TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup);
void close_proc_table(THD *thd, Open_tables_state *backup);
// //
// Utilities... // Utilities...
// //
......
...@@ -401,8 +401,7 @@ static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table) ...@@ -401,8 +401,7 @@ static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table)
upper level) and will leave prelocked mode if needed. upper level) and will leave prelocked mode if needed.
*/ */
void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived, void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived)
TABLE *stopper)
{ {
bool found_old_table; bool found_old_table;
prelocked_mode_type prelocked_mode= thd->prelocked_mode; prelocked_mode_type prelocked_mode= thd->prelocked_mode;
...@@ -508,7 +507,7 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived, ...@@ -508,7 +507,7 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived,
DBUG_PRINT("info", ("thd->open_tables: %p", thd->open_tables)); DBUG_PRINT("info", ("thd->open_tables: %p", thd->open_tables));
found_old_table= 0; found_old_table= 0;
while (thd->open_tables != stopper) while (thd->open_tables)
found_old_table|=close_thread_table(thd, &thd->open_tables); found_old_table|=close_thread_table(thd, &thd->open_tables);
thd->some_tables_deleted=0; thd->some_tables_deleted=0;
...@@ -1771,6 +1770,9 @@ err: ...@@ -1771,6 +1770,9 @@ err:
thd - thread handler thd - thread handler
start - list of tables in/out start - list of tables in/out
counter - number of opened tables will be return using this parameter 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.
NOTE NOTE
Unless we are already in prelocked mode, this function will also precache Unless we are already in prelocked mode, this function will also precache
...@@ -1788,7 +1790,7 @@ err: ...@@ -1788,7 +1790,7 @@ err:
-1 - error -1 - error
*/ */
int open_tables(THD *thd, TABLE_LIST **start, uint *counter) int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
{ {
TABLE_LIST *tables; TABLE_LIST *tables;
bool refresh; bool refresh;
...@@ -1863,7 +1865,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter) ...@@ -1863,7 +1865,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter)
} }
(*counter)++; (*counter)++;
if (!tables->table && if (!tables->table &&
!(tables->table= open_table(thd, tables, &new_frm_mem, &refresh, 0))) !(tables->table= open_table(thd, tables, &new_frm_mem, &refresh, flags)))
{ {
free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC)); free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC));
if (tables->view) if (tables->view)
...@@ -2089,7 +2091,8 @@ int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables) ...@@ -2089,7 +2091,8 @@ int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables)
{ {
DBUG_ENTER("simple_open_n_lock_tables"); DBUG_ENTER("simple_open_n_lock_tables");
uint counter; uint counter;
if (open_tables(thd, &tables, &counter) || lock_tables(thd, tables, counter)) if (open_tables(thd, &tables, &counter, 0) ||
lock_tables(thd, tables, counter))
DBUG_RETURN(-1); /* purecov: inspected */ DBUG_RETURN(-1); /* purecov: inspected */
DBUG_RETURN(0); DBUG_RETURN(0);
} }
...@@ -2116,7 +2119,7 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables) ...@@ -2116,7 +2119,7 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables)
{ {
uint counter; uint counter;
DBUG_ENTER("open_and_lock_tables"); DBUG_ENTER("open_and_lock_tables");
if (open_tables(thd, &tables, &counter) || if (open_tables(thd, &tables, &counter, 0) ||
lock_tables(thd, tables, counter) || lock_tables(thd, tables, counter) ||
mysql_handle_derived(thd->lex, &mysql_derived_prepare) || mysql_handle_derived(thd->lex, &mysql_derived_prepare) ||
(thd->fill_derived_tables() && (thd->fill_derived_tables() &&
...@@ -2133,6 +2136,9 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables) ...@@ -2133,6 +2136,9 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables)
open_normal_and_derived_tables open_normal_and_derived_tables
thd - thread handler thd - thread handler
tables - list of tables for open tables - list of tables for open
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.
RETURN RETURN
FALSE - ok FALSE - ok
...@@ -2143,12 +2149,12 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables) ...@@ -2143,12 +2149,12 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables)
data from the tables. data from the tables.
*/ */
bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables) bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags)
{ {
uint counter; uint counter;
DBUG_ENTER("open_normal_and_derived_tables"); DBUG_ENTER("open_normal_and_derived_tables");
DBUG_ASSERT(!thd->fill_derived_tables()); DBUG_ASSERT(!thd->fill_derived_tables());
if (open_tables(thd, &tables, &counter) || if (open_tables(thd, &tables, &counter, flags) ||
mysql_handle_derived(thd->lex, &mysql_derived_prepare)) mysql_handle_derived(thd->lex, &mysql_derived_prepare))
DBUG_RETURN(TRUE); /* purecov: inspected */ DBUG_RETURN(TRUE); /* purecov: inspected */
DBUG_RETURN(0); DBUG_RETURN(0);
......
...@@ -157,8 +157,8 @@ bool foreign_key_prefix(Key *a, Key *b) ...@@ -157,8 +157,8 @@ bool foreign_key_prefix(Key *a, Key *b)
** Thread specific functions ** Thread specific functions
****************************************************************************/ ****************************************************************************/
Open_tables_state::Open_tables_state() Open_tables_state::Open_tables_state(ulong version_arg)
:version(refresh_version) :version(version_arg)
{ {
reset_open_tables_state(); reset_open_tables_state();
} }
...@@ -172,7 +172,7 @@ Open_tables_state::Open_tables_state() ...@@ -172,7 +172,7 @@ Open_tables_state::Open_tables_state()
THD::THD() THD::THD()
:Statement(CONVENTIONAL_EXECUTION, 0, ALLOC_ROOT_MIN_BLOCK_SIZE, 0), :Statement(CONVENTIONAL_EXECUTION, 0, ALLOC_ROOT_MIN_BLOCK_SIZE, 0),
Open_tables_state(), Open_tables_state(refresh_version),
user_time(0), global_read_lock(0), is_fatal_error(0), user_time(0), global_read_lock(0), is_fatal_error(0),
rand_used(0), time_zone_used(0), rand_used(0), time_zone_used(0),
last_insert_id_used(0), insert_id_used(0), clear_next_insert_id(0), last_insert_id_used(0), insert_id_used(0), clear_next_insert_id(0),
...@@ -1789,31 +1789,26 @@ void THD::set_status_var_init() ...@@ -1789,31 +1789,26 @@ void THD::set_status_var_init()
access to mysql.proc table to find definitions of stored routines. access to mysql.proc table to find definitions of stored routines.
****************************************************************************/ ****************************************************************************/
bool THD::push_open_tables_state() void THD::reset_n_backup_open_tables_state(Open_tables_state *backup)
{ {
Open_tables_state *state; DBUG_ENTER("reset_n_backup_open_tables_state");
DBUG_ENTER("push_open_table_state"); backup->set_open_tables_state(this);
/* Currently we only push things one level */
DBUG_ASSERT(open_state_list.elements == 0);
if (!(state= (Open_tables_state*) alloc(sizeof(*state))))
DBUG_RETURN(1); // Fatal error is set
/* Store state for currently open tables */
state->set_open_tables_state(this);
if (open_state_list.push_back(state, mem_root))
DBUG_RETURN(1); // Fatal error is set
reset_open_tables_state(); reset_open_tables_state();
DBUG_RETURN(0); DBUG_VOID_RETURN;
} }
void THD::pop_open_tables_state()
{
Open_tables_state *state;
DBUG_ENTER("pop_open_table_state");
/* Currently we only push things one level */
DBUG_ASSERT(open_state_list.elements == 1);
state= open_state_list.pop(); void THD::restore_backup_open_tables_state(Open_tables_state *backup)
set_open_tables_state(state); {
DBUG_ENTER("restore_backup_open_tables_state");
/*
Before we will throw away current open tables state we want
to be sure that it was properly cleaned up.
*/
DBUG_ASSERT(open_tables == 0 && temporary_tables == 0 &&
handler_tables == 0 && derived_tables == 0 &&
lock == 0 && locked_tables == 0 &&
prelocked_mode == NON_PRELOCKED);
set_open_tables_state(backup);
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
...@@ -996,7 +996,13 @@ public: ...@@ -996,7 +996,13 @@ public:
ulong version; ulong version;
uint current_tablenr; uint current_tablenr;
Open_tables_state(); /*
This constructor serves for creation of Open_tables_state instances
which are used as backup storage.
*/
Open_tables_state() {};
Open_tables_state(ulong version_arg);
void set_open_tables_state(Open_tables_state *state) void set_open_tables_state(Open_tables_state *state)
{ {
...@@ -1203,7 +1209,6 @@ public: ...@@ -1203,7 +1209,6 @@ public:
List <MYSQL_ERROR> warn_list; List <MYSQL_ERROR> warn_list;
uint warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_END]; uint warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_END];
uint total_warn_count; uint total_warn_count;
List <Open_tables_state> open_state_list;
/* /*
Id of current query. Statement can be reused to execute several queries Id of current query. Statement can be reused to execute several queries
query_id is global in context of the whole MySQL server. query_id is global in context of the whole MySQL server.
...@@ -1457,8 +1462,8 @@ public: ...@@ -1457,8 +1462,8 @@ public:
(variables.sql_mode & MODE_STRICT_ALL_TABLES))); (variables.sql_mode & MODE_STRICT_ALL_TABLES)));
} }
void set_status_var_init(); void set_status_var_init();
bool push_open_tables_state(); void reset_n_backup_open_tables_state(Open_tables_state *backup);
void pop_open_tables_state(); void restore_backup_open_tables_state(Open_tables_state *backup);
}; };
......
...@@ -187,7 +187,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) ...@@ -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 */ /* for now HANDLER can be used only for real TABLES */
tables->required_type= FRMTYPE_TABLE; tables->required_type= FRMTYPE_TABLE;
error= open_tables(thd, &tables, &counter); error= open_tables(thd, &tables, &counter, 0);
HANDLER_TABLES_HACK(thd); HANDLER_TABLES_HACK(thd);
if (error) if (error)
......
...@@ -925,7 +925,7 @@ static bool mysql_test_insert(Prepared_statement *stmt, ...@@ -925,7 +925,7 @@ static bool mysql_test_insert(Prepared_statement *stmt,
If we would use locks, then we have to ensure we are not using If we would use locks, then we have to ensure we are not using
TL_WRITE_DELAYED as having two such locks can cause table corruption. TL_WRITE_DELAYED as having two such locks can cause table corruption.
*/ */
if (open_normal_and_derived_tables(thd, table_list)) if (open_normal_and_derived_tables(thd, table_list, 0))
goto error; goto error;
if ((values= its++)) if ((values= its++))
...@@ -1005,7 +1005,7 @@ static int mysql_test_update(Prepared_statement *stmt, ...@@ -1005,7 +1005,7 @@ static int mysql_test_update(Prepared_statement *stmt,
if (update_precheck(thd, table_list)) if (update_precheck(thd, table_list))
goto error; goto error;
if (open_tables(thd, &table_list, &table_count)) if (open_tables(thd, &table_list, &table_count, 0))
goto error; goto error;
if (table_list->multitable_view) if (table_list->multitable_view)
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "mysql_priv.h" #include "mysql_priv.h"
#include "sql_select.h" // For select_describe #include "sql_select.h" // For select_describe
#include "repl_failsafe.h" #include "repl_failsafe.h"
#include "sp.h"
#include "sp_head.h" #include "sp_head.h"
#include <my_dir.h> #include <my_dir.h>
...@@ -351,7 +352,7 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) ...@@ -351,7 +352,7 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
thd->lex->view_prepare_mode= TRUE; thd->lex->view_prepare_mode= TRUE;
/* Only one table for now, but VIEW can involve several tables */ /* Only one table for now, but VIEW can involve several tables */
if (open_normal_and_derived_tables(thd, table_list)) if (open_normal_and_derived_tables(thd, table_list, 0))
{ {
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
} }
...@@ -550,7 +551,7 @@ mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild) ...@@ -550,7 +551,7 @@ mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild)
DBUG_ENTER("mysqld_list_fields"); DBUG_ENTER("mysqld_list_fields");
DBUG_PRINT("enter",("table: %s",table_list->table_name)); DBUG_PRINT("enter",("table: %s",table_list->table_name));
if (open_normal_and_derived_tables(thd, table_list)) if (open_normal_and_derived_tables(thd, table_list, 0))
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
table= table_list->table; table= table_list->table;
...@@ -1934,6 +1935,8 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) ...@@ -1934,6 +1935,8 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
TABLE *table= tables->table; TABLE *table= tables->table;
SELECT_LEX *select_lex= &lex->select_lex; SELECT_LEX *select_lex= &lex->select_lex;
SELECT_LEX *old_all_select_lex= lex->all_selects_list; SELECT_LEX *old_all_select_lex= lex->all_selects_list;
TABLE_LIST **save_query_tables_last= lex->query_tables_last;
enum_sql_command save_sql_command= lex->sql_command;
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; SELECT_LEX sel;
...@@ -1942,40 +1945,49 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) ...@@ -1942,40 +1945,49 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
uint len; uint len;
bool with_i_schema; bool with_i_schema;
enum enum_schema_tables schema_table_idx; enum enum_schema_tables schema_table_idx;
thr_lock_type lock_type;
List<char> bases; List<char> bases;
List_iterator_fast<char> it(bases); List_iterator_fast<char> it(bases);
COND *partial_cond; COND *partial_cond;
uint derived_tables= lex->derived_tables; uint derived_tables= lex->derived_tables;
int error= 1; int error= 1;
Open_tables_state open_tables_state_backup;
DBUG_ENTER("get_all_tables"); DBUG_ENTER("get_all_tables");
LINT_INIT(end); LINT_INIT(end);
LINT_INIT(len); LINT_INIT(len);
/*
Let us set fake sql_command so views won't try to merge
themselves into main statement.
*/
lex->sql_command= SQLCOM_SHOW_FIELDS;
/*
We should not introduce deadlocks even if we already have some
tables open and locked, since we won't lock tables which we will
open and will ignore possible name-locks for these tables.
*/
thd->reset_n_backup_open_tables_state(&open_tables_state_backup);
if (lsel) if (lsel)
{ {
TABLE *old_open_tables= thd->open_tables;
TABLE_LIST *show_table_list= (TABLE_LIST*) lsel->table_list.first; TABLE_LIST *show_table_list= (TABLE_LIST*) lsel->table_list.first;
bool res; bool res;
lex->all_selects_list= lsel; lex->all_selects_list= lsel;
res= open_normal_and_derived_tables(thd, show_table_list); res= open_normal_and_derived_tables(thd, show_table_list,
MYSQL_LOCK_IGNORE_FLUSH);
if (schema_table->process_table(thd, show_table_list, if (schema_table->process_table(thd, show_table_list,
table, res, show_table_list->db, table, res, show_table_list->db,
show_table_list->alias)) show_table_list->alias))
goto err; goto err;
close_thread_tables(thd, 0, 0, old_open_tables); close_thread_tables(thd);
show_table_list->table= 0; show_table_list->table= 0;
error= 0; error= 0;
goto err; goto err;
} }
schema_table_idx= get_schema_table_idx(schema_table); schema_table_idx= get_schema_table_idx(schema_table);
lock_type= TL_UNLOCK;
if (schema_table_idx == SCH_TABLES)
lock_type= TL_READ;
if (make_db_list(thd, &bases, &idx_field_vals, if (make_db_list(thd, &bases, &idx_field_vals,
&with_i_schema, 0)) &with_i_schema, 0))
...@@ -2058,19 +2070,18 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) ...@@ -2058,19 +2070,18 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
else else
{ {
int res; int res;
TABLE *old_open_tables= thd->open_tables;
if (make_table_list(thd, &sel, base_name, file_name)) if (make_table_list(thd, &sel, base_name, file_name))
goto err; goto err;
TABLE_LIST *show_table_list= (TABLE_LIST*) sel.table_list.first; TABLE_LIST *show_table_list= (TABLE_LIST*) sel.table_list.first;
show_table_list->lock_type= lock_type;
lex->all_selects_list= &sel; lex->all_selects_list= &sel;
lex->derived_tables= 0; lex->derived_tables= 0;
res= open_normal_and_derived_tables(thd, show_table_list); res= open_normal_and_derived_tables(thd, show_table_list,
MYSQL_LOCK_IGNORE_FLUSH);
if (schema_table->process_table(thd, show_table_list, table, if (schema_table->process_table(thd, show_table_list, table,
res, base_name, res, base_name,
show_table_list->alias)) show_table_list->alias))
goto err; goto err;
close_thread_tables(thd, 0, 0, old_open_tables); close_thread_tables(thd);
} }
} }
} }
...@@ -2084,8 +2095,12 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) ...@@ -2084,8 +2095,12 @@ 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);
lex->derived_tables= derived_tables; lex->derived_tables= derived_tables;
lex->all_selects_list= old_all_select_lex; lex->all_selects_list= old_all_select_lex;
lex->query_tables_last= save_query_tables_last;
*save_query_tables_last= 0;
lex->sql_command= save_sql_command;
DBUG_RETURN(error); DBUG_RETURN(error);
} }
...@@ -2190,7 +2205,8 @@ static int get_schema_tables_record(THD *thd, struct st_table_list *tables, ...@@ -2190,7 +2205,8 @@ static int get_schema_tables_record(THD *thd, struct st_table_list *tables,
TABLE_SHARE *share= show_table->s; TABLE_SHARE *share= show_table->s;
handler *file= show_table->file; handler *file= show_table->file;
file->info(HA_STATUS_VARIABLE | HA_STATUS_TIME | HA_STATUS_NO_LOCK); file->info(HA_STATUS_VARIABLE | HA_STATUS_TIME | HA_STATUS_AUTO |
HA_STATUS_NO_LOCK);
if (share->tmp_table == TMP_TABLE) if (share->tmp_table == TMP_TABLE)
table->field[3]->store("TEMPORARY", 9, cs); table->field[3]->store("TEMPORARY", 9, cs);
else else
...@@ -2246,13 +2262,8 @@ static int get_schema_tables_record(THD *thd, struct st_table_list *tables, ...@@ -2246,13 +2262,8 @@ static int get_schema_tables_record(THD *thd, struct st_table_list *tables,
table->field[12]->store((longlong) file->delete_length); table->field[12]->store((longlong) file->delete_length);
if (show_table->found_next_number_field) if (show_table->found_next_number_field)
{ {
show_table->next_number_field=show_table->found_next_number_field; table->field[13]->store((longlong) file->auto_increment_value);
show_table->next_number_field->reset();
file->update_auto_increment();
table->field[13]->store((longlong) show_table->
next_number_field->val_int());
table->field[13]->set_notnull(); table->field[13]->set_notnull();
show_table->next_number_field=0;
} }
if (file->create_time) if (file->create_time)
{ {
...@@ -2711,12 +2722,14 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond) ...@@ -2711,12 +2722,14 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond)
TABLE_LIST proc_tables; TABLE_LIST proc_tables;
const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS; const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS;
int res= 0; int res= 0;
TABLE *table= tables->table, *old_open_tables= thd->open_tables; TABLE *table= tables->table;
bool full_access; bool full_access;
char definer[HOSTNAME_LENGTH+USERNAME_LENGTH+2]; char definer[HOSTNAME_LENGTH+USERNAME_LENGTH+2];
Open_tables_state open_tables_state_backup;
DBUG_ENTER("fill_schema_proc"); DBUG_ENTER("fill_schema_proc");
strxmov(definer, thd->priv_user, "@", thd->priv_host, NullS); strxmov(definer, thd->priv_user, "@", thd->priv_host, NullS);
/* We use this TABLE_LIST instance only for checking of privileges. */
bzero((char*) &proc_tables,sizeof(proc_tables)); bzero((char*) &proc_tables,sizeof(proc_tables));
proc_tables.db= (char*) "mysql"; proc_tables.db= (char*) "mysql";
proc_tables.db_length= 5; proc_tables.db_length= 5;
...@@ -2724,7 +2737,7 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond) ...@@ -2724,7 +2737,7 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond)
proc_tables.table_name_length= 4; proc_tables.table_name_length= 4;
proc_tables.lock_type= TL_READ; proc_tables.lock_type= TL_READ;
full_access= !check_table_access(thd, SELECT_ACL, &proc_tables, 1); full_access= !check_table_access(thd, SELECT_ACL, &proc_tables, 1);
if (!(proc_table= open_ltable(thd, &proc_tables, TL_READ))) if (!(proc_table= open_proc_table_for_read(thd, &open_tables_state_backup)))
{ {
DBUG_RETURN(1); DBUG_RETURN(1);
} }
...@@ -2750,7 +2763,7 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond) ...@@ -2750,7 +2763,7 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond)
err: err:
proc_table->file->ha_index_end(); proc_table->file->ha_index_end();
close_thread_tables(thd, 0, 0, old_open_tables); close_proc_table(thd, &open_tables_state_backup);
DBUG_RETURN(res); DBUG_RETURN(res);
} }
...@@ -3567,11 +3580,6 @@ bool get_schema_tables_result(JOIN *join) ...@@ -3567,11 +3580,6 @@ bool get_schema_tables_result(JOIN *join)
TABLE_LIST *table_list= tab->table->pos_in_table_list; TABLE_LIST *table_list= tab->table->pos_in_table_list;
if (table_list->schema_table && thd->fill_derived_tables()) if (table_list->schema_table && thd->fill_derived_tables())
{ {
TABLE_LIST **query_tables_last= lex->query_tables_last;
TABLE *old_derived_tables= thd->derived_tables;
MYSQL_LOCK *sql_lock= thd->lock;
lex->sql_command= SQLCOM_SHOW_FIELDS;
DBUG_ASSERT(!*query_tables_last);
if (&lex->unit != lex->current_select->master_unit()) // is subselect if (&lex->unit != lex->current_select->master_unit()) // is subselect
{ {
table_list->table->file->extra(HA_EXTRA_RESET_STATE); table_list->table->file->extra(HA_EXTRA_RESET_STATE);
...@@ -3582,16 +3590,9 @@ bool get_schema_tables_result(JOIN *join) ...@@ -3582,16 +3590,9 @@ bool get_schema_tables_result(JOIN *join)
else else
table_list->table->file->records= 0; table_list->table->file->records= 0;
thd->derived_tables= 0;
thd->lock=0;
if (table_list->schema_table->fill_table(thd, table_list, if (table_list->schema_table->fill_table(thd, table_list,
tab->select_cond)) tab->select_cond))
result= 1; result= 1;
thd->lock= sql_lock;
lex->sql_command= SQLCOM_SELECT;
thd->derived_tables= old_derived_tables;
lex->query_tables_last= query_tables_last;
*query_tables_last= 0;
} }
} }
thd->no_warnings_for_error= 0; thd->no_warnings_for_error= 0;
......
...@@ -138,7 +138,7 @@ int mysql_update(THD *thd, ...@@ -138,7 +138,7 @@ int mysql_update(THD *thd,
LINT_INIT(timestamp_query_id); LINT_INIT(timestamp_query_id);
if (open_tables(thd, &table_list, &table_count)) if (open_tables(thd, &table_list, &table_count, 0))
DBUG_RETURN(1); DBUG_RETURN(1);
if (table_list->multitable_view) if (table_list->multitable_view)
...@@ -632,7 +632,8 @@ bool mysql_multi_update_prepare(THD *thd) ...@@ -632,7 +632,8 @@ bool mysql_multi_update_prepare(THD *thd)
thd->lex->sql_command= SQLCOM_UPDATE_MULTI; thd->lex->sql_command= SQLCOM_UPDATE_MULTI;
/* open tables and create derived ones, but do not lock and fill them */ /* 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, 0)) ||
mysql_handle_derived(lex, &mysql_derived_prepare)) mysql_handle_derived(lex, &mysql_derived_prepare))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
/* /*
......
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