Commit c70dbeab authored by gshchepa@bk-internal.mysql.com's avatar gshchepa@bk-internal.mysql.com

Merge bk-internal.mysql.com:/data0/bk/mysql-5.0

into  bk-internal.mysql.com:/users/gshchepa/mysql-5.0-opt
parents 16e0a4de 88107378
...@@ -482,3 +482,10 @@ ERROR 42S02: Table 'test.t1' doesn't exist ...@@ -482,3 +482,10 @@ ERROR 42S02: Table 'test.t1' doesn't exist
drop table if exists t1; drop table if exists t1;
Warnings: Warnings:
Note 1051 Unknown table 't1' Note 1051 Unknown table 't1'
drop table if exists t1;
create table t1 (a int) ENGINE=MEMORY;
--> client 2
handler t1 open;
ERROR HY000: Table storage engine for 't1' doesn't have this option
--> client 1
drop table t1;
...@@ -1621,6 +1621,35 @@ a (select count(*) from t2) ...@@ -1621,6 +1621,35 @@ a (select count(*) from t2)
3 0 3 0
4 0 4 0
drop table t1,t2; drop table t1,t2;
DROP DATABASE IF EXISTS bug30269;
FLUSH STATUS;
CREATE DATABASE bug30269;
USE bug30269;
CREATE TABLE test1 (id int, name varchar(23));
CREATE VIEW view1 AS SELECT * FROM test1;
INSERT INTO test1 VALUES (5, 'testit');
GRANT SELECT (id) ON TABLE bug30269.test1 TO 'bug30269'@'localhost';
GRANT SELECT ON TABLE bug30269.view1 TO 'bug30269'@'localhost';
set global query_cache_size= 81920;
USE bug30269;
show status like 'Qcache_queries_in_cache';
Variable_name Value
Qcache_queries_in_cache 0
# Select statement not stored in query cache because of column privileges.
SELECT id FROM test1 WHERE id>2;
id
5
show status like 'Qcache_queries_in_cache';
Variable_name Value
Qcache_queries_in_cache 0
SELECT id FROM view1 WHERE id>2;
id
5
show status like 'Qcache_queries_in_cache';
Variable_name Value
Qcache_queries_in_cache 1
DROP DATABASE bug30269;
DROP USER 'bug30269'@'localhost';
set GLOBAL query_cache_type=default; set GLOBAL query_cache_type=default;
set GLOBAL query_cache_limit=default; set GLOBAL query_cache_limit=default;
set GLOBAL query_cache_min_res_unit=default; set GLOBAL query_cache_min_res_unit=default;
......
...@@ -427,3 +427,17 @@ select * from t1; ...@@ -427,3 +427,17 @@ select * from t1;
# Just to be sure and not confuse the next test case writer. # Just to be sure and not confuse the next test case writer.
drop table if exists t1; drop table if exists t1;
#
# Bug#25856 - HANDLER table OPEN in one connection lock DROP TABLE in another one
#
--disable_warnings
drop table if exists t1;
--enable_warnings
create table t1 (a int) ENGINE=MEMORY;
--echo --> client 2
connection con2;
--error 1031
handler t1 open;
--echo --> client 1
connection default;
drop table t1;
...@@ -1220,9 +1220,40 @@ connection default; ...@@ -1220,9 +1220,40 @@ connection default;
disconnect user1; disconnect user1;
disconnect user2; disconnect user2;
disconnect user3; disconnect user3;
#
# Bug #30269 Query cache eats memory
#
--disable_warnings
DROP DATABASE IF EXISTS bug30269;
--enable_warnings
FLUSH STATUS;
CREATE DATABASE bug30269;
USE bug30269;
CREATE TABLE test1 (id int, name varchar(23));
CREATE VIEW view1 AS SELECT * FROM test1;
INSERT INTO test1 VALUES (5, 'testit');
GRANT SELECT (id) ON TABLE bug30269.test1 TO 'bug30269'@'localhost';
GRANT SELECT ON TABLE bug30269.view1 TO 'bug30269'@'localhost';
set global query_cache_size= 81920;
connect (bug30269, localhost, bug30269,,);
connection bug30269;
USE bug30269;
show status like 'Qcache_queries_in_cache';
--echo # Select statement not stored in query cache because of column privileges.
SELECT id FROM test1 WHERE id>2;
show status like 'Qcache_queries_in_cache';
SELECT id FROM view1 WHERE id>2;
show status like 'Qcache_queries_in_cache';
connection default;
DROP DATABASE bug30269;
disconnect bug30269;
DROP USER 'bug30269'@'localhost';
set GLOBAL query_cache_type=default; set GLOBAL query_cache_type=default;
set GLOBAL query_cache_limit=default; set GLOBAL query_cache_limit=default;
set GLOBAL query_cache_min_res_unit=default; set GLOBAL query_cache_min_res_unit=default;
set GLOBAL query_cache_size=default; set GLOBAL query_cache_size=default;
# End of 5.0 tests
# End of 5.0 tests
...@@ -730,6 +730,7 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, ...@@ -730,6 +730,7 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
if ((table=table_ptr[i])->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE) if ((table=table_ptr[i])->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE)
continue; continue;
lock_type= table->reginfo.lock_type; lock_type= table->reginfo.lock_type;
DBUG_ASSERT (lock_type != TL_WRITE_DEFAULT);
if (lock_type >= TL_WRITE_ALLOW_WRITE) if (lock_type >= TL_WRITE_ALLOW_WRITE)
{ {
*write_lock_used=table; *write_lock_used=table;
......
...@@ -1529,7 +1529,6 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, ...@@ -1529,7 +1529,6 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
HASH_SEARCH_STATE state; HASH_SEARCH_STATE state;
DBUG_ENTER("open_table"); DBUG_ENTER("open_table");
DBUG_ASSERT (table_list->lock_type != TL_WRITE_DEFAULT);
/* find a unused table in the open table cache */ /* find a unused table in the open table cache */
if (refresh) if (refresh)
*refresh=0; *refresh=0;
...@@ -2700,11 +2699,6 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) ...@@ -2700,11 +2699,6 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
{ {
safe_to_ignore_table= FALSE; safe_to_ignore_table= FALSE;
if (tables->lock_type == TL_WRITE_DEFAULT)
{
tables->lock_type= thd->update_lock_default;
DBUG_ASSERT (tables->lock_type >= TL_WRITE_ALLOW_WRITE);
}
/* /*
Ignore placeholders for derived tables. After derived tables Ignore placeholders for derived tables. After derived tables
processing, link to created temporary table will be put here. processing, link to created temporary table will be put here.
...@@ -2849,7 +2843,8 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) ...@@ -2849,7 +2843,8 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
} }
if (tables->lock_type != TL_UNLOCK && ! thd->locked_tables) if (tables->lock_type != TL_UNLOCK && ! thd->locked_tables)
tables->table->reginfo.lock_type=tables->lock_type; tables->table->reginfo.lock_type= tables->lock_type == TL_WRITE_DEFAULT ?
thd->update_lock_default : tables->lock_type;
tables->table->grant= tables->grant; tables->table->grant= tables->grant;
process_view_routines: process_view_routines:
......
...@@ -3000,14 +3000,42 @@ void Query_cache::double_linked_list_join(Query_cache_block *head_tail, ...@@ -3000,14 +3000,42 @@ void Query_cache::double_linked_list_join(Query_cache_block *head_tail,
>0 number of tables >0 number of tables
*/ */
static TABLE_COUNTER_TYPE process_and_count_tables(TABLE_LIST *tables_used, TABLE_COUNTER_TYPE
uint8 *tables_type) Query_cache::process_and_count_tables(THD *thd, TABLE_LIST *tables_used,
uint8 *tables_type)
{ {
DBUG_ENTER("process_and_count_tables"); DBUG_ENTER("process_and_count_tables");
TABLE_COUNTER_TYPE table_count = 0; TABLE_COUNTER_TYPE table_count = 0;
for (; tables_used; tables_used= tables_used->next_global) for (; tables_used; tables_used= tables_used->next_global)
{ {
table_count++; table_count++;
#ifdef HAVE_QUERY_CACHE
/*
Disable any attempt to store this statement if there are
column level grants on any referenced tables.
The grant.want_privileges flag was set to 1 in the
check_grant() function earlier if the TABLE_LIST object
had any associated column privileges.
We need to check that the TABLE_LIST object isn't part
of a VIEW definition because we want to be able to cache
views.
TODO: Although it is possible to cache views, the privilege
check on view tables always fall back on column privileges
even if there are more generic table privileges. Thus it isn't
currently possible to retrieve cached view-tables unless the
client has the super user privileges.
*/
if (tables_used->grant.want_privilege &&
tables_used->belong_to_view == NULL)
{
DBUG_PRINT("qcache", ("Don't cache statement as it refers to "
"tables with column privileges."));
thd->lex->safe_to_cache_query= 0;
DBUG_RETURN(0);
}
#endif
if (tables_used->view) if (tables_used->view)
{ {
DBUG_PRINT("qcache", ("view: %s db: %s", DBUG_PRINT("qcache", ("view: %s db: %s",
...@@ -3080,7 +3108,8 @@ Query_cache::is_cacheable(THD *thd, uint32 query_len, char *query, LEX *lex, ...@@ -3080,7 +3108,8 @@ Query_cache::is_cacheable(THD *thd, uint32 query_len, char *query, LEX *lex,
(long) lex->select_lex.options, (long) lex->select_lex.options,
(int) thd->variables.query_cache_type)); (int) thd->variables.query_cache_type));
if (!(table_count= process_and_count_tables(tables_used, tables_type))) if (!(table_count= process_and_count_tables(thd, tables_used,
tables_type)))
DBUG_RETURN(0); DBUG_RETURN(0);
if ((thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) && if ((thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) &&
......
...@@ -368,10 +368,12 @@ protected: ...@@ -368,10 +368,12 @@ protected:
If query is cacheable return number tables in query If query is cacheable return number tables in query
(query without tables not cached) (query without tables not cached)
*/ */
static
TABLE_COUNTER_TYPE is_cacheable(THD *thd, uint32 query_len, char *query, TABLE_COUNTER_TYPE is_cacheable(THD *thd, uint32 query_len, char *query,
LEX *lex, TABLE_LIST *tables_used, LEX *lex, TABLE_LIST *tables_used,
uint8 *tables_type); uint8 *tables_type);
TABLE_COUNTER_TYPE process_and_count_tables(THD *thd,
TABLE_LIST *tables_used,
uint8 *tables_type);
static my_bool ask_handler_allowance(THD *thd, TABLE_LIST *tables_used); static my_bool ask_handler_allowance(THD *thd, TABLE_LIST *tables_used);
public: public:
......
...@@ -119,6 +119,44 @@ static void mysql_ha_hash_free(TABLE_LIST *tables) ...@@ -119,6 +119,44 @@ static void mysql_ha_hash_free(TABLE_LIST *tables)
my_free((char*) tables, MYF(0)); my_free((char*) tables, MYF(0));
} }
/**
Close a HANDLER table.
@param thd Thread identifier.
@param tables A list of tables with the first entry to close.
@note Though this function takes a list of tables, only the first list entry
will be closed.
@note Broadcasts refresh if it closed the table.
*/
static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables)
{
TABLE **table_ptr;
/*
Though we could take the table pointer from hash_tables->table,
we must follow the thd->handler_tables chain anyway, as we need the
address of the 'next' pointer referencing this table
for close_thread_table().
*/
for (table_ptr= &(thd->handler_tables);
*table_ptr && (*table_ptr != tables->table);
table_ptr= &(*table_ptr)->next)
;
if (*table_ptr)
{
(*table_ptr)->file->ha_index_or_rnd_end();
VOID(pthread_mutex_lock(&LOCK_open));
if (close_thread_table(thd, table_ptr))
{
/* Tell threads waiting for refresh that something has happened */
broadcast_refresh();
}
VOID(pthread_mutex_unlock(&LOCK_open));
}
}
/* /*
Open a HANDLER table. Open a HANDLER table.
...@@ -145,7 +183,7 @@ static void mysql_ha_hash_free(TABLE_LIST *tables) ...@@ -145,7 +183,7 @@ static void mysql_ha_hash_free(TABLE_LIST *tables)
bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
{ {
TABLE_LIST *hash_tables; TABLE_LIST *hash_tables = NULL;
char *db, *name, *alias; char *db, *name, *alias;
uint dblen, namelen, aliaslen, counter; uint dblen, namelen, aliaslen, counter;
int error; int error;
...@@ -197,7 +235,6 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) ...@@ -197,7 +235,6 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
{ {
if (! reopen) if (! reopen)
my_error(ER_ILLEGAL_HA, MYF(0), tables->alias); my_error(ER_ILLEGAL_HA, MYF(0), tables->alias);
mysql_ha_close(thd, tables);
goto err; goto err;
} }
...@@ -225,11 +262,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) ...@@ -225,11 +262,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
/* add to hash */ /* add to hash */
if (my_hash_insert(&thd->handler_tables_hash, (byte*) hash_tables)) if (my_hash_insert(&thd->handler_tables_hash, (byte*) hash_tables))
{
my_free((char*) hash_tables, MYF(0));
mysql_ha_close(thd, tables);
goto err; goto err;
}
} }
if (! reopen) if (! reopen)
...@@ -238,13 +271,17 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) ...@@ -238,13 +271,17 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
DBUG_RETURN(FALSE); DBUG_RETURN(FALSE);
err: err:
if (hash_tables)
my_free((char*) hash_tables, MYF(0));
if (tables->table)
mysql_ha_close_table(thd, tables);
DBUG_PRINT("exit",("ERROR")); DBUG_PRINT("exit",("ERROR"));
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
} }
/* /*
Close a HANDLER table. Close a HANDLER table by alias or table name
SYNOPSIS SYNOPSIS
mysql_ha_close() mysql_ha_close()
...@@ -252,9 +289,8 @@ err: ...@@ -252,9 +289,8 @@ err:
tables A list of tables with the first entry to close. tables A list of tables with the first entry to close.
DESCRIPTION DESCRIPTION
Though this function takes a list of tables, only the first list entry Closes the table that is associated (on the handler tables hash) with the
will be closed. name (table->alias) of the specified table.
Broadcasts refresh if it closed the table.
RETURN RETURN
FALSE ok FALSE ok
...@@ -264,7 +300,6 @@ err: ...@@ -264,7 +300,6 @@ err:
bool mysql_ha_close(THD *thd, TABLE_LIST *tables) bool mysql_ha_close(THD *thd, TABLE_LIST *tables)
{ {
TABLE_LIST *hash_tables; TABLE_LIST *hash_tables;
TABLE **table_ptr;
DBUG_ENTER("mysql_ha_close"); DBUG_ENTER("mysql_ha_close");
DBUG_PRINT("enter",("'%s'.'%s' as '%s'", DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
tables->db, tables->table_name, tables->alias)); tables->db, tables->table_name, tables->alias));
...@@ -273,28 +308,7 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables) ...@@ -273,28 +308,7 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables)
(byte*) tables->alias, (byte*) tables->alias,
strlen(tables->alias) + 1))) strlen(tables->alias) + 1)))
{ {
/* mysql_ha_close_table(thd, hash_tables);
Though we could take the table pointer from hash_tables->table,
we must follow the thd->handler_tables chain anyway, as we need the
address of the 'next' pointer referencing this table
for close_thread_table().
*/
for (table_ptr= &(thd->handler_tables);
*table_ptr && (*table_ptr != hash_tables->table);
table_ptr= &(*table_ptr)->next)
;
if (*table_ptr)
{
(*table_ptr)->file->ha_index_or_rnd_end();
VOID(pthread_mutex_lock(&LOCK_open));
if (close_thread_table(thd, table_ptr))
{
/* Tell threads waiting for refresh that something has happened */
broadcast_refresh();
}
VOID(pthread_mutex_unlock(&LOCK_open));
}
hash_delete(&thd->handler_tables_hash, (byte*) hash_tables); hash_delete(&thd->handler_tables_hash, (byte*) hash_tables);
} }
else else
......
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