mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-04 04:46:15 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1606 lines
		
	
	
	
		
			44 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1606 lines
		
	
	
	
		
			44 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
  Copyright (c) 2016, 2021, MariaDB Corporation.
 | 
						|
 | 
						|
  This program is free software; you can redistribute it and/or modify
 | 
						|
  it under the terms of the GNU General Public License as published by
 | 
						|
  the Free Software Foundation; version 2 of the License.
 | 
						|
 | 
						|
  This program is distributed in the hope that it will be useful,
 | 
						|
  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
  GNU General Public License for more details.
 | 
						|
 | 
						|
  You should have received a copy of the GNU General Public License
 | 
						|
  along with this program; if not, write to the Free Software
 | 
						|
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 | 
						|
*/
 | 
						|
 | 
						|
/**
 | 
						|
  All methods pertaining to temporary tables.
 | 
						|
*/
 | 
						|
 | 
						|
#include "mariadb.h"
 | 
						|
#include "sql_acl.h"                            /* TMP_TABLE_ACLS */
 | 
						|
#include "sql_base.h"                           /* tdc_create_key */
 | 
						|
#include "lock.h"                               /* mysql_lock_remove */
 | 
						|
#include "log_event.h"                          /* Query_log_event */
 | 
						|
#include "sql_show.h"                           /* append_identifier */
 | 
						|
#include "sql_handler.h"                        /* mysql_ha_rm_temporary_tables */
 | 
						|
#include "rpl_rli.h"                            /* rpl_group_info */
 | 
						|
 | 
						|
#define IS_USER_TABLE(A) ((A->tmp_table == TRANSACTIONAL_TMP_TABLE) || \
 | 
						|
                          (A->tmp_table == NON_TRANSACTIONAL_TMP_TABLE))
 | 
						|
 | 
						|
/**
 | 
						|
  Check whether temporary tables exist. The decision is made based on the
 | 
						|
  existence of TMP_TABLE_SHAREs in Open_tables_state::temporary_tables list.
 | 
						|
 | 
						|
  @return false                       Temporary tables exist
 | 
						|
          true                        No temporary table exist
 | 
						|
*/
 | 
						|
