Commit 70092601 authored by unknown's avatar unknown

merge of 2876.430.11 & 2876.430.1

CF_PREOPEN_TMP_TABLES & CF_HA_CLOSE & Patch for Bug#11746602 (27480: Extend CREATE TEMPORARY TABLES 
  privilege to allow temp table operations).
parent 94d722b6
......@@ -4643,6 +4643,7 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
Note that during creation of temporary table we still need to check
if user has CREATE_TMP_ACL.
*/
tl->grant.privilege|= TMP_TABLE_ACLS;
tl->grant.want_privilege= 0;
continue;
}
......
......@@ -122,8 +122,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
MDL_EXCLUSIVE, MDL_TRANSACTION);
if (lock_table_names(thd, table_list, table_list->next_global,
thd->variables.lock_wait_timeout,
MYSQL_OPEN_SKIP_TEMPORARY))
thd->variables.lock_wait_timeout, 0))
DBUG_RETURN(0);
has_mdl_lock= TRUE;
......
......@@ -2276,14 +2276,15 @@ TABLE *find_temporary_table(THD *thd,
int drop_temporary_table(THD *thd, TABLE_LIST *table_list, bool *is_trans)
{
TABLE *table;
DBUG_ENTER("drop_temporary_table");
DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'",
table_list->db, table_list->table_name));
if (!(table= find_temporary_table(thd, table_list)))
if (!is_temporary_table(table_list))
DBUG_RETURN(1);
TABLE *table= table_list->table;
/* Table might be in use by some outer statement. */
if (table->query_id && table->query_id != thd->query_id)
{
......@@ -2291,8 +2292,7 @@ int drop_temporary_table(THD *thd, TABLE_LIST *table_list, bool *is_trans)
DBUG_RETURN(-1);
}
if (is_trans != NULL)
*is_trans= table->file->has_transactions();
*is_trans= table->file->has_transactions();
/*
If LOCK TABLES list is not empty and contains this table,
......@@ -2300,6 +2300,7 @@ int drop_temporary_table(THD *thd, TABLE_LIST *table_list, bool *is_trans)
*/
mysql_lock_remove(thd, thd->lock, table);
close_temporary_table(thd, table, 1, 1);
table_list->table= NULL;
DBUG_RETURN(0);
}
......@@ -4238,8 +4239,7 @@ recover_from_failed_open(THD *thd)
case OT_DISCOVER:
{
if ((result= lock_table_names(thd, m_failed_table, NULL,
get_timeout(),
MYSQL_OPEN_SKIP_TEMPORARY)))
get_timeout(), 0)))
break;
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
......@@ -4255,8 +4255,7 @@ recover_from_failed_open(THD *thd)
case OT_REPAIR:
{
if ((result= lock_table_names(thd, m_failed_table, NULL,
get_timeout(),
MYSQL_OPEN_SKIP_TEMPORARY)))
get_timeout(), 0)))
break;
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
......
......@@ -93,7 +93,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update,
/* mysql_lock_tables() and open_table() flags bits */
#define MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK 0x0001
#define MYSQL_OPEN_IGNORE_FLUSH 0x0002
#define MYSQL_OPEN_TEMPORARY_ONLY 0x0004
/* MYSQL_OPEN_TEMPORARY_ONLY (0x0004) is not used anymore. */
#define MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY 0x0008
#define MYSQL_LOCK_LOG_TABLE 0x0010
/**
......@@ -106,8 +106,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update,
a new instance of the table.
*/
#define MYSQL_OPEN_GET_NEW_TABLE 0x0040
/** Don't look up the table in the list of temporary tables. */
#define MYSQL_OPEN_SKIP_TEMPORARY 0x0080
/* 0x0080 used to be MYSQL_OPEN_SKIP_TEMPORARY */
/** Fail instead of waiting when conficting metadata lock is discovered. */
#define MYSQL_OPEN_FAIL_ON_MDL_CONFLICT 0x0100
/** Open tables using MDL_SHARED lock instead of one specified in parser. */
......@@ -140,7 +139,6 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update,
MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY |\
MYSQL_LOCK_IGNORE_TIMEOUT |\
MYSQL_OPEN_GET_NEW_TABLE |\
MYSQL_OPEN_SKIP_TEMPORARY |\
MYSQL_OPEN_HAS_MDL_LOCK)
bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
......
......@@ -815,7 +815,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
/* Lock all tables and stored routines about to be dropped. */
if (lock_table_names(thd, tables, NULL, thd->variables.lock_wait_timeout,
MYSQL_OPEN_SKIP_TEMPORARY) ||
0) ||
lock_db_routines(thd, db))
goto exit;
......
......@@ -3918,10 +3918,10 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
{
/*
This shouldn't happen as creation of temporary table should make
it preparable for open. But let us do close_temporary_table() here
just in case.
it preparable for open. Anyway we can't drop temporary table if
we are unable to find it.
*/
drop_temporary_table(thd, create_table, NULL);
DBUG_ASSERT(0);
}
else
table= create_table->table;
......
......@@ -24,7 +24,7 @@
// set_handler_table_locks,
// lock_global_read_lock,
// make_global_read_lock_block_commit
#include "sql_base.h" // find_temporary_tablesx
#include "sql_base.h" // find_temporary_table
#include "sql_cache.h" // QUERY_CACHE_FLAGS_SIZE, query_cache_*
#include "sql_show.h" // mysqld_list_*, mysqld_show_*,
// calc_sum_of_all_status
......@@ -44,7 +44,6 @@
#include "sql_table.h" // mysql_create_like_table,
// mysql_create_table,
// mysql_alter_table,
// mysql_recreate_table,
// mysql_backup_table,
// mysql_restore_table
#include "sql_reload.h" // reload_acl_and_cache
......@@ -124,6 +123,7 @@ static void sql_kill(THD *thd, ulong id, killed_state state);
static void sql_kill_user(THD *thd, LEX_USER *user, killed_state state);
static bool execute_show_status(THD *, TABLE_LIST *);
static bool execute_rename_table(THD *, TABLE_LIST *, TABLE_LIST *);
static bool lock_tables_precheck(THD *thd, TABLE_LIST *tables);
const char *any_db="*any*"; // Special symbol for check_access
......@@ -499,6 +499,7 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_SELECT]|= CF_PREOPEN_TMP_TABLES;
sql_command_flags[SQLCOM_SET_OPTION]|= CF_PREOPEN_TMP_TABLES;
sql_command_flags[SQLCOM_DO]|= CF_PREOPEN_TMP_TABLES;
sql_command_flags[SQLCOM_HA_OPEN]|= CF_PREOPEN_TMP_TABLES;
sql_command_flags[SQLCOM_CALL]|= CF_PREOPEN_TMP_TABLES;
sql_command_flags[SQLCOM_CHECKSUM]|= CF_PREOPEN_TMP_TABLES;
sql_command_flags[SQLCOM_ANALYZE]|= CF_PREOPEN_TMP_TABLES;
......@@ -2413,6 +2414,7 @@ mysql_execute_command(THD *thd)
if (open_temporary_tables(thd, all_tables))
goto error;
}
switch (lex->sql_command) {
case SQLCOM_SHOW_EVENTS:
......@@ -2422,8 +2424,7 @@ mysql_execute_command(THD *thd)
#endif
case SQLCOM_SHOW_STATUS_PROC:
case SQLCOM_SHOW_STATUS_FUNC:
if ((res= check_table_access(thd, SELECT_ACL, all_tables, FALSE,
UINT_MAX, FALSE)))
if (lock_tables_precheck(thd, all_tables))
goto error;
res= execute_sqlcom_select(thd, all_tables);
break;
......@@ -2798,12 +2799,6 @@ case SQLCOM_PREPARE:
}
#endif
/*
Close any open handlers for the table. We need to this extra call here
as there may have been new handlers created since the previous call.
*/
mysql_ha_rm_tables(thd, create_table);
if (select_lex->item_list.elements) // With select
{
select_result *result;
......@@ -4173,6 +4168,9 @@ end_with_restore_list:
DBUG_ASSERT(first_table == all_tables && first_table != 0);
if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE))
goto error;
/* Close temporary tables which were pre-opened for privilege checking. */
close_thread_tables(thd);
all_tables->table= NULL;
res= mysql_ha_open(thd, first_table, 0);
break;
case SQLCOM_HA_CLOSE:
......@@ -5519,6 +5517,9 @@ static bool check_show_access(THD *thd, TABLE_LIST *table)
if (check_grant(thd, SELECT_ACL, dst_table, TRUE, UINT_MAX, FALSE))
return TRUE; /* Access denied */
close_thread_tables(thd);
dst_table->table= NULL;
/* Access granted */
return FALSE;
}
......@@ -5604,10 +5605,10 @@ check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables,
DBUG_PRINT("info", ("derived: %d view: %d", tables->derived != 0,
tables->view != 0));
if (tables->is_anonymous_derived_table() ||
(tables->table && tables->table->s &&
(int)tables->table->s->tmp_table))
if (tables->is_anonymous_derived_table())
continue;
thd->security_ctx= sctx;
if (check_access(thd, want_access, tables->get_db_name(),
......@@ -7465,6 +7466,19 @@ bool multi_delete_precheck(THD *thd, TABLE_LIST *tables)
TABLE_LIST **save_query_tables_own_last= thd->lex->query_tables_own_last;
DBUG_ENTER("multi_delete_precheck");
/*
Temporary tables are pre-opened in 'tables' list only. Here we need to
initialize TABLE instances in 'aux_tables' list.
*/
for (TABLE_LIST *tl= aux_tables; tl; tl= tl->next_global)
{
if (tl->table)
continue;
if (tl->correspondent_table)
tl->table= tl->correspondent_table->table;
}
/* sql_yacc guarantees that tables and aux_tables are not zero */
DBUG_ASSERT(aux_tables != 0);
if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE))
......@@ -7733,9 +7747,9 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables,
CREATE TABLE ... SELECT, also require INSERT.
*/
want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ?
CREATE_TMP_ACL : CREATE_ACL) |
(select_lex->item_list.elements ? INSERT_ACL : 0);
want_priv= (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ?
CREATE_TMP_ACL :
(CREATE_ACL | (select_lex->item_list.elements ? INSERT_ACL : 0));
if (check_access(thd, want_priv, create_table->db,
&create_table->grant.privilege,
......@@ -7744,11 +7758,48 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables,
goto err;
/* If it is a merge table, check privileges for merge children. */
if (lex->create_info.merge_list.first &&
check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
lex->create_info.merge_list.first,
FALSE, UINT_MAX, FALSE))
goto err;
if (lex->create_info.merge_list.first)
{
/*
The user must have (SELECT_ACL | UPDATE_ACL | DELETE_ACL) on the
underlying base tables, even if there are temporary tables with the same
names.
From user's point of view, it might look as if the user must have these
privileges on temporary tables to create a merge table over them. This is
one of two cases when a set of privileges is required for operations on
temporary tables (see also CREATE TABLE).
The reason for this behavior stems from the following facts:
- For merge tables, the underlying table privileges are checked only
at CREATE TABLE / ALTER TABLE time.
In other words, once a merge table is created, the privileges of
the underlying tables can be revoked, but the user will still have
access to the merge table (provided that the user has privileges on
the merge table itself).
- Temporary tables shadow base tables.
I.e. there might be temporary and base tables with the same name, and
the temporary table takes the precedence in all operations.
- For temporary MERGE tables we do not track if their child tables are
base or temporary. As result we can't guarantee that privilege check
which was done in presence of temporary child will stay relevant later
as this temporary table might be removed.
If SELECT_ACL | UPDATE_ACL | DELETE_ACL privileges were not checked for
the underlying *base* tables, it would create a security breach as in
Bug#12771903.
*/
if (check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
lex->create_info.merge_list.first,
FALSE, UINT_MAX, FALSE))
goto err;
}
if (want_priv != CREATE_TMP_ACL &&
check_grant(thd, want_priv, create_table, FALSE, 1, FALSE))
......@@ -7773,6 +7824,35 @@ err:
}
/**
Check privileges for LOCK TABLES statement.
@param thd Thread context.
@param tables List of tables to be locked.
@retval FALSE - Success.
@retval TRUE - Failure.
*/
static bool lock_tables_precheck(THD *thd, TABLE_LIST *tables)
{
TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
for (TABLE_LIST *table= tables; table != first_not_own_table && table;
table= table->next_global)
{
if (is_temporary_table(table))
continue;
if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, table,
FALSE, 1, FALSE))
return TRUE;
}
return FALSE;
}
/**
negate given expression.
......
......@@ -106,6 +106,7 @@ When one supplies long data for a placeholder:
#include "sp_head.h"
#include "sp.h"
#include "sp_cache.h"
#include "sql_handler.h" // mysql_ha_rm_tables
#include "probes_mysql.h"
#ifdef EMBEDDED_LIBRARY
/* include MYSQL_BIND headers */
......
......@@ -142,7 +142,7 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
}
if (lock_table_names(thd, table_list, 0, thd->variables.lock_wait_timeout,
MYSQL_OPEN_SKIP_TEMPORARY))
0))
goto err;
for (ren_table= table_list; ren_table; ren_table= ren_table->next_local)
......
......@@ -2072,15 +2072,13 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
(void) delete_statistics_for_table(thd, &db_name, &table_name);
}
}
mysql_ha_rm_tables(thd, tables);
if (!drop_temporary)
{
if (!thd->locked_tables_mode)
{
if (lock_table_names(thd, tables, NULL, thd->variables.lock_wait_timeout,
MYSQL_OPEN_SKIP_TEMPORARY))
if (lock_table_names(thd, tables, NULL,
thd->variables.lock_wait_timeout, 0))
DBUG_RETURN(true);
for (table= tables; table; table= table->next_local)
{
......@@ -2206,6 +2204,9 @@ static uint32 comment_length(THD *thd, uint32 comment_pos,
@note This function assumes that metadata locks have already been taken.
It is also assumed that the tables have been removed from TDC.
@note This function assumes that temporary tables to be dropped have
been pre-opened using corresponding table list elements.
@todo When logging to the binary log, we should log
tmp_tables and transactional tables as separate statements if we
are in a transaction; This is needed to get these tables into the
......@@ -5060,6 +5061,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
String query(buf, sizeof(buf), system_charset_info);
query.length(0); // Have to zero it since constructor doesn't
Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN);
bool new_table= FALSE; // Whether newly created table is open.
/*
The condition avoids a crash as described in BUG#48506. Other
......@@ -5068,14 +5070,21 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
*/
if (!table->view)
{
/*
Here we open the destination table, on which we already have
exclusive metadata lock. This is needed for store_create_info()
to work. The table will be closed by close_thread_table() at
the end of this branch.
*/
if (open_table(thd, table, thd->mem_root, &ot_ctx))
goto err;
if (!table->table)
{
/*
In order for store_create_info() to work we need to open
destination table if it is not already open (i.e. if it
has not existed before). We don't need acquire metadata
lock in order to do this as we already hold exclusive
lock on this table. The table will be closed by
close_thread_table() at the end of this branch.
*/
if (open_table(thd, table, thd->mem_root, &ot_ctx))
goto err;
new_table= TRUE;
}
int result __attribute__((unused))=
store_create_info(thd, table, &query,
......@@ -5085,13 +5094,16 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
if (write_bin_log(thd, TRUE, query.ptr(), query.length()))
goto err;
DBUG_ASSERT(thd->open_tables == table->table);
/*
When opening the table, we ignored the locked tables
(MYSQL_OPEN_GET_NEW_TABLE). Now we can close the table without
risking to close some locked table.
*/
close_thread_table(thd, &thd->open_tables);
if (new_table)
{
DBUG_ASSERT(thd->open_tables == table->table);
/*
When opening the table, we ignored the locked tables
(MYSQL_OPEN_GET_NEW_TABLE). Now we can close the table
without risking to close some locked table.
*/
close_thread_table(thd, &thd->open_tables);
}
}
}
else // Case 1
......@@ -8821,11 +8833,6 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
if (! thd->in_sub_stmt)
trans_rollback_stmt(thd);
close_thread_tables(thd);
/*
Don't release metadata locks, this will be done at
statement end.
*/
table->table=0; // For query cache
}
if (protocol->write())
goto err;
......
......@@ -18,9 +18,8 @@
#include "sql_class.h" // THD
#include "sql_base.h" // open_and_lock_tables
#include "sql_table.h" // write_bin_log
#include "sql_handler.h" // mysql_ha_rm_tables
#include "datadict.h" // dd_recreate_table()
#include "lock.h" // MYSQL_OPEN_TEMPORARY_ONLY
#include "lock.h" // MYSQL_OPEN_* flags
#include "sql_acl.h" // DROP_ACL
#include "sql_parse.h" // check_one_table_access()
#include "sql_truncate.h"
......@@ -199,9 +198,7 @@ int Sql_cmd_truncate_table::handler_truncate(THD *thd, TABLE_LIST *table_ref,
*/
/* If it is a temporary table, no need to take locks. */
if (is_tmp_table)
flags= MYSQL_OPEN_TEMPORARY_ONLY;
else
if (!is_tmp_table)
{
/* We don't need to load triggers. */
DBUG_ASSERT(table_ref->trg_event_map == 0);
......@@ -216,7 +213,7 @@ int Sql_cmd_truncate_table::handler_truncate(THD *thd, TABLE_LIST *table_ref,
the MDL lock taken above and otherwise there is no way to
wait for FLUSH TABLES in deadlock-free fashion.
*/
flags= MYSQL_OPEN_IGNORE_FLUSH | MYSQL_OPEN_SKIP_TEMPORARY;
flags= MYSQL_OPEN_IGNORE_FLUSH;
/*
Even though we have an MDL lock on the table here, we don't
pass MYSQL_OPEN_HAS_MDL_LOCK to open_and_lock_tables
......@@ -346,8 +343,7 @@ bool Sql_cmd_truncate_table::lock_table(THD *thd, TABLE_LIST *table_ref,
/* Acquire an exclusive lock. */
DBUG_ASSERT(table_ref->next_global == NULL);
if (lock_table_names(thd, table_ref, NULL,
thd->variables.lock_wait_timeout,
MYSQL_OPEN_SKIP_TEMPORARY))
thd->variables.lock_wait_timeout, 0))
DBUG_RETURN(TRUE);
if (dd_check_storage_engine_flag(thd, table_ref->db, table_ref->table_name,
......@@ -399,26 +395,28 @@ bool Sql_cmd_truncate_table::lock_table(THD *thd, TABLE_LIST *table_ref,
bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref)
{
int error;
TABLE *table;
bool binlog_stmt;
DBUG_ENTER("Sql_cmd_truncate_table::truncate_table");
DBUG_ASSERT((!table_ref->table) ||
(table_ref->table && table_ref->table->s));
/* Initialize, or reinitialize in case of reexecution (SP). */
m_ticket_downgrade= NULL;
/* Remove table from the HANDLER's hash. */
mysql_ha_rm_tables(thd, table_ref);
/* If it is a temporary table, no need to take locks. */
if ((table= find_temporary_table(thd, table_ref)))
if (is_temporary_table(table_ref))
{
TABLE *tmp_table= table_ref->table;
/* In RBR, the statement is not binlogged if the table is temporary. */
binlog_stmt= !thd->is_current_stmt_binlog_format_row();
/* Note that a temporary table cannot be partitioned. */
if (ha_check_storage_engine_flag(table->s->db_type(), HTON_CAN_RECREATE))
if (ha_check_storage_engine_flag(tmp_table->s->db_type(),
HTON_CAN_RECREATE))
{
if ((error= recreate_temporary_table(thd, table)))
if ((error= recreate_temporary_table(thd, tmp_table)))
binlog_stmt= FALSE; /* No need to binlog failed truncate-by-recreate. */
DBUG_ASSERT(! thd->transaction.stmt.modified_non_trans_table);
......
......@@ -1665,8 +1665,7 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
DBUG_RETURN(TRUE);
}
if (lock_table_names(thd, views, 0, thd->variables.lock_wait_timeout,
MYSQL_OPEN_SKIP_TEMPORARY))
if (lock_table_names(thd, views, 0, thd->variables.lock_wait_timeout, 0))
DBUG_RETURN(TRUE);
for (view= views; view; view= view->next_local)
......
......@@ -484,6 +484,11 @@ int ha_myisammrg::add_children_list(void)
/* Set the expected table version, to not cause spurious re-prepare. */
child_l->set_table_ref_id(mrg_child_def->get_child_table_ref_type(),
mrg_child_def->get_child_def_version());
/*
Copy parent's prelocking attribute to allow opening of child
temporary residing in the prelocking list.
*/
child_l->prelocking_placeholder= parent_l->prelocking_placeholder;
/*
For statements which acquire a SNW metadata lock on a parent table and
then later try to upgrade it to an X lock (e.g. ALTER TABLE), SNW
......
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