bool THD::has_thd_temporary_tables()
 | 
						|
{
 | 
						|
  DBUG_ENTER("THD::has_thd_temporary_tables");
 | 
						|
  bool result= (temporary_tables && !temporary_tables->is_empty());
 | 
						|
  DBUG_RETURN(result);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Create a temporary table, open it and return the TABLE handle.
 | 
						|
 | 
						|
  @param frm  [IN]                    Binary frm image
 | 
						|
  @param path [IN]                    File path (without extension)
 | 
						|
  @param db   [IN]                    Schema name
 | 
						|
  @param table_name [IN]              Table name
 | 
						|
 | 
						|
  @return Success                     A pointer to table object
 | 
						|
          Failure                     NULL
 | 
						|
*/
 | 
						|
TABLE *THD::create_and_open_tmp_table(LEX_CUSTRING *frm,
 | 
						|
                                      const char *path,
 | 
						|
                                      const char *db,
 | 
						|
                                      const char *table_name,
 | 
						|
                                      bool open_internal_tables)
 | 
						|
{
 | 
						|
  DBUG_ENTER("THD::create_and_open_tmp_table");
 | 
						|
 | 
						|
  TMP_TABLE_SHARE *share;
 | 
						|
  TABLE *table= NULL;
 | 
						|
 | 
						|
  if ((share= create_temporary_table(frm, path, db, table_name)))
 | 
						|
  {
 | 
						|
    open_options|= HA_OPEN_FOR_CREATE;
 | 
						|
    table= open_temporary_table(share, table_name);
 | 
						|
    open_options&= ~HA_OPEN_FOR_CREATE;
 | 
						|
 | 
						|
    /*
 | 
						|
      Failed to open a temporary table instance. As we are not passing
 | 
						|
      the created TMP_TABLE_SHARE to the caller, we must remove it from
 | 
						|
      the list and free it here.
 | 
						|
    */
 | 
						|
    if (!table)
 | 
						|
    {
 | 
						|
      /* Remove the TABLE_SHARE from the list of temporary tables. */
 | 
						|
      temporary_tables->remove(share);
 | 
						|
 | 
						|
      /* Free the TMP_TABLE_SHARE. */
 | 
						|
      free_tmp_table_share(share, false);
 | 
						|
      DBUG_RETURN(0);
 | 
						|
    }
 | 
						|
 | 
						|
    /* Open any related tables */
 | 
						|
    if (open_internal_tables && table->internal_tables &&
 | 
						|
        open_and_lock_internal_tables(table, true))
 | 
						|
    {
 | 
						|
      drop_temporary_table(table, NULL, false);
 | 
						|
      DBUG_RETURN(0);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  DBUG_RETURN(table);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Check whether an open table with db/table name is in use.
 | 
						|
 | 
						|
  @param db [IN]                      Database name
 | 
						|
  @param table_name [IN]              Table name
 | 
						|
  @param state [IN]                   State of temp table to open
 | 
						|
 | 
						|
  @return Success                     Pointer to first used table instance.
 | 
						|
          Failure                     NULL
 | 
						|
*/
 | 
						|
TABLE *THD::find_temporary_table(const char *db,
 | 
						|
                                 const char *table_name,
 | 
						|
                                 Temporary_table_state state)
 | 
						|
{
 | 
						|
  DBUG_ENTER("THD::find_temporary_table");
 | 
						|
 | 
						|
  TABLE *table;
 | 
						|
  char key[MAX_DBKEY_LENGTH];
 | 
						|
  uint key_length;
 | 
						|
  bool locked;
 | 
						|
 | 
						|
  if (!has_temporary_tables())
 | 
						|
  {
 | 
						|
    DBUG_RETURN(NULL);
 | 
						|
  }
 | 
						|
 | 
						|
  key_length= create_tmp_table_def_key(key, db, table_name);
 | 
						|
 | 
						|
  locked= lock_temporary_tables();
 | 
						|
  table=  find_temporary_table(key, key_length, state);
 | 
						|
  if (locked)
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(m_tmp_tables_locked);
 | 
						|
    unlock_temporary_tables();
 | 
						|
  }
 | 
						|
 | 
						|
  DBUG_RETURN(table);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Check whether an open table specified in TABLE_LIST is in use.
 | 
						|
 | 
						|
  @return tl [IN]                     TABLE_LIST
 | 
						|
 | 
						|
  @return Success                     Pointer to first used table instance.
 | 
						|
          Failure                     NULL
 | 
						|
*/
 | 
						|
TABLE *THD::find_temporary_table(const TABLE_LIST *tl,
 | 
						|
                                 Temporary_table_state state)
 | 
						|
{
 | 
						|
  DBUG_ENTER("THD::find_temporary_table");
 | 
						|
  TABLE *table= find_temporary_table(tl->get_db_name().str,
 | 
						|
                                     tl->get_table_name().str,
 | 
						|
                                     state);
 | 
						|
  DBUG_RETURN(table);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Check whether a temporary table exists with the specified key.
 | 
						|
  The key, in this case, is not the usual key used for temporary tables.
 | 
						|
  It does not contain server_id & pseudo_thread_id. This function is
 | 
						|
  essentially used use to check whether there is any temporary table
 | 
						|
  which _shadows_ a base table.
 | 
						|
  (see: Query_cache::send_result_to_client())
 | 
						|
 | 
						|
  @return Success                     A pointer to table share object
 | 
						|
          Failure                     NULL
 | 
						|
*/
 | 
						|
TMP_TABLE_SHARE *THD::find_tmp_table_share_w_base_key(const char *key,
 | 
						|
                                                      uint key_length)
 | 
						|
{
 | 
						|
  DBUG_ENTER("THD::find_tmp_table_share_w_base_key");
 | 
						|
 | 
						|
  TMP_TABLE_SHARE *share;
 | 
						|
  TMP_TABLE_SHARE *result= NULL;
 | 
						|
  bool locked;
 | 
						|
 | 
						|
  if (!has_temporary_tables())
 | 
						|
  {
 | 
						|
    DBUG_RETURN(NULL);
 | 
						|
  }
 | 
						|
 | 
						|
  locked= lock_temporary_tables();
 | 
						|
 | 
						|
  All_tmp_tables_list::Iterator it(*temporary_tables);
 | 
						|
  while ((share= it++))
 | 
						|
  {
 | 
						|
    if ((share->table_cache_key.length - TMP_TABLE_KEY_EXTRA) == key_length
 | 
						|
        && !memcmp(share->table_cache_key.str, key, key_length))
 | 
						|
    {
 | 
						|
      result= share;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (locked)
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(m_tmp_tables_locked);
 | 
						|
    unlock_temporary_tables();
 | 
						|
  }
 | 
						|
 | 
						|
  DBUG_RETURN(result);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Lookup the TMP_TABLE_SHARE using the given db/table_name.The server_id and
 | 
						|
  pseudo_thread_id used to generate table definition key is taken from THD
 | 
						|
  (see create_tmp_table_def_key()). Return NULL is none found.
 | 
						|
 | 
						|
  @return Success                     A pointer to table share object
 | 
						|
          Failure                     NULL
 | 
						|
*/
 | 
						|
TMP_TABLE_SHARE *THD::find_tmp_table_share(const char *db,
 | 
						|
                                           const char *table_name)
 | 
						|
{
 | 
						|
  DBUG_ENTER("THD::find_tmp_table_share");
 | 
						|
 | 
						|
  TMP_TABLE_SHARE *share;
 | 
						|
  char key[MAX_DBKEY_LENGTH];
 | 
						|
  uint key_length;
 | 
						|
 | 
						|
  key_length= create_tmp_table_def_key(key, db, table_name);
 | 
						|
  share= find_tmp_table_share(key, key_length);
 | 
						|
 | 
						|
  DBUG_RETURN(share);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Lookup TMP_TABLE_SHARE using the specified TABLE_LIST element.
 | 
						|
  Return NULL is none found.
 | 
						|
 | 
						|
  @param tl [IN]                      Table
 | 
						|
 | 
						|
  @return Success                     A pointer to table share object
 | 
						|
          Failure                     NULL
 | 
						|
*/
 | 
						|
TMP_TABLE_SHARE *THD::find_tmp_table_share(const TABLE_LIST *tl)
 | 
						|
{
 | 
						|
  DBUG_ENTER("THD::find_tmp_table_share");
 | 
						|
  TMP_TABLE_SHARE *share= find_tmp_table_share(tl->get_db_name().str,
 | 
						|
                                               tl->get_table_name().str);
 | 
						|
  DBUG_RETURN(share);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Lookup TMP_TABLE_SHARE using the specified table definition key.
 | 
						|
  Return NULL is none found.
 | 
						|
 | 
						|
  @return Success                     A pointer to table share object
 | 
						|
          Failure                     NULL
 | 
						|
*/
 | 
						|
TMP_TABLE_SHARE *THD::find_tmp_table_share(const char *key, size_t key_length)
 | 
						|
{
 | 
						|
  DBUG_ENTER("THD::find_tmp_table_share");
 | 
						|
 | 
						|
  TMP_TABLE_SHARE *share;
 | 
						|
  TMP_TABLE_SHARE *result= NULL;
 | 
						|
  bool locked;
 | 
						|
 | 
						|
  if (!has_temporary_tables())
 | 
						|
  {
 | 
						|
    DBUG_RETURN(NULL);
 | 
						|
  }
 | 
						|
 | 
						|
  locked= lock_temporary_tables();
 | 
						|
 | 
						|
  All_tmp_tables_list::Iterator it(*temporary_tables);
 | 
						|
  while ((share= it++))
 | 
						|
  {
 | 
						|
    if (share->table_cache_key.length == key_length &&
 | 
						|
        !(memcmp(share->table_cache_key.str, key, key_length)))
 | 
						|
    {
 | 
						|
      result= share;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (locked)
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(m_tmp_tables_locked);
 | 
						|
    unlock_temporary_tables();
 | 
						|
  }
 | 
						|
 | 
						|
  DBUG_RETURN(result);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Find a temporary table specified by TABLE_LIST instance in the open table
 | 
						|
  list and prepare its TABLE instance for use. If
 | 
						|
 | 
						|
  This function tries to resolve this table in the list of temporary tables
 | 
						|
  of this thread. Temporary tables are thread-local and "shadow" base
 | 
						|
  tables with the same name.
 | 
						|
 | 
						|
  @note In most cases one should use THD::open_tables() instead
 | 
						|
        of this call.
 | 
						|
 | 
						|
  @note One should finalize process of opening temporary table for table
 | 
						|
        list element by calling open_and_process_table(). This function
 | 
						|
        is responsible for table version checking and handling of merge
 | 
						|
        tables.
 | 
						|
 | 
						|
  @note We used to check global_read_lock before opening temporary tables.
 | 
						|
        However, that limitation was artificial and is removed now.
 | 
						|
 | 
						|
  @param tl [IN]                      TABLE_LIST
 | 
						|
 | 
						|
  @return Error status.
 | 
						|
    @retval false                     On success. If a temporary table exists
 | 
						|
                                      for the given key, tl->table is set.
 | 
						|
    @retval true                      On error. my_error() has been called.
 | 
						|
*/
 | 
						|
bool THD::open_temporary_table(TABLE_LIST *tl)
 | 
						|
{
 | 
						|
  DBUG_ENTER("THD::open_temporary_table");
 | 
						|
  DBUG_PRINT("enter", ("table: '%s'.'%s'", tl->db.str, tl->table_name.str));
 | 
						|
 | 
						|
  TMP_TABLE_SHARE *share;
 | 
						|
  TABLE *table= NULL;
 | 
						|
 | 
						|
  /*
 | 
						|
    Code in open_table() assumes that TABLE_LIST::table can be non-zero only
 | 
						|
    for pre-opened temporary tables.
 | 
						|
  */
 | 
						|
  DBUG_ASSERT(tl->table == NULL);
 | 
						|
 | 
						|
  /*
 | 
						|
    This function should not be called for cases when derived or I_S
 | 
						|
    tables can be met since table list elements for such tables can
 | 
						|
    have invalid db or table name.
 | 
						|
    Instead THD::open_tables() should be used.
 | 
						|
  */
 | 
						|
  DBUG_ASSERT(!tl->derived);
 | 
						|
  DBUG_ASSERT(!tl->schema_table);
 | 
						|
  DBUG_ASSERT(has_temporary_tables() ||
 | 
						|
              (rgi_slave && rgi_slave->is_parallel_exec));
 | 
						|
 | 
						|
  if (tl->open_type == OT_BASE_ONLY)
 | 
						|
  {
 | 
						|
    DBUG_PRINT("info", ("skip_temporary is set or no temporary tables"));
 | 
						|
    DBUG_RETURN(false);
 | 
						|
  }
 | 
						|
 | 
						|
  if (!tl->db.str)
 | 
						|
  {
 | 
						|
    DBUG_PRINT("info",
 | 
						|
               ("Table reference to a temporary table must have database set"));
 | 
						|
    DBUG_RETURN(false);
 | 
						|
  }
 | 
						|
 | 
						|
  /*
 | 
						|
    Temporary tables are not safe for parallel replication. They were
 | 
						|
    designed to be visible to one thread only, so have no table locking.
 | 
						|
    Thus there is no protection against two conflicting transactions
 | 
						|
    committing in parallel and things like that.
 | 
						|
 | 
						|
    So for now, anything that uses temporary tables will be serialised
 | 
						|
    with anything before it, when using parallel replication.
 | 
						|
  */
 | 
						|
 | 
						|
  if (rgi_slave &&
 | 
						|
      rgi_slave->is_parallel_exec &&
 | 
						|
      find_temporary_table(tl) &&
 | 
						|
      wait_for_prior_commit())
 | 
						|
    DBUG_RETURN(true);
 | 
						|
 | 
						|
  /*
 | 
						|
    First check if there is a reusable open table available in the
 | 
						|
    open table list.
 | 
						|
  */
 | 
						|
  if (find_and_use_tmp_table(tl, &table))
 | 
						|
  {
 | 
						|
    DBUG_RETURN(true);                          /* Error */
 | 
						|
  }
 | 
						|
 | 
						|
  /*
 | 
						|
    No reusable table was found. We will have to open a new instance.
 | 
						|
  */
 | 
						|
  if (!table && (share= find_tmp_table_share(tl)))
 | 
						|
  {
 | 
						|
    table= open_temporary_table(share, tl->get_table_name().str);
 | 
						|
    /*
 | 
						|
       Temporary tables are not safe for parallel replication. They were
 | 
						|
       designed to be visible to one thread only, so have no table locking.
 | 
						|
       Thus there is no protection against two conflicting transactions
 | 
						|
       committing in parallel and things like that.
 | 
						|
 | 
						|
       So for now, anything that uses temporary tables will be serialised
 | 
						|
       with anything before it, when using parallel replication.
 | 
						|
    */
 | 
						|
    if (table && rgi_slave &&
 | 
						|
        rgi_slave->is_parallel_exec &&
 | 
						|
        wait_for_prior_commit())
 | 
						|
      DBUG_RETURN(true);
 | 
						|
 | 
						|
    if (!table && is_error())
 | 
						|
      DBUG_RETURN(true);                        // Error when opening table
 | 
						|
  }
 | 
						|
 | 
						|
  if (!table)
 | 
						|
  {
 | 
						|
    if (tl->open_type == OT_TEMPORARY_ONLY &&
 | 
						|
        tl->open_strategy == TABLE_LIST::OPEN_NORMAL)
 | 
						|
    {
 | 
						|
      my_error(ER_NO_SUCH_TABLE, MYF(0), tl->db.str, tl->table_name.str);
 | 
						|
      DBUG_RETURN(true);
 | 
						|
    }
 | 
						|
    DBUG_RETURN(false);
 | 
						|
  }
 | 
						|
 | 
						|
#ifdef WITH_PARTITION_STORAGE_ENGINE
 | 
						|
  if (tl->partition_names)
 | 
						|
  {
 | 
						|
    /* Partitioned temporary tables is not supported. */
 | 
						|
    DBUG_ASSERT(!table->part_info);
 | 
						|
    my_error(ER_PARTITION_CLAUSE_ON_NONPARTITIONED, MYF(0));
 | 
						|
    DBUG_RETURN(true);
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  table->query_id= query_id;
 | 
						|
  used|= THREAD_SPECIFIC_USED;
 | 
						|
 | 
						|
  /* It is neither a derived table nor non-updatable view. */
 | 
						|
  tl->updatable= true;
 | 
						|
  tl->table= table;
 | 
						|
 | 
						|
  table->init(this, tl);
 | 
						|
 | 
						|
  DBUG_PRINT("info", ("Using temporary table"));
 | 
						|
  DBUG_RETURN(false);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Pre-open temporary tables corresponding to table list elements.
 | 
						|
 | 
						|
  @note One should finalize process of opening temporary tables
 | 
						|
        by calling open_tables(). This function is responsible
 | 
						|
        for table version checking and handling of merge tables.
 | 
						|
 | 
						|
  @param tl [IN]                      TABLE_LIST
 | 
						|
 | 
						|
  @return false                       On success. If a temporary table exists
 | 
						|
                                      for the given element, tl->table is set.
 | 
						|
          true                        On error. my_error() has been called.
 | 
						|
*/
 | 
						|
bool THD::open_temporary_tables(TABLE_LIST *tl)
 | 
						|
{
 | 
						|
  TABLE_LIST *first_not_own;
 | 
						|
  DBUG_ENTER("THD::open_temporary_tables");
 | 
						|
 | 
						|
  if (!has_temporary_tables())
 | 
						|
    DBUG_RETURN(0);
 | 
						|
 | 
						|
  first_not_own= lex->first_not_own_table();
 | 
						|
  for (TABLE_LIST *table= tl; table && table != first_not_own;
 | 
						|
       table= table->next_global)
 | 
						|
  {
 | 
						|
    if (table->derived || table->schema_table)
 | 
						|
    {
 | 
						|
      /*
 | 
						|
        Derived and I_S tables will be handled by a later call to open_tables().
 | 
						|
      */
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    if (open_temporary_table(table))
 | 
						|
    {
 | 
						|
      DBUG_RETURN(true);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  DBUG_RETURN(false);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Close all temporary tables created by 'CREATE TEMPORARY TABLE' for thread
 | 
						|
  creates one DROP TEMPORARY TABLE binlog event for each pseudo-thread.
 | 
						|
 | 
						|
  Temporary tables created in a sql slave is closed by
 | 
						|
  Relay_log_info::close_temporary_tables().
 | 
						|
 | 
						|
  @return false                       Success
 | 
						|
          true                        Failure
 | 
						|
*/
 | 
						|
bool THD::close_temporary_tables()
 | 
						|
{
 | 
						|
  DBUG_ENTER("THD::close_temporary_tables");
 | 
						|
 | 
						|
  TMP_TABLE_SHARE *share;
 | 
						|
  TABLE *table;
 | 
						|
 | 
						|
  bool error= false;
 | 
						|
 | 
						|
  if (!has_thd_temporary_tables())
 | 
						|
  {
 | 
						|
    if (temporary_tables)
 | 
						|
    {
 | 
						|
      my_free(temporary_tables);
 | 
						|
      temporary_tables= NULL;
 | 
						|
    }
 | 
						|
    DBUG_RETURN(false);
 | 
						|
  }
 | 
						|
 | 
						|
  DBUG_ASSERT(!rgi_slave);
 | 
						|
 | 
						|
  /*
 | 
						|
    Ensure we don't have open HANDLERs for tables we are about to close.
 | 
						|
    This is necessary when THD::close_temporary_tables() is called as
 | 
						|
    part of execution of BINLOG statement (e.g. for format description event).
 | 
						|
  */
 | 
						|
  mysql_ha_rm_temporary_tables(this);
 | 
						|
 | 
						|
  /* Close all open temporary tables. */
 | 
						|
  All_tmp_tables_list::Iterator it(*temporary_tables);
 | 
						|
  while ((share= it++))
 | 
						|
  {
 | 
						|
    /* Traverse the table list. */
 | 
						|
    while ((table= share->all_tmp_tables.pop_front()))
 | 
						|
    {
 | 
						|
      table->file->extra(HA_EXTRA_PREPARE_FOR_DROP);
 | 
						|
      free_temporary_table(table);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // Write DROP TEMPORARY TABLE query log events to binary log.
 | 
						|
  if (mysql_bin_log.is_open())
 | 
						|
  {
 | 
						|
    error= log_events_and_free_tmp_shares();
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    while ((share= temporary_tables->pop_front()))
 | 
						|
    {
 | 
						|
      free_tmp_table_share(share, true);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /* By now, there mustn't be any elements left in the list. */
 | 
						|
  DBUG_ASSERT(temporary_tables->is_empty());
 | 
						|
 | 
						|
  my_free(temporary_tables);
 | 
						|
  temporary_tables= NULL;
 | 
						|
 | 
						|
  DBUG_RETURN(error);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Rename a temporary table.
 | 
						|
 | 
						|
  @param table [IN]                   Table handle
 | 
						|
  @param db [IN]                      New schema name
 | 
						|
  @param table_name [IN]              New table name
 | 
						|
 | 
						|
  @return false                       Success
 | 
						|
          true                        Error
 | 
						|
*/
 | 
						|
bool THD::rename_temporary_table(TABLE *table,
 | 
						|
                                 const LEX_CSTRING *db,
 | 
						|
                                 const LEX_CSTRING *table_name)
 | 
						|
{
 | 
						|
  char *key;
 | 
						|
  uint key_length;
 | 
						|
  TABLE_SHARE *share= table->s;
 | 
						|
  DBUG_ENTER("THD::rename_temporary_table");
 | 
						|
 | 
						|
  if (!(key= (char *) alloc_root(&share->mem_root, MAX_DBKEY_LENGTH)))
 | 
						|
    DBUG_RETURN(true);
 | 
						|
 | 
						|
  /*
 | 
						|
    Temporary tables are renamed by simply changing their table definition key.
 | 
						|
  */
 | 
						|
  key_length= create_tmp_table_def_key(key, db->str, table_name->str);
 | 
						|
  share->set_table_cache_key(key, key_length);
 | 
						|
 | 
						|
  DBUG_RETURN(false);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Drop a temporary table.
 | 
						|
 | 
						|
  Try to locate the table in the list of open temporary tables.
 | 
						|
  If the table is found:
 | 
						|
   - If the table is locked with LOCK TABLES or by prelocking,
 | 
						|
     unlock it and remove it from the list of locked tables
 | 
						|
     (THD::lock). Currently only transactional temporary tables
 | 
						|
     are locked.
 | 
						|
   - Close the temporary table, remove its .FRM.
 | 
						|
   - Remove the table share from the list of temporary table shares.
 | 
						|
 | 
						|
  This function is used to drop user temporary tables, as well as
 | 
						|
  internal tables created in CREATE TEMPORARY TABLE ... SELECT
 | 
						|
  or ALTER TABLE.
 | 
						|
 | 
						|
  @param table [IN]                   Temporary table to be deleted
 | 
						|
  @param is_trans [OUT]               Is set to the type of the table:
 | 
						|
                                      transactional (e.g. innodb) as true or
 | 
						|
                                      non-transactional (e.g. myisam) as false.
 | 
						|
  @paral delete_table [IN]            Whether to delete the table files?
 | 
						|
 | 
						|
  @return false                       Table was dropped
 | 
						|
          true                        Error
 | 
						|
*/
 | 
						|
bool THD::drop_temporary_table(TABLE *table, bool *is_trans, bool delete_table)
 | 
						|
{
 | 
						|
  DBUG_ENTER("THD::drop_temporary_table");
 | 
						|
 | 
						|
  TMP_TABLE_SHARE *share;
 | 
						|
  TABLE *tab;
 | 
						|
  bool result= false;
 | 
						|
  bool locked;
 | 
						|
 | 
						|
  DBUG_ASSERT(table);
 | 
						|
  DBUG_PRINT("tmptable", ("Dropping table: '%s'.'%s'",
 | 
						|
                          table->s->db.str, table->s->table_name.str));
 | 
						|
 | 
						|
  // close all handlers in case it is statement abort and some can be left
 | 
						|
  if (is_error())
 | 
						|
    table->file->ha_reset();
 | 
						|
 | 
						|
  locked= lock_temporary_tables();
 | 
						|
 | 
						|
  share= tmp_table_share(table);
 | 
						|
 | 
						|
  /* Table might be in use by some outer statement. */
 | 
						|
  All_share_tables_list::Iterator it(share->all_tmp_tables);
 | 
						|
  while ((tab= it++))
 | 
						|
  {
 | 
						|
    if (tab != table && tab->query_id != 0)
 | 
						|
    {
 | 
						|
      /* Found a table instance in use. This table cannot be be dropped. */
 | 
						|
      my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias.c_ptr());
 | 
						|
      result= true;
 | 
						|
      goto end;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (is_trans)
 | 
						|
    *is_trans= table->file->has_transactions();
 | 
						|
 | 
						|
  /*
 | 
						|
    Iterate over the list of open tables and close them.
 | 
						|
  */
 | 
						|
  while ((tab= share->all_tmp_tables.pop_front()))
 | 
						|
  {
 | 
						|
    /*
 | 
						|
      We need to set the THD as it may be different in case of
 | 
						|
      parallel replication
 | 
						|
    */
 | 
						|
    tab->in_use= this;
 | 
						|
    if (delete_table)
 | 
						|
      tab->file->extra(HA_EXTRA_PREPARE_FOR_DROP);
 | 
						|
    free_temporary_table(tab);
 | 
						|
  }
 | 
						|
 | 
						|
  DBUG_ASSERT(temporary_tables);
 | 
						|
 | 
						|
  /* Remove the TABLE_SHARE from the list of temporary tables. */
 | 
						|
  temporary_tables->remove(share);
 | 
						|
 | 
						|
  /* Free the TABLE_SHARE and/or delete the files. */
 | 
						|
  result= free_tmp_table_share(share, delete_table);
 | 
						|
 | 
						|
end:
 | 
						|
  if (locked)
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(m_tmp_tables_locked);
 | 
						|
    unlock_temporary_tables();
 | 
						|
  }
 | 
						|
 | 
						|
  DBUG_RETURN(result);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Delete the temporary table files.
 | 
						|
 | 
						|
  @param base [IN]                    Handlerton for table to be deleted.
 | 
						|
  @param path [IN]                    Path to the table to be deleted (i.e. path
 | 
						|
                                      to its .frm without an extension).
 | 
						|
 | 
						|
  @return false                       Success
 | 
						|
          true                        Error
 | 
						|
*/
 | 
						|
bool THD::rm_temporary_table(handlerton *base, const char *path)
 | 
						|
{
 | 
						|
  DBUG_ENTER("THD::rm_temporary_table");
 | 
						|
 | 
						|
  bool error= false;
 | 
						|
  char frm_path[FN_REFLEN + 1];
 | 
						|
 | 
						|
  strxnmov(frm_path, sizeof(frm_path) - 1, path, reg_ext, NullS);
 | 
						|
 | 
						|
  if (base->drop_table(base, path) > 0)
 | 
						|
  {
 | 
						|
    error= true;
 | 
						|
    sql_print_warning("Could not remove temporary table: '%s', error: %d",
 | 
						|
                      path, my_errno);
 | 
						|
  }
 | 
						|
 | 
						|
  if (mysql_file_delete(key_file_frm, frm_path,
 | 
						|
                        MYF(MY_WME | MY_IGNORE_ENOENT)))
 | 
						|
    error= true;
 | 
						|
 | 
						|
  DBUG_RETURN(error);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Mark all temporary tables which were used by the current statement or
 | 
						|
  sub-statement as free for reuse, but only if the query_id can be cleared.
 | 
						|
 | 
						|
  @remark For temp tables associated with a open SQL HANDLER the query_id
 | 
						|
          is not reset until the HANDLER is closed.
 | 
						|
*/
 | 
						|
void THD::mark_tmp_tables_as_free_for_reuse()
 | 
						|
{
 | 
						|
  DBUG_ENTER("THD::mark_tmp_tables_as_free_for_reuse");
 | 
						|
 | 
						|
  TMP_TABLE_SHARE *share;
 | 
						|
  TABLE *table;
 | 
						|
  bool locked;
 | 
						|
 | 
						|
  if (query_id == 0)
 | 
						|
  {
 | 
						|
    /*
 | 
						|
      Thread has not executed any statement and has not used any
 | 
						|
      temporary tables.
 | 
						|
    */
 | 
						|
    DBUG_VOID_RETURN;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!has_temporary_tables())
 | 
						|
  {
 | 
						|
    DBUG_VOID_RETURN;
 | 
						|
  }
 | 
						|
 | 
						|
  locked= lock_temporary_tables();
 | 
						|
 | 
						|
  All_tmp_tables_list::Iterator it(*temporary_tables);
 | 
						|
  while ((share= it++))
 | 
						|
  {
 | 
						|
    All_share_tables_list::Iterator tables_it(share->all_tmp_tables);
 | 
						|
    while ((table= tables_it++))
 | 
						|
    {
 | 
						|
      if ((table->query_id == query_id) && !table->open_by_handler)
 | 
						|
        mark_tmp_table_as_free_for_reuse(table);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (locked)
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(m_tmp_tables_locked);
 | 
						|
    unlock_temporary_tables();
 | 
						|
  }
 | 
						|
 | 
						|
  if (rgi_slave)
 | 
						|
  {
 | 
						|
    /*
 | 
						|
      Temporary tables are shared with other by sql execution threads.
 | 
						|
      As a safety measure, clear the pointer to the common area.
 | 
						|
    */
 | 
						|
    temporary_tables= NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  DBUG_VOID_RETURN;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Reset a single temporary table. Effectively this "closes" one temporary
 | 
						|
  table in a session.
 | 
						|
 | 
						|
  @param table              Temporary table
 | 
						|
 | 
						|
  @return void
 | 
						|
*/
 | 
						|
void THD::mark_tmp_table_as_free_for_reuse(TABLE *table)
 | 
						|
{
 | 
						|
  DBUG_ENTER("THD::mark_tmp_table_as_free_for_reuse");
 | 
						|
 | 
						|
  DBUG_ASSERT(table->s->tmp_table);
 | 
						|
 | 
						|
  table->query_id= 0;
 | 
						|
  table->file->ha_reset();
 | 
						|
 | 
						|
  /* Detach temporary MERGE children from temporary parent. */
 | 
						|
  DBUG_ASSERT(table->file);
 | 
						|
  table->file->extra(HA_EXTRA_DETACH_CHILDREN);
 | 
						|
 | 
						|
  /*
 | 
						|
    Reset temporary table lock type to it's default value (TL_WRITE).
 | 
						|
 | 
						|
    Statements such as INSERT INTO .. SELECT FROM tmp, CREATE TABLE
 | 
						|
    .. SELECT FROM tmp and UPDATE may under some circumstances modify
 | 
						|
    the lock type of the tables participating in the statement. This
 | 
						|
    isn't a problem for non-temporary tables since their lock type is
 | 
						|
    reset at every open, but the same does not occur for temporary
 | 
						|
    tables for historical reasons.
 | 
						|
 | 
						|
    Furthermore, the lock type of temporary tables is not really that
 | 
						|
    important because they can only be used by one query at a time.
 | 
						|
    Nonetheless, it's safer from a maintenance point of view to reset
 | 
						|
    the lock type of this singleton TABLE object as to not cause problems
 | 
						|
    when the table is reused.
 | 
						|
 | 
						|
    Even under LOCK TABLES mode its okay to reset the lock type as
 | 
						|
    LOCK TABLES is allowed (but ignored) for a temporary table.
 | 
						|
  */
 | 
						|
  table->reginfo.lock_type= TL_WRITE;
 | 
						|
  DBUG_VOID_RETURN;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Remove and return the specified table's TABLE_SHARE from the temporary
 | 
						|
  tables list.
 | 
						|
 | 
						|
  @param table [IN]                   Table
 | 
						|
 | 
						|
  @return TMP_TABLE_SHARE of the specified table.
 | 
						|
*/
 | 
						|
TMP_TABLE_SHARE *THD::save_tmp_table_share(TABLE *table)
 | 
						|
{
 | 
						|
  DBUG_ENTER("THD::save_tmp_table_share");
 | 
						|
 | 
						|
  TMP_TABLE_SHARE *share;
 | 
						|
 | 
						|
  lock_temporary_tables();
 | 
						|
  DBUG_ASSERT(temporary_tables);
 | 
						|
  share= tmp_table_share(table);
 | 
						|
  temporary_tables->remove(share);
 | 
						|
  unlock_temporary_tables();
 | 
						|
 | 
						|
  DBUG_RETURN(share);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Add the specified TMP_TABLE_SHARE to the temporary tables list.
 | 
						|
 | 
						|
  @param share [IN]                   Table share
 | 
						|
 | 
						|
  @return void
 | 
						|
*/
 | 
						|
void THD::restore_tmp_table_share(TMP_TABLE_SHARE *share)
 | 
						|
{
 | 
						|
  DBUG_ENTER("THD::restore_tmp_table_share");
 | 
						|
 | 
						|
  lock_temporary_tables();
 | 
						|
  DBUG_ASSERT(temporary_tables);
 | 
						|
  temporary_tables->push_front(share);
 | 
						|
  unlock_temporary_tables();
 | 
						|
 | 
						|
  DBUG_VOID_RETURN;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  If its a replication slave, report whether slave temporary tables
 | 
						|
  exist (Relay_log_info::save_temporary_tables) or report about THD
 | 
						|
  temporary table (Open_tables_state::temporary_tables) otherwise.
 | 
						|
 | 
						|
  @return false                       Temporary tables exist
 | 
						|
          true                        No temporary table exist
 | 
						|
*/
 | 
						|
bool THD::has_temporary_tables()
 | 
						|
{
 | 
						|
  DBUG_ENTER("THD::has_temporary_tables");
 | 
						|
  bool result;
 | 
						|
#ifdef HAVE_REPLICATION
 | 
						|
  if (rgi_slave)
 | 
						|
  {
 | 
						|
    mysql_mutex_lock(&rgi_slave->rli->data_lock);
 | 
						|
    result= rgi_slave->rli->save_temporary_tables &&
 | 
						|
      !rgi_slave->rli->save_temporary_tables->is_empty();
 | 
						|
    mysql_mutex_unlock(&rgi_slave->rli->data_lock);
 | 
						|
  }
 | 
						|
  else
 | 
						|
#endif
 | 
						|
  {
 | 
						|
    result= has_thd_temporary_tables();
 | 
						|
  }
 | 
						|
  DBUG_RETURN(result);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Create a table definition key.
 | 
						|
 | 
						|
  @param key [OUT]                    Buffer for the key to be created (must
 | 
						|
                                      be of size MAX_DBKRY_LENGTH)
 | 
						|
  @param db [IN]                      Database name
 | 
						|
  @param table_name [IN]              Table name
 | 
						|
 | 
						|
  @return                             Key length.
 | 
						|
 | 
						|
  @note
 | 
						|
    The table key is create from:
 | 
						|
    db + \0
 | 
						|
    table_name + \0
 | 
						|
 | 
						|
    Additionally, we add the following to make each temporary table unique on
 | 
						|
    the slave.
 | 
						|
 | 
						|
    4 bytes of master thread id
 | 
						|
    4 bytes of pseudo thread id
 | 
						|
*/
 | 
						|
uint THD::create_tmp_table_def_key(char *key, const char *db,
 | 
						|
                                    const char *table_name)
 | 
						|
{
 | 
						|
  uint key_length;
 | 
						|
  DBUG_ENTER("THD::create_tmp_table_def_key");
 | 
						|
 | 
						|
  key_length= tdc_create_key(key, db, table_name);
 | 
						|
  int4store(key + key_length, variables.server_id);
 | 
						|
  int4store(key + key_length + 4, variables.pseudo_thread_id);
 | 
						|
  key_length += TMP_TABLE_KEY_EXTRA;
 | 
						|
 | 
						|
  DBUG_RETURN(key_length);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Create a temporary table.
 | 
						|
 | 
						|
  @param frm  [IN]                    Binary frm image
 | 
						|
  @param path [IN]                    File path (without extension)
 | 
						|
  @param db   [IN]                    Schema name
 | 
						|
  @param table_name [IN]              Table name
 | 
						|
 | 
						|
  @return Success                     A pointer to table share object
 | 
						|
          Failure                     NULL
 | 
						|
*/
 | 
						|
TMP_TABLE_SHARE *THD::create_temporary_table(LEX_CUSTRING *frm,
 | 
						|
                                             const char *path,
 | 
						|
                                             const char *db,
 | 
						|
                                             const char *table_name)
 | 
						|
{
 | 
						|
  DBUG_ENTER("THD::create_temporary_table");
 | 
						|
 | 
						|
  TMP_TABLE_SHARE *share;
 | 
						|
  char key_cache[MAX_DBKEY_LENGTH];
 | 
						|
  char *saved_key_cache;
 | 
						|
  char *tmp_path;
 | 
						|
  uint key_length;
 | 
						|
  bool locked;
 | 
						|
  int res;
 | 
						|
 | 
						|
  /* Temporary tables are not safe for parallel replication. */
 | 
						|
  if (rgi_slave &&
 | 
						|
      rgi_slave->is_parallel_exec &&
 | 
						|
      wait_for_prior_commit())
 | 
						|
    DBUG_RETURN(NULL);
 | 
						|
 | 
						|
  /* Create the table definition key for the temporary table. */
 | 
						|
  key_length= create_tmp_table_def_key(key_cache, db, table_name);
 | 
						|
 | 
						|
  if (!(share= (TMP_TABLE_SHARE *) my_malloc(key_memory_table_share,
 | 
						|
                                             sizeof(TMP_TABLE_SHARE) +
 | 
						|
                                             strlen(path) + 1 + key_length,
 | 
						|
                                             MYF(MY_WME))))
 | 
						|
  {
 | 
						|
    DBUG_RETURN(NULL);                          /* Out of memory */
 | 
						|
  }
 | 
						|
 | 
						|
  tmp_path= (char *)(share + 1);
 | 
						|
  saved_key_cache= strmov(tmp_path, path) + 1;
 | 
						|
  memcpy(saved_key_cache, key_cache, key_length);
 | 
						|
 | 
						|
  init_tmp_table_share(this, share, saved_key_cache, key_length,
 | 
						|
                       strend(saved_key_cache) + 1, tmp_path);
 | 
						|
 | 
						|
  /*
 | 
						|
    Prefer using frm image over file. The image might not be available in
 | 
						|
    ALTER TABLE, when the discovering engine took over the ownership (see
 | 
						|
    TABLE::read_frm_image).
 | 
						|
  */
 | 
						|
  res= (frm->str)
 | 
						|
    ? share->init_from_binary_frm_image(this, false, frm->str, frm->length)
 | 
						|
    : open_table_def(this, share, GTS_TABLE | GTS_USE_DISCOVERY);
 | 
						|
 | 
						|
  if (res)
 | 
						|
  {
 | 
						|
    /*
 | 
						|
      No need to lock share->mutex as this is not needed for temporary tables.
 | 
						|
    */
 | 
						|
    free_table_share(share);
 | 
						|
    my_free(share);
 | 
						|
    DBUG_RETURN(NULL);
 | 
						|
  }
 | 
						|
 | 
						|
  share->m_psi= PSI_CALL_get_table_share(true, share);
 | 
						|
 | 
						|
  locked= lock_temporary_tables();
 | 
						|
 | 
						|
  /* Initialize the all_tmp_tables list. */
 | 
						|
  share->all_tmp_tables.empty();
 | 
						|
 | 
						|
  /*
 | 
						|
    We need to alloc & initialize temporary_tables if this happens
 | 
						|
    to be the very first temporary table.
 | 
						|
  */
 | 
						|
  if (!temporary_tables)
 | 
						|
  {
 | 
						|
    if ((temporary_tables=
 | 
						|
         (All_tmp_tables_list *) my_malloc(key_memory_table_share,
 | 
						|
                                           sizeof(All_tmp_tables_list),
 | 
						|
                                           MYF(MY_WME))))
 | 
						|
    {
 | 
						|
      temporary_tables->empty();
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      DBUG_RETURN(NULL);                        /* Out of memory */
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /* Add share to the head of the temporary table share list. */
 | 
						|
  temporary_tables->push_front(share);
 | 
						|
 | 
						|
  if (locked)
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(m_tmp_tables_locked);
 | 
						|
    unlock_temporary_tables();
 | 
						|
  }
 | 
						|
 | 
						|
  DBUG_RETURN(share);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Find a table with the specified key.
 | 
						|
 | 
						|
  @param key [IN]                     Key
 | 
						|
  @param key_length [IN]              Key length
 | 
						|
  @param state [IN]                   Open table state to look for
 | 
						|
 | 
						|
  @return Success                     Pointer to the table instance.
 | 
						|
          Failure                     NULL
 | 
						|
*/
 | 
						|
TABLE *THD::find_temporary_table(const char *key, uint key_length,
 | 
						|
                                 Temporary_table_state state)
 | 
						|
{
 | 
						|
  DBUG_ENTER("THD::find_temporary_table");
 | 
						|
 | 
						|
  TMP_TABLE_SHARE *share;
 | 
						|
  TABLE *table;
 | 
						|
  TABLE *result= NULL;
 | 
						|
  bool locked;
 | 
						|
 | 
						|
  locked= lock_temporary_tables();
 | 
						|
 | 
						|
  All_tmp_tables_list::Iterator it(*temporary_tables);
 | 
						|
  while ((share= it++))
 | 
						|
  {
 | 
						|
    if (share->table_cache_key.length == key_length &&
 | 
						|
        !(memcmp(share->table_cache_key.str, key, key_length)))
 | 
						|
    {
 | 
						|
      /* A matching TMP_TABLE_SHARE is found. */
 | 
						|
      All_share_tables_list::Iterator tables_it(share->all_tmp_tables);
 | 
						|
 | 
						|
      bool found= false;
 | 
						|
      while (!found && (table= tables_it++))
 | 
						|
      {
 | 
						|
        switch (state)
 | 
						|
        {
 | 
						|
        case TMP_TABLE_IN_USE:     found= table->query_id > 0;  break;
 | 
						|
        case TMP_TABLE_NOT_IN_USE: found= table->query_id == 0; break;
 | 
						|
        case TMP_TABLE_ANY:        found= true;                 break;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      if (table && unlikely(table->needs_reopen()))
 | 
						|
      {
 | 
						|
        share->all_tmp_tables.remove(table);
 | 
						|
        free_temporary_table(table);
 | 
						|
        it.rewind();
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      result= table;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (locked)
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(m_tmp_tables_locked);
 | 
						|
    unlock_temporary_tables();
 | 
						|
  }
 | 
						|
 | 
						|
  DBUG_RETURN(result);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Open a table from the specified TABLE_SHARE with the given alias.
 | 
						|
 | 
						|
  @param share [IN]                   Table share
 | 
						|
  @param alias [IN]                   Table alias
 | 
						|
 | 
						|
  @return Success                     A pointer to table object
 | 
						|
          Failure                     NULL
 | 
						|
*/
 | 
						|
TABLE *THD::open_temporary_table(TMP_TABLE_SHARE *share,
 | 
						|
                                 const char *alias_arg)
 | 
						|
{
 | 
						|
  TABLE *table;
 | 
						|
  LEX_CSTRING alias= {alias_arg, strlen(alias_arg) };
 | 
						|
  DBUG_ENTER("THD::open_temporary_table");
 | 
						|
 | 
						|
 | 
						|
  if (!(table= (TABLE *) my_malloc(key_memory_TABLE, sizeof(TABLE),
 | 
						|
                                   MYF(MY_WME))))
 | 
						|
  {
 | 
						|
    DBUG_RETURN(NULL);                          /* Out of memory */
 | 
						|
  }
 | 
						|
 | 
						|
  uint flags= ha_open_options | (open_options & HA_OPEN_FOR_CREATE);
 | 
						|
  /*
 | 
						|
    In replication, temporary tables are not confined to a single
 | 
						|
    thread/THD.
 | 
						|
  */
 | 
						|
  if (slave_thread)
 | 
						|
    flags|= HA_OPEN_GLOBAL_TMP_TABLE;
 | 
						|
  if (open_table_from_share(this, share, &alias,
 | 
						|
                            (uint) HA_OPEN_KEYFILE,
 | 
						|
                            EXTRA_RECORD, flags,
 | 
						|
                            table, false))
 | 
						|
  {
 | 
						|
    my_free(table);
 | 
						|
    DBUG_RETURN(NULL);
 | 
						|
  }
 | 
						|
 | 
						|
  table->reginfo.lock_type= TL_WRITE;           /* Simulate locked */
 | 
						|
  table->grant.privilege= TMP_TABLE_ACLS;
 | 
						|
  table->query_id= query_id;
 | 
						|
  share->tmp_table= (table->file->has_transaction_manager() ?
 | 
						|
                     TRANSACTIONAL_TMP_TABLE : NON_TRANSACTIONAL_TMP_TABLE);
 | 
						|
  share->not_usable_by_query_cache= 1;
 | 
						|
 | 
						|
  /* Add table to the head of table list. */
 | 
						|
  share->all_tmp_tables.push_front(table);
 | 
						|
 | 
						|
  /* Increment Slave_open_temp_table_definitions status variable count. */
 | 
						|
  if (rgi_slave)
 | 
						|
    slave_open_temp_tables++;
 | 
						|
 | 
						|
  DBUG_PRINT("tmptable", ("Opened table: '%s'.'%s  table: %p",
 | 
						|
                          table->s->db.str,
 | 
						|
                          table->s->table_name.str, table));
 | 
						|
  DBUG_RETURN(table);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Find a reusable table in the open table list using the specified TABLE_LIST.
 | 
						|
 | 
						|
  @param tl [IN]                      Table list
 | 
						|
  @param out_table [OUT]              Pointer to the requested TABLE object
 | 
						|
 | 
						|
  @return Success                     false
 | 
						|
          Failure                     true
 | 
						|
*/
 | 
						|
bool THD::find_and_use_tmp_table(const TABLE_LIST *tl, TABLE **out_table)
 | 
						|
{
 | 
						|
  char key[MAX_DBKEY_LENGTH];
 | 
						|
  uint key_length;
 | 
						|
  bool result;
 | 
						|
  DBUG_ENTER("THD::find_and_use_tmp_table");
 | 
						|
 | 
						|
  key_length= create_tmp_table_def_key(key, tl->get_db_name().str,
 | 
						|
                                        tl->get_table_name().str);
 | 
						|
  result= use_temporary_table(find_temporary_table(key, key_length,
 | 
						|
                                                   TMP_TABLE_NOT_IN_USE),
 | 
						|
                              out_table);
 | 
						|
  DBUG_RETURN(result);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Mark table as in-use.
 | 
						|
 | 
						|
  @param table [IN]                   Table to be marked in-use
 | 
						|
  @param out_table [OUT]              Pointer to the specified table
 | 
						|
 | 
						|
  @return false                       Success
 | 
						|
          true                        Error
 | 
						|
*/
 | 
						|
bool THD::use_temporary_table(TABLE *table, TABLE **out_table)
 | 
						|
{
 | 
						|
  DBUG_ENTER("THD::use_temporary_table");
 | 
						|
 | 
						|
  *out_table= table;
 | 
						|
 | 
						|
  /* The following can happen if find_temporary_table() returns NULL */
 | 
						|
  if (!table)
 | 
						|
    DBUG_RETURN(false);
 | 
						|
 | 
						|
  /*
 | 
						|
    Temporary tables are not safe for parallel replication. They were
 | 
						|
    designed to be visible to one thread only, so have no table locking.
 | 
						|
    Thus there is no protection against two conflicting transactions
 | 
						|
    committing in parallel and things like that.
 | 
						|
 | 
						|
    So for now, anything that uses temporary tables will be serialised
 | 
						|
    with anything before it, when using parallel replication.
 | 
						|
 | 
						|
    TODO: We might be able to introduce a reference count or something
 | 
						|
    on temp tables, and have slave worker threads wait for it to reach
 | 
						|
    zero before being allowed to use the temp table. Might not be worth
 | 
						|
    it though, as statement-based replication using temporary tables is
 | 
						|
    in any case rather fragile.
 | 
						|
  */
 | 
						|
  if (rgi_slave &&
 | 
						|
      rgi_slave->is_parallel_exec &&
 | 
						|
      wait_for_prior_commit())
 | 
						|
    DBUG_RETURN(true);
 | 
						|
 | 
						|
  /*
 | 
						|
    We need to set the THD as it may be different in case of
 | 
						|
    parallel replication
 | 
						|
  */
 | 
						|
  table->in_use= this;
 | 
						|
 | 
						|
  DBUG_RETURN(false);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Close a temporary table.
 | 
						|
 | 
						|
  @param table [IN]                   Table handle
 | 
						|
 | 
						|
  @return void
 | 
						|
*/
 | 
						|
void THD::close_temporary_table(TABLE *table)
 | 
						|
{
 | 
						|
  DBUG_ENTER("THD::close_temporary_table");
 | 
						|
 | 
						|
  DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'%p  alias: '%s'",
 | 
						|
                          table->s->db.str, table->s->table_name.str,
 | 
						|
                          table, table->alias.c_ptr()));
 | 
						|
 | 
						|
  closefrm(table);
 | 
						|
  my_free(table);
 | 
						|
 | 
						|
  if (rgi_slave)
 | 
						|
  {
 | 
						|
    /* Natural invariant of temporary_tables */
 | 
						|
    DBUG_ASSERT(slave_open_temp_tables || !temporary_tables);
 | 
						|
    /* Decrement Slave_open_temp_table_definitions status variable count. */
 | 
						|
    slave_open_temp_tables--;
 | 
						|
  }
 | 
						|
 | 
						|
  DBUG_VOID_RETURN;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Write query log events with "DROP TEMPORARY TABLES .." for each pseudo
 | 
						|
  thread to the binary log.
 | 
						|
 | 
						|
  @return false                       Success
 | 
						|
          true                        Error
 | 
						|
*/
 | 
						|
bool THD::log_events_and_free_tmp_shares()
 | 
						|
{
 | 
						|
  DBUG_ENTER("THD::log_events_and_free_tmp_shares");
 | 
						|
 | 
						|
  DBUG_ASSERT(!rgi_slave);
 | 
						|
 | 
						|
  TMP_TABLE_SHARE *share;
 | 
						|
  TMP_TABLE_SHARE *sorted;
 | 
						|
  TMP_TABLE_SHARE *prev_sorted;
 | 
						|
  // Assume thd->variables.option_bits has OPTION_QUOTE_SHOW_CREATE.
 | 
						|
  bool was_quote_show= true;
 | 
						|
  bool error= false;
 | 
						|
  bool found_user_tables= false;
 | 
						|
  // Better add "IF EXISTS" in case a RESET MASTER has been done.
 | 
						|
  const char stub[]= "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS ";
 | 
						|
  char buf[FN_REFLEN];
 | 
						|
 | 
						|
  String s_query(buf, sizeof(buf), system_charset_info);
 | 
						|
  s_query.copy(stub, sizeof(stub) - 1, system_charset_info);
 | 
						|
 | 
						|
  /*
 | 
						|
    Insertion sort of temporary tables by pseudo_thread_id to build ordered
 | 
						|
    list of sublists of equal pseudo_thread_id.
 | 
						|
  */
 | 
						|
  All_tmp_tables_list::Iterator it_sorted(*temporary_tables);
 | 
						|
  All_tmp_tables_list::Iterator it_unsorted(*temporary_tables);
 | 
						|
  uint sorted_count= 0;
 | 
						|
  while((share= it_unsorted++))
 | 
						|
  {
 | 
						|
    if (IS_USER_TABLE(share))
 | 
						|
    {
 | 
						|
      prev_sorted= NULL;
 | 
						|
 | 
						|
      if (!found_user_tables) found_user_tables= true;
 | 
						|
 | 
						|
      for (uint i= 0; i < sorted_count; i ++)
 | 
						|
      {
 | 
						|
        sorted= it_sorted ++;
 | 
						|
 | 
						|
        if (!IS_USER_TABLE(sorted) ||
 | 
						|
            (tmpkeyval(sorted) > tmpkeyval(share)))
 | 
						|
        {
 | 
						|
          /*
 | 
						|
            Insert this share before the current element in
 | 
						|
            the sorted part of the list.
 | 
						|
          */
 | 
						|
          temporary_tables->remove(share);
 | 
						|
 | 
						|
          if (prev_sorted)
 | 
						|
          {
 | 
						|
            temporary_tables->insert_after(prev_sorted, share);
 | 
						|
          }
 | 
						|
          else
 | 
						|
          {
 | 
						|
            temporary_tables->push_front(share);
 | 
						|
          }
 | 
						|
          break;
 | 
						|
        }
 | 
						|
        prev_sorted= sorted;
 | 
						|
      }
 | 
						|
      it_sorted.rewind();
 | 
						|
    }
 | 
						|
    sorted_count ++;
 | 
						|
  }
 | 
						|
 | 
						|
  /*
 | 
						|
    We always quote db & table names.
 | 
						|
  */
 | 
						|
  if (found_user_tables &&
 | 
						|
      !(was_quote_show= MY_TEST(variables.option_bits &
 | 
						|
                                OPTION_QUOTE_SHOW_CREATE)))
 | 
						|
  {
 | 
						|
    variables.option_bits |= OPTION_QUOTE_SHOW_CREATE;
 | 
						|
  }
 | 
						|
 | 
						|
  /*
 | 
						|
    Scan sorted temporary tables to generate sequence of DROP.
 | 
						|
  */
 | 
						|
  share= temporary_tables->pop_front();
 | 
						|
  while (share)
 | 
						|
  {
 | 
						|
    if (IS_USER_TABLE(share))
 | 
						|
    {
 | 
						|
      used_t save_thread_specific_used= used & THREAD_SPECIFIC_USED;
 | 
						|
      my_thread_id save_pseudo_thread_id= variables.pseudo_thread_id;
 | 
						|
      char db_buf[FN_REFLEN];
 | 
						|
      String db(db_buf, sizeof(db_buf), system_charset_info);
 | 
						|
      bool at_least_one_create_logged;
 | 
						|
 | 
						|
      /*
 | 
						|
        Set pseudo_thread_id to be that of the processed table.
 | 
						|
      */
 | 
						|
      variables.pseudo_thread_id= tmpkeyval(share);
 | 
						|
 | 
						|
      db.copy(share->db.str, share->db.length, system_charset_info);
 | 
						|
      /*
 | 
						|
        Reset s_query() if changed by previous loop.
 | 
						|
      */
 | 
						|
      s_query.length(sizeof(stub) - 1);
 | 
						|
 | 
						|
      /*
 | 
						|
        Loop forward through all tables that belong to a common database
 | 
						|
        within the sublist of common pseudo_thread_id to create single
 | 
						|
        DROP query.
 | 
						|
      */
 | 
						|
      for (at_least_one_create_logged= false;
 | 
						|
           share && IS_USER_TABLE(share) &&
 | 
						|
           tmpkeyval(share) == variables.pseudo_thread_id &&
 | 
						|
           share->db.length == db.length() &&
 | 
						|
           memcmp(share->db.str, db.ptr(), db.length()) == 0;
 | 
						|
           /* Get the next TABLE_SHARE in the list. */
 | 
						|
           share= temporary_tables->pop_front())
 | 
						|
      {
 | 
						|
        if (share->table_creation_was_logged)
 | 
						|
        {
 | 
						|
          at_least_one_create_logged= true;
 | 
						|
          /*
 | 
						|
             We are going to add ` around the table names and possible more
 | 
						|
             due to special characters.
 | 
						|
          */
 | 
						|
          append_identifier(this, &s_query, &share->table_name);
 | 
						|
          s_query.append(',');
 | 
						|
        }
 | 
						|
        rm_temporary_table(share->db_type(), share->path.str);
 | 
						|
        free_table_share(share);
 | 
						|
        my_free(share);
 | 
						|
      }
 | 
						|
 | 
						|
      if (at_least_one_create_logged)
 | 
						|
      {
 | 
						|
        clear_error();
 | 
						|
        CHARSET_INFO *cs_save= variables.character_set_client;
 | 
						|
        variables.character_set_client= system_charset_info;
 | 
						|
        used|= THREAD_SPECIFIC_USED;
 | 
						|
 | 
						|
        Query_log_event qinfo(this, s_query.ptr(),
 | 
						|
            s_query.length() - 1 /* to remove trailing ',' */,
 | 
						|
            false, true, false, 0);
 | 
						|
        qinfo.db= db.ptr();
 | 
						|
        qinfo.db_len= db.length();
 | 
						|
        variables.character_set_client= cs_save;
 | 
						|
 | 
						|
        get_stmt_da()->set_overwrite_status(true);
 | 
						|
        transaction->stmt.mark_dropped_temp_table();
 | 
						|
        bool error2= mysql_bin_log.write(&qinfo);
 | 
						|
        if (unlikely(error|= error2))
 | 
						|
        {
 | 
						|
          /*
 | 
						|
             If we're here following THD::cleanup, thence the connection
 | 
						|
             has been closed already. So lets print a message to the
 | 
						|
             error log instead of pushing yet another error into the
 | 
						|
             stmt_da.
 | 
						|
 | 
						|
             Also, we keep the error flag so that we propagate the error
 | 
						|
             up in the stack. This way, if we're the SQL thread we notice
 | 
						|
             that THD::close_tables failed. (Actually, the SQL
 | 
						|
             thread only calls THD::close_tables while applying
 | 
						|
             old Start_log_event_v3 events.)
 | 
						|
          */
 | 
						|
          sql_print_error("Failed to write the DROP statement for "
 | 
						|
              "temporary tables to binary log");
 | 
						|
        }
 | 
						|
 | 
						|
        get_stmt_da()->set_overwrite_status(false);
 | 
						|
      }
 | 
						|
      variables.pseudo_thread_id= save_pseudo_thread_id;
 | 
						|
      used = (used & ~THREAD_SPECIFIC_USED) | save_thread_specific_used;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      free_tmp_table_share(share, true);
 | 
						|
      /* Get the next TABLE_SHARE in the list. */
 | 
						|
      share= temporary_tables->pop_front();
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (!was_quote_show)
 | 
						|
  {
 | 
						|
    /*
 | 
						|
      Restore option.
 | 
						|
    */
 | 
						|
    variables.option_bits&= ~OPTION_QUOTE_SHOW_CREATE;
 | 
						|
  }
 | 
						|
 | 
						|
  DBUG_RETURN(error);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Delete the files and free the specified table share.
 | 
						|
 | 
						|
  @param share [IN]                   TABLE_SHARE to free
 | 
						|
  @param delete_table [IN]            Whether to delete the table files?
 | 
						|
 | 
						|
  @return false                       Success
 | 
						|
          true                        Error
 | 
						|
*/
 | 
						|
bool THD::free_tmp_table_share(TMP_TABLE_SHARE *share, bool delete_table)
 | 
						|
{
 | 
						|
  bool error= false;
 | 
						|
  DBUG_ENTER("THD::free_tmp_table_share");
 | 
						|
 | 
						|
  if (delete_table)
 | 
						|
  {
 | 
						|
    error= rm_temporary_table(share->db_type(), share->path.str);
 | 
						|
  }
 | 
						|
  free_table_share(share);
 | 
						|
  my_free(share);
 | 
						|
  DBUG_RETURN(error);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Free the specified table object.
 | 
						|
 | 
						|
  @param table [IN]                   Table object to free.
 | 
						|
 | 
						|
  @return void
 | 
						|
*/
 | 
						|
void THD::free_temporary_table(TABLE *table)
 | 
						|
{
 | 
						|
  DBUG_ENTER("THD::free_temporary_table");
 | 
						|
 | 
						|
  /*
 | 
						|
    If LOCK TABLES list is not empty and contains this table, unlock the table
 | 
						|
    and remove the table from this list.
 | 
						|
  */
 | 
						|
  mysql_lock_remove(this, lock, table);
 | 
						|
 | 
						|
  close_temporary_table(table);
 | 
						|
 | 
						|
  DBUG_VOID_RETURN;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  On replication slave, acquire the Relay_log_info's data_lock and use slave
 | 
						|
  temporary tables.
 | 
						|
 | 
						|
  @return true                        Lock acquired
 | 
						|
          false                       Lock wasn't acquired
 | 
						|
*/
 | 
						|
bool THD::lock_temporary_tables()
 | 
						|
{
 | 
						|
  DBUG_ENTER("THD::lock_temporary_tables");
 | 
						|
 | 
						|
  /* Do not proceed if a lock has already been taken. */
 | 
						|
  if (m_tmp_tables_locked)
 | 
						|
  {
 | 
						|
    DBUG_RETURN(false);
 | 
						|
  }
 | 
						|
 | 
						|
#ifdef HAVE_REPLICATION
 | 
						|
  if (rgi_slave)
 | 
						|
  {
 | 
						|
    mysql_mutex_lock(&rgi_slave->rli->data_lock);
 | 
						|
    temporary_tables= rgi_slave->rli->save_temporary_tables;
 | 
						|
    m_tmp_tables_locked= true;
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  DBUG_RETURN(m_tmp_tables_locked);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  On replication slave, release the Relay_log_info::data_lock previously
 | 
						|
  acquired to use slave temporary tables.
 | 
						|
 | 
						|
  @return void
 | 
						|
*/
 | 
						|
void THD::unlock_temporary_tables()
 | 
						|
{
 | 
						|
  DBUG_ENTER("THD::unlock_temporary_tables");
 | 
						|
 | 
						|
  if (!m_tmp_tables_locked)
 | 
						|
  {
 | 
						|
    DBUG_VOID_RETURN;
 | 
						|
  }
 | 
						|
 | 
						|
#ifdef HAVE_REPLICATION
 | 
						|
  if (rgi_slave)
 | 
						|
  {
 | 
						|
    rgi_slave->rli->save_temporary_tables= temporary_tables;
 | 
						|
    temporary_tables= NULL;                     /* Safety */
 | 
						|
    mysql_mutex_unlock(&rgi_slave->rli->data_lock);
 | 
						|
    m_tmp_tables_locked= false;
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  DBUG_VOID_RETURN;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Close unused TABLE instances for given temporary table.
 | 
						|
 | 
						|
  @param tl [IN]                      TABLE_LIST
 | 
						|
 | 
						|
  Initial use case was TRUNCATE, which expects only one instance (which is used
 | 
						|
  by TRUNCATE itself) to be open. Most probably some ALTER TABLE variants and
 | 
						|
  REPAIR may have similar expectations.
 | 
						|
*/
 | 
						|
 | 
						|
void THD::close_unused_temporary_table_instances(const TABLE_LIST *tl)
 | 
						|
{
 | 
						|
  TMP_TABLE_SHARE *share= find_tmp_table_share(tl);
 | 
						|
 | 
						|
  if (share)
 | 
						|
  {
 | 
						|
    All_share_tables_list::Iterator tables_it(share->all_tmp_tables);
 | 
						|
 | 
						|
     while (TABLE *table= tables_it++)
 | 
						|
     {
 | 
						|
       if (table->query_id == 0)
 | 
						|
       {
 | 
						|
         /* Note: removing current list element doesn't invalidate iterator. */
 | 
						|
         share->all_tmp_tables.remove(table);
 | 
						|
         /*
 | 
						|
           At least one instance should be left (guaratead by calling this
 | 
						|
           function for table which is opened and the table is under processing)
 | 
						|
         */
 | 
						|
         DBUG_ASSERT(share->all_tmp_tables.front());
 | 
						|
         free_temporary_table(table);
 | 
						|
       }
 | 
						|
     }
 | 
						|
  }
 | 
						|
}
 |