mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-31 02:46:29 +01:00 
			
		
		
		
	 4992aaf9a2
			
		
	
	
	4992aaf9a2
	
	
	
		
			
			Remove not anymore used
bool ddl_log_write_execute_entry(uint first_entry,
                                 DDL_LOG_MEMORY_ENTRY **active_entry)
Simple transformations, no logic changes.
		
	
			
		
			
				
	
	
		
			1053 lines
		
	
	
	
		
			36 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1053 lines
		
	
	
	
		
			36 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
 | |
|    Copyright (c) 2014, SkySQL Ab.
 | |
|    Copyright (c) 2016, 2018, 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-1335  USA */
 | |
| 
 | |
| #include "mariadb.h"
 | |
| #include "sql_parse.h"                      // check_one_table_access
 | |
|                                             // check_merge_table_access
 | |
|                                             // check_one_table_access
 | |
| #include "sql_table.h"                      // mysql_alter_table, etc.
 | |
| #include "sql_cmd.h"                        // Sql_cmd
 | |
| #include "sql_alter.h"                      // Sql_cmd_alter_table
 | |
| #include "sql_partition.h"                  // struct partition_info, etc.
 | |
| #include "debug_sync.h"                     // DEBUG_SYNC
 | |
| #include "sql_truncate.h"                   // mysql_truncate_table,
 | |
|                                             // Sql_cmd_truncate_table
 | |
| #include "sql_admin.h"                      // Analyze/Check/.._table_statement
 | |
| #include "sql_partition_admin.h"            // Alter_table_*_partition
 | |
| #ifdef WITH_PARTITION_STORAGE_ENGINE
 | |
| #include "ha_partition.h"                   // ha_partition
 | |
| #endif
 | |
| #include "sql_base.h"                       // open_and_lock_tables
 | |
| #include "ddl_log.h"
 | |
| #include "wsrep_mysqld.h"
 | |
| 
 | |
| #ifndef WITH_PARTITION_STORAGE_ENGINE
 | |
| 
 | |
| bool Sql_cmd_partition_unsupported::execute(THD *)
 | |
| {
 | |
|   DBUG_ENTER("Sql_cmd_partition_unsupported::execute");
 | |
|   /* error, partitioning support not compiled in... */
 | |
|   my_error(ER_FEATURE_DISABLED, MYF(0), "partitioning",
 | |
|            "--with-plugin-partition");
 | |
|   DBUG_RETURN(TRUE);
 | |
| }
 | |
| 
 | |
| #else
 | |
| 
 | |
| static bool return_with_logging(THD *thd)
 | |
| {
 | |
|   if (thd->slave_thread &&
 | |
|       write_bin_log_with_if_exists(thd, true, false, true))
 | |
|     return(true);
 | |
|   my_ok(thd);
 | |
|   return(false);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Sql_cmd_alter_table_exchange_partition::execute(THD *thd)
 | |
| {
 | |
|   /* Moved from mysql_execute_command */
 | |
|   LEX *lex= thd->lex;
 | |
|   /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */
 | |
|   SELECT_LEX *select_lex= lex->first_select_lex();
 | |
|   /* first table of first SELECT_LEX */
 | |
|   TABLE_LIST *first_table= (TABLE_LIST*) select_lex->table_list.first;
 | |
|   /*
 | |
|     Code in mysql_alter_table() may modify its HA_CREATE_INFO argument,
 | |
|     so we have to use a copy of this structure to make execution
 | |
|     prepared statement- safe. A shallow copy is enough as no memory
 | |
|     referenced from this structure will be modified.
 | |
|     @todo move these into constructor...
 | |
|   */
 | |
|   IF_DBUG(HA_CREATE_INFO create_info(lex->create_info);,)
 | |
|   Alter_info alter_info(lex->alter_info, thd->mem_root);
 | |
|   privilege_t priv_needed(ALTER_ACL | DROP_ACL | INSERT_ACL | CREATE_ACL);
 | |
| 
 | |
|   DBUG_ENTER("Sql_cmd_alter_table_exchange_partition::execute");
 | |
| 
 | |
|   if (unlikely(thd->is_fatal_error))
 | |
|   {
 | |
|     /* out of memory creating a copy of alter_info */
 | |
|     DBUG_RETURN(TRUE);
 | |
|   }
 | |
| 
 | |
|   /* Must be set in the parser */
 | |
|   DBUG_ASSERT(select_lex->db.str);
 | |
|   /* also check the table to be exchanged with the partition */
 | |
|   DBUG_ASSERT(alter_info.partition_flags & ALTER_PARTITION_EXCHANGE);
 | |
| 
 | |
|   if (unlikely(check_access(thd, priv_needed, first_table->db.str,
 | |
|                             &first_table->grant.privilege,
 | |
|                             &first_table->grant.m_internal,
 | |
|                             0, 0)) ||
 | |
|       unlikely(check_access(thd, priv_needed, first_table->next_local->db.str,
 | |
|                             &first_table->next_local->grant.privilege,
 | |
|                             &first_table->next_local->grant.m_internal,
 | |
|                             0, 0)))
 | |
|     DBUG_RETURN(TRUE);
 | |
| 
 | |
|   if (unlikely(check_grant(thd, priv_needed, first_table, FALSE, UINT_MAX,
 | |
|                            FALSE)))
 | |
|     DBUG_RETURN(TRUE);
 | |
| 
 | |
|   /* Not allowed with EXCHANGE PARTITION */
 | |
|   DBUG_ASSERT(!create_info.data_file_name && !create_info.index_file_name);
 | |
|   WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table);
 | |
| 
 | |
|   DBUG_RETURN(exchange_partition(thd, first_table, &alter_info));
 | |
| #ifdef WITH_WSREP
 | |
|  wsrep_error_label:
 | |
|   /* handle errors in TO_ISOLATION here */
 | |
|   DBUG_RETURN(true);
 | |
| #endif /* WITH_WSREP */
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   @brief Checks that the tables will be able to be used for EXCHANGE PARTITION.
 | |
|   @param table      Non partitioned table.
 | |
|   @param part_table Partitioned table.
 | |
| 
 | |
|   @retval FALSE if OK, otherwise error is reported and TRUE is returned.
 | |
| */
 | |
| 
 | |
| static bool check_exchange_partition(TABLE *table, TABLE *part_table)
 | |
| {
 | |
|   DBUG_ENTER("check_exchange_partition");
 | |
| 
 | |
|   /* Both tables must exist */
 | |
|   if (unlikely(!part_table || !table))
 | |
|   {
 | |
|     my_error(ER_CHECK_NO_SUCH_TABLE, MYF(0));
 | |
|     DBUG_RETURN(TRUE);
 | |
|   }
 | |
| 
 | |
|   /* The first table must be partitioned, and the second must not */
 | |
|   if (unlikely(!part_table->part_info))
 | |
|   {
 | |
|     my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
 | |
|     DBUG_RETURN(TRUE);
 | |
|   }
 | |
|   if (unlikely(table->part_info))
 | |
|   {
 | |
|     my_error(ER_PARTITION_EXCHANGE_PART_TABLE, MYF(0),
 | |
|              table->s->table_name.str);
 | |
|     DBUG_RETURN(TRUE);
 | |
|   }
 | |
| 
 | |
|   if (unlikely(part_table->file->ht != partition_hton))
 | |
|   {
 | |
|     /*
 | |
|       Only allowed on partitioned tables throughout the generic ha_partition
 | |
|       handler, i.e not yet for native partitioning.
 | |
|     */
 | |
|     my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
 | |
|     DBUG_RETURN(TRUE);
 | |
|   }
 | |
| 
 | |
|   if (unlikely(table->file->ht != part_table->part_info->default_engine_type))
 | |
|   {
 | |
|     my_error(ER_MIX_HANDLER_ERROR, MYF(0));
 | |
|     DBUG_RETURN(TRUE);
 | |
|   }
 | |
| 
 | |
|   /* Verify that table is not tmp table, partitioned tables cannot be tmp. */
 | |
|   if (unlikely(table->s->tmp_table != NO_TMP_TABLE))
 | |
|   {
 | |
|     my_error(ER_PARTITION_EXCHANGE_TEMP_TABLE, MYF(0),
 | |
|              table->s->table_name.str);
 | |
|     DBUG_RETURN(TRUE);
 | |
|   }
 | |
| 
 | |
|   /* The table cannot have foreign keys constraints or be referenced */
 | |
|   if (unlikely(!table->file->can_switch_engines()))
 | |
|   {
 | |
|     my_error(ER_PARTITION_EXCHANGE_FOREIGN_KEY, MYF(0),
 | |
|              table->s->table_name.str);
 | |
|     DBUG_RETURN(TRUE);
 | |
|   }
 | |
|   DBUG_RETURN(FALSE);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   @brief Compare table structure/options between a non partitioned table
 | |
|   and a specific partition of a partitioned table.
 | |
| 
 | |
|   @param thd        Thread object.
 | |
|   @param table      Non partitioned table.
 | |
|   @param part_table Partitioned table.
 | |
|   @param part_elem  Partition element to use for partition specific compare.
 | |
| */
 | |
| bool compare_table_with_partition(THD *thd, TABLE *table, TABLE *part_table,
 | |
|                                   partition_element *part_elem, uint part_id)
 | |
| {
 | |
|   HA_CREATE_INFO table_create_info;
 | |
|   Table_specification_st part_create_info;
 | |
|   Alter_info part_alter_info;
 | |
|   Alter_table_ctx part_alter_ctx; // Not used
 | |
|   DBUG_ENTER("compare_table_with_partition");
 | |
| 
 | |
|   bool metadata_equal= false;
 | |
|   part_create_info.init();
 | |
|   table_create_info.init();
 | |
| 
 | |
|   update_create_info_from_table(&table_create_info, table);
 | |
|   /* get the current auto_increment value */
 | |
|   table->file->update_create_info(&table_create_info);
 | |
|   /* mark all columns used, since they are used when preparing the new table */
 | |
|   part_table->use_all_columns();
 | |
|   table->use_all_columns();
 | |
|   if (unlikely(mysql_prepare_alter_table(thd, part_table, &part_create_info,
 | |
|                                          &part_alter_info, &part_alter_ctx)))
 | |
|   {
 | |
|     my_error(ER_TABLES_DIFFERENT_METADATA, MYF(0));
 | |
|     DBUG_RETURN(TRUE);
 | |
|   }
 | |
|   /* db_type is not set in prepare_alter_table */
 | |
|   part_create_info.db_type= part_table->part_info->default_engine_type;
 | |
|   ((ha_partition*)(part_table->file))->update_part_create_info(&part_create_info, part_id);
 | |
|   /*
 | |
|     Since we exchange the partition with the table, allow exchanging
 | |
|     auto_increment value as well.
 | |
|   */
 | |
|   part_create_info.auto_increment_value=
 | |
|                                 table_create_info.auto_increment_value;
 | |
| 
 | |
|   /* Check compatible row_types and set create_info accordingly. */
 | |
|   {
 | |
|     enum row_type part_row_type= part_table->file->get_row_type();
 | |
|     enum row_type table_row_type= table->file->get_row_type();
 | |
|     if (part_row_type != table_row_type)
 | |
|     {
 | |
|       my_error(ER_PARTITION_EXCHANGE_DIFFERENT_OPTION, MYF(0),
 | |
|                "ROW_FORMAT");
 | |
|       DBUG_RETURN(true);
 | |
|     }
 | |
|     part_create_info.row_type= table->s->row_type;
 | |
|   }
 | |
| 
 | |
|   part_create_info.table= part_table;
 | |
| 
 | |
|   /*
 | |
|     NOTE: ha_blackhole does not support check_if_compatible_data,
 | |
|     so this always fail for blackhole tables.
 | |
|     ha_myisam compares pointers to verify that DATA/INDEX DIRECTORY is
 | |
|     the same, so any table using data/index_file_name will fail.
 | |
|   */
 | |
|   if (mysql_compare_tables(table, &part_alter_info, &part_create_info,
 | |
|                            &metadata_equal))
 | |
|   {
 | |
|     my_error(ER_TABLES_DIFFERENT_METADATA, MYF(0));
 | |
|     DBUG_RETURN(TRUE);
 | |
|   }
 | |
| 
 | |
|   DEBUG_SYNC(thd, "swap_partition_after_compare_tables");
 | |
|   if (!metadata_equal)
 | |
|   {
 | |
|     my_error(ER_TABLES_DIFFERENT_METADATA, MYF(0));
 | |
|     DBUG_RETURN(TRUE);
 | |
|   }
 | |
| 
 | |
|   if (table->s->db_create_options != part_table->s->db_create_options)
 | |
|   {
 | |
|     my_error(ER_TABLES_DIFFERENT_METADATA, MYF(0));
 | |
|     DBUG_RETURN(TRUE);
 | |
|   }
 | |
| 
 | |
|   DBUG_ASSERT(table->s->db_options_in_use ==
 | |
|               part_table->s->db_options_in_use);
 | |
| 
 | |
|   if (table_create_info.avg_row_length != part_create_info.avg_row_length)
 | |
|   {
 | |
|     my_error(ER_PARTITION_EXCHANGE_DIFFERENT_OPTION, MYF(0),
 | |
|              "AVG_ROW_LENGTH");
 | |
|     DBUG_RETURN(TRUE);
 | |
|   }
 | |
| 
 | |
|   if (table_create_info.table_options != part_create_info.table_options)
 | |
|   {
 | |
|     my_error(ER_PARTITION_EXCHANGE_DIFFERENT_OPTION, MYF(0),
 | |
|              "TABLE OPTION");
 | |
|     DBUG_RETURN(TRUE);
 | |
|   }
 | |
| 
 | |
|   if (table->s->table_charset != part_table->s->table_charset)
 | |
|   {
 | |
|     my_error(ER_PARTITION_EXCHANGE_DIFFERENT_OPTION, MYF(0),
 | |
|              "CHARACTER SET");
 | |
|     DBUG_RETURN(TRUE);
 | |
|   }
 | |
| 
 | |
|   /*
 | |
|     NOTE: We do not support update of frm-file, i.e. change
 | |
|     max/min_rows, data/index_file_name etc.
 | |
|     The workaround is to use REORGANIZE PARTITION to rewrite
 | |
|     the frm file and then use EXCHANGE PARTITION when they are the same.
 | |
|   */
 | |
|   if (part_elem && compare_partition_options(&table_create_info, part_elem))
 | |
|     DBUG_RETURN(TRUE);
 | |
| 
 | |
|   DBUG_RETURN(FALSE);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   @brief Exchange partition/table with ddl log.
 | |
| 
 | |
|   @details How to handle a crash in the middle of the rename (break on error):
 | |
|   1) register in ddl_log that we are going to exchange swap_table with part.
 | |
|   2) do the first rename (swap_table -> tmp-name) and sync the ddl_log.
 | |
|   3) do the second rename (part -> swap_table) and sync the ddl_log.
 | |
|   4) do the last rename (tmp-name -> part).
 | |
|   5) mark the entry done.
 | |
| 
 | |
|   Recover by:
 | |
|     5) is done, All completed. Nothing to recover.
 | |
|     4) is done see 3). (No mark or sync in the ddl_log...)
 | |
|     3) is done -> try rename part -> tmp-name (ignore failure) goto 2).
 | |
|     2) is done -> try rename swap_table -> part (ignore failure) goto 1).
 | |
|     1) is done -> try rename tmp-name -> swap_table (ignore failure).
 | |
|     before 1) Nothing to recover...
 | |
| 
 | |
|   @param thd        Thread handle
 | |
|   @param name       name of table/partition 1 (to be exchanged with 2)
 | |
|   @param from_name  name of table/partition 2 (to be exchanged with 1)
 | |
|   @param tmp_name   temporary name to use while exchanging
 | |
|   @param ht         handlerton of the table/partitions
 | |
| 
 | |
|   @return Operation status
 | |
|     @retval TRUE    Error
 | |
|     @retval FALSE   Success
 | |
| 
 | |
|   @note ha_heap always succeeds in rename (since it is created upon usage).
 | |
|   This is OK when to recover from a crash since all heap are empty and the
 | |
|   recover is done early in the startup of the server (right before
 | |
|   read_init_file which can populate the tables).
 | |
| 
 | |
|   And if no crash we can trust the syncs in the ddl_log.
 | |
| 
 | |
|   What about if the rename is put into a background thread? That will cause
 | |
|   corruption and is avoided by the exlusive metadata lock.
 | |
| */
 | |
| static bool exchange_name_with_ddl_log(THD *thd,
 | |
|                                        const char *name,
 | |
|                                        const char *from_name,
 | |
|                                        const char *tmp_name,
 | |
|                                        handlerton *ht)
 | |
| {
 | |
|   DDL_LOG_ENTRY exchange_entry;
 | |
|   DDL_LOG_MEMORY_ENTRY *log_entry= NULL;
 | |
|   DDL_LOG_MEMORY_ENTRY *exec_log_entry= NULL;
 | |
|   bool error= TRUE;
 | |
|   bool error_set= FALSE;
 | |
|   handler *file= NULL;
 | |
|   DBUG_ENTER("exchange_name_with_ddl_log");
 | |
| 
 | |
|   if (unlikely(!(file= get_new_handler(NULL, thd->mem_root, ht))))
 | |
|     DBUG_RETURN(TRUE);
 | |
| 
 | |
|   /* prepare the action entry */
 | |
|   bzero(&exchange_entry, sizeof(exchange_entry));
 | |
|   exchange_entry.entry_type=   DDL_LOG_ENTRY_CODE;
 | |
|   exchange_entry.action_type=  DDL_LOG_EXCHANGE_ACTION;
 | |
|   lex_string_set(&exchange_entry.name,         name);
 | |
|   lex_string_set(&exchange_entry.from_name,    from_name);
 | |
|   lex_string_set(&exchange_entry.tmp_name,     tmp_name);
 | |
|   lex_string_set(&exchange_entry.handler_name,
 | |
|                  ha_resolve_storage_engine_name(ht));
 | |
|   exchange_entry.phase=        EXCH_PHASE_NAME_TO_TEMP;
 | |
| 
 | |
|   mysql_mutex_lock(&LOCK_gdl);
 | |
|   /*
 | |
|     write to the ddl log what to do by:
 | |
|     1) write the action entry (i.e. which names to be exchanged)
 | |
|     2) write the execution entry with a link to the action entry
 | |
|   */
 | |
|   DBUG_EXECUTE_IF("exchange_partition_fail_1", goto err_no_action_written;);
 | |
|   DBUG_EXECUTE_IF("exchange_partition_abort_1", DBUG_SUICIDE(););
 | |
|   if (unlikely(ddl_log_write_entry(&exchange_entry, &log_entry)))
 | |
|     goto err_no_action_written;
 | |
| 
 | |
|   DBUG_EXECUTE_IF("exchange_partition_fail_2", goto err_no_execute_written;);
 | |
|   DBUG_EXECUTE_IF("exchange_partition_abort_2", DBUG_SUICIDE(););
 | |
|   if (unlikely(ddl_log_write_execute_entry(log_entry->entry_pos, 0,
 | |
|                                            &exec_log_entry)))
 | |
|     goto err_no_execute_written;
 | |
|   /* ddl_log is written and synced */
 | |
| 
 | |
|   mysql_mutex_unlock(&LOCK_gdl);
 | |
|   /*
 | |
|     Execute the name exchange.
 | |
|     Do one rename, increase the phase, update the action entry and sync.
 | |
|     In case of errors in the ddl_log we must fail and let the ddl_log try
 | |
|     to revert the changes, since otherwise it could revert the command after
 | |
|     we sent OK to the client.
 | |
|   */
 | |
|   /* call rename table from table to tmp-name */
 | |
|   DBUG_EXECUTE_IF("exchange_partition_fail_3",
 | |
|                   my_error(ER_ERROR_ON_RENAME, MYF(0), name, tmp_name, 0);
 | |
|                   error_set= TRUE;
 | |
|                   goto err_rename;);
 | |
|   DBUG_EXECUTE_IF("exchange_partition_abort_3", DBUG_SUICIDE(););
 | |
|   if (unlikely(file->ha_rename_table(name, tmp_name)))
 | |
|   {
 | |
|     my_error(ER_ERROR_ON_RENAME, MYF(0), name, tmp_name, my_errno);
 | |
|     error_set= TRUE;
 | |
|     goto err_rename;
 | |
|   }
 | |
|   DBUG_EXECUTE_IF("exchange_partition_fail_4", goto err_rename;);
 | |
|   DBUG_EXECUTE_IF("exchange_partition_abort_4", DBUG_SUICIDE(););
 | |
|   if (unlikely(ddl_log_increment_phase(log_entry->entry_pos)))
 | |
|     goto err_rename;
 | |
| 
 | |
|   /* call rename table from partition to table */
 | |
|   DBUG_EXECUTE_IF("exchange_partition_fail_5",
 | |
|                   my_error(ER_ERROR_ON_RENAME, MYF(0), from_name, name, 0);
 | |
|                   error_set= TRUE;
 | |
|                   goto err_rename;);
 | |
|   DBUG_EXECUTE_IF("exchange_partition_abort_5", DBUG_SUICIDE(););
 | |
|   if (unlikely(file->ha_rename_table(from_name, name)))
 | |
|   {
 | |
|     my_error(ER_ERROR_ON_RENAME, MYF(0), from_name, name, my_errno);
 | |
|     error_set= TRUE;
 | |
|     goto err_rename;
 | |
|   }
 | |
|   DBUG_EXECUTE_IF("exchange_partition_fail_6", goto err_rename;);
 | |
|   DBUG_EXECUTE_IF("exchange_partition_abort_6", DBUG_SUICIDE(););
 | |
|   if (unlikely(ddl_log_increment_phase(log_entry->entry_pos)))
 | |
|     goto err_rename;
 | |
| 
 | |
|   /* call rename table from tmp-nam to partition */
 | |
|   DBUG_EXECUTE_IF("exchange_partition_fail_7",
 | |
|                   my_error(ER_ERROR_ON_RENAME, MYF(0), tmp_name, from_name, 0);
 | |
|                   error_set= TRUE;
 | |
|                   goto err_rename;);
 | |
|   DBUG_EXECUTE_IF("exchange_partition_abort_7", DBUG_SUICIDE(););
 | |
|   if (unlikely(file->ha_rename_table(tmp_name, from_name)))
 | |
|   {
 | |
|     my_error(ER_ERROR_ON_RENAME, MYF(0), tmp_name, from_name, my_errno);
 | |
|     error_set= TRUE;
 | |
|     goto err_rename;
 | |
|   }
 | |
|   DBUG_EXECUTE_IF("exchange_partition_fail_8", goto err_rename;);
 | |
|   DBUG_EXECUTE_IF("exchange_partition_abort_8", DBUG_SUICIDE(););
 | |
|   if (unlikely(ddl_log_increment_phase(log_entry->entry_pos)))
 | |
|     goto err_rename;
 | |
| 
 | |
|   /* The exchange is complete and ddl_log is deactivated */
 | |
|   DBUG_EXECUTE_IF("exchange_partition_fail_9", goto err_rename;);
 | |
|   DBUG_EXECUTE_IF("exchange_partition_abort_9", DBUG_SUICIDE(););
 | |
|   /* all OK */
 | |
|   error= FALSE;
 | |
|   delete file;
 | |
|   DBUG_RETURN(error);
 | |
| err_rename:
 | |
|   /*
 | |
|     Nothing to do if any of these commands fails :( the commands itselfs
 | |
|     will log to the error log about the failures...
 | |
|   */
 | |
|   /* execute the ddl log entry to revert the renames */
 | |
|   (void) ddl_log_execute_entry(current_thd, log_entry->entry_pos);
 | |
|   mysql_mutex_lock(&LOCK_gdl);
 | |
|   /* mark the execute log entry done */
 | |
|   (void) ddl_log_disable_execute_entry(&exec_log_entry);
 | |
|   /* release the execute log entry */
 | |
|   (void) ddl_log_release_memory_entry(exec_log_entry);
 | |
| err_no_execute_written:
 | |
|   /* release the action log entry */
 | |
|   (void) ddl_log_release_memory_entry(log_entry);
 | |
| err_no_action_written:
 | |
|   mysql_mutex_unlock(&LOCK_gdl);
 | |
|   delete file;
 | |
|   if (!error_set)
 | |
|     my_error(ER_DDL_LOG_ERROR, MYF(0));
 | |
|   DBUG_RETURN(error);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   @brief Swap places between a partition and a table.
 | |
| 
 | |
|   @details Verify that the tables are compatible (same engine, definition etc),
 | |
|   verify that all rows in the table will fit in the partition,
 | |
|   if all OK, rename table to tmp name, rename partition to table
 | |
|   and finally rename tmp name to partition.
 | |
| 
 | |
|   1) Take upgradable mdl, open tables and then lock them (inited in parse)
 | |
|   2) Verify that metadata matches
 | |
|   3) verify data
 | |
|   4) Upgrade to exclusive mdl for both tables
 | |
|   5) Rename table <-> partition
 | |
|   6) Rely on close_thread_tables to release mdl and table locks
 | |
| 
 | |
|   @param thd            Thread handle
 | |
|   @param table_list     Table where the partition exists as first table,
 | |
|                         Table to swap with the partition as second table
 | |
|   @param alter_info     Contains partition name to swap
 | |
| 
 | |
|   @note This is a DDL operation so triggers will not be used.
 | |
| */
 | |
| bool Sql_cmd_alter_table_exchange_partition::
 | |
|   exchange_partition(THD *thd, TABLE_LIST *table_list, Alter_info *alter_info)
 | |
| {
 | |
|   TABLE *part_table, *swap_table;
 | |
|   TABLE_LIST *swap_table_list;
 | |
|   handlerton *table_hton;
 | |
|   partition_element *part_elem;
 | |
|   char temp_name[FN_REFLEN+1];
 | |
|   char part_file_name[2*FN_REFLEN+1];
 | |
|   char swap_file_name[FN_REFLEN+1];
 | |
|   char temp_file_name[FN_REFLEN+1];
 | |
|   char part_table_name[NAME_LEN + 1];
 | |
|   char part_db[NAME_LEN + 1];
 | |
|   char swap_table_name[NAME_LEN + 1];
 | |
|   char swap_db[NAME_LEN + 1];
 | |
|   uchar part_tabledef_version[MY_UUID_SIZE];
 | |
|   uchar swap_tabledef_version[MY_UUID_SIZE];
 | |
| 
 | |
|   backup_log_info ddl_log;
 | |
|   bzero(&ddl_log, sizeof(ddl_log));
 | |
| 
 | |
|   uint swap_part_id;
 | |
|   uint part_file_name_len;
 | |
|   Alter_table_prelocking_strategy alter_prelocking_strategy;
 | |
|   MDL_ticket *swap_table_mdl_ticket= NULL;
 | |
|   MDL_ticket *part_table_mdl_ticket= NULL;
 | |
|   uint table_counter;
 | |
|   bool error= TRUE, force_if_exists= 0;
 | |
|   ulonglong save_option_bits= thd->variables.option_bits;
 | |
|   DBUG_ENTER("mysql_exchange_partition");
 | |
|   DBUG_ASSERT(alter_info->partition_flags & ALTER_PARTITION_EXCHANGE);
 | |
| 
 | |
|   /* Don't allow to exchange with log table */
 | |
|   swap_table_list= table_list->next_local;
 | |
|   if (check_if_log_table(swap_table_list, FALSE, "ALTER PARTITION"))
 | |
|     DBUG_RETURN(TRUE);
 | |
| 
 | |
|   /*
 | |
|     Currently no MDL lock that allows both read and write and is upgradeable
 | |
|     to exclusive, so leave the lock type to TL_WRITE_ALLOW_READ also on the
 | |
|     partitioned table.
 | |
| 
 | |
|     TODO: add MDL lock that allows both read and write and is upgradable to
 | |
|     exclusive lock. This would allow to continue using the partitioned table
 | |
|     also with update/insert/delete while the verification of the swap table
 | |
|     is running.
 | |
|   */
 | |
| 
 | |
|   /*
 | |
|     NOTE: It is not possible to exchange a crashed partition/table since
 | |
|     we need some info from the engine, which we can only access after open,
 | |
|     to be able to verify the structure/metadata.
 | |
|   */
 | |
|   table_list->mdl_request.set_type(MDL_SHARED_NO_WRITE);
 | |
|   if (unlikely(open_tables(thd, &table_list, &table_counter, 0,
 | |
|                            &alter_prelocking_strategy)))
 | |
|   {
 | |
|     if (thd->lex->if_exists() &&
 | |
|         thd->get_stmt_da()->sql_errno() == ER_NO_SUCH_TABLE)
 | |
|     {
 | |
|       /*
 | |
|         ALTER TABLE IF EXISTS was used on not existing table
 | |
|         We have to log the query on a slave as the table may be a shared one
 | |
|         from the master and we need to ensure that the next slave can see
 | |
|         the statement as this slave may not have the table shared
 | |
|       */
 | |
|       thd->clear_error();
 | |
|       if (thd->slave_thread &&
 | |
|           write_bin_log(thd, true, thd->query(), thd->query_length()))
 | |
|         DBUG_RETURN(true);
 | |
|       my_ok(thd);
 | |
|       DBUG_RETURN(false);
 | |
|     }
 | |
|     DBUG_RETURN(true);
 | |
|   }
 | |
| 
 | |
|   part_table= table_list->table;
 | |
|   swap_table= swap_table_list->table;
 | |
| 
 | |
|   /* Don't allow to exchange with a VIEW */
 | |
|   if (unlikely(swap_table_list->view))
 | |
|   {
 | |
|     my_error(ER_WRONG_OBJECT, MYF(0), table_list->db.str,
 | |
|               swap_table_list->table_name.str, "BASE TABLE");
 | |
|     DBUG_RETURN(TRUE);
 | |
|   }
 | |
| 
 | |
|   if (unlikely(check_exchange_partition(swap_table, part_table)))
 | |
|     DBUG_RETURN(TRUE);
 | |
| 
 | |
|   if (part_table->file->check_if_updates_are_ignored("ALTER"))
 | |
|     DBUG_RETURN(return_with_logging(thd));
 | |
| 
 | |
|   /* Add IF EXISTS to binlog if shared table */
 | |
|   if (part_table->file->partition_ht()->flags &
 | |
|       HTON_TABLE_MAY_NOT_EXIST_ON_SLAVE)
 | |
|     force_if_exists= 1;
 | |
| 
 | |
|   ddl_log.org_table.str= part_table_name;
 | |
|   DBUG_ASSERT(part_table->s->table_name.length <= NAME_LEN);
 | |
|   ddl_log.org_table.length= part_table->s->table_name.length;
 | |
|   strmake(part_table_name, part_table->s->table_name.str, NAME_LEN);
 | |
| 
 | |
|   ddl_log.org_database.str= part_db;
 | |
|   DBUG_ASSERT(part_table->s->db.length <= NAME_LEN);
 | |
|   ddl_log.org_database.length= part_table->s->db.length;
 | |
|   strmake(part_db, part_table->s->db.str, NAME_LEN);
 | |
| 
 | |
|   ddl_log.new_table.str= swap_table_name;
 | |
|   DBUG_ASSERT(swap_table->s->table_name.length <= NAME_LEN);
 | |
|   ddl_log.new_table.length= swap_table->s->table_name.length;
 | |
|   strmake(swap_table_name, swap_table->s->table_name.str, NAME_LEN);
 | |
| 
 | |
|   ddl_log.new_database.str= swap_db;
 | |
|   DBUG_ASSERT(swap_table->s->db.length <= NAME_LEN);
 | |
|   ddl_log.new_database.length= swap_table->s->db.length;
 | |
|   strmake(swap_db, swap_table->s->db.str, NAME_LEN);
 | |
| 
 | |
|   memcpy(part_tabledef_version, part_table->s->tabledef_version.str,
 | |
|          MY_UUID_SIZE);
 | |
|   ddl_log.org_table_id.str= part_tabledef_version;
 | |
|   ddl_log.org_table_id.length= MY_UUID_SIZE;
 | |
|   memcpy(swap_tabledef_version, swap_table->s->tabledef_version.str,
 | |
|          MY_UUID_SIZE);
 | |
|   ddl_log.new_table_id.str= swap_tabledef_version;
 | |
|   ddl_log.new_table_id.length= MY_UUID_SIZE;
 | |
| 
 | |
|   /* set lock pruning on first table */
 | |
|   const Lex_cstring_strlen partition_name= alter_info->partition_names.head();
 | |
|   if (unlikely(table_list->table->part_info->
 | |
|                set_named_partition_bitmap(partition_name.str,
 | |
|                                           partition_name.length)))
 | |
|     DBUG_RETURN(true);
 | |
| 
 | |
|   if (unlikely(lock_tables(thd, table_list, table_counter, 0)))
 | |
|     DBUG_RETURN(true);
 | |
| 
 | |
|   table_hton= swap_table->file->ht;
 | |
| 
 | |
|   THD_STAGE_INFO(thd, stage_verifying_table);
 | |
| 
 | |
|   /* Will append the partition name later in part_info->get_part_elem() */
 | |
|   part_file_name_len= build_table_filename(part_file_name,
 | |
|                                            sizeof(part_file_name),
 | |
|                                            table_list->db.str,
 | |
|                                            table_list->table_name.str,
 | |
|                                            "", 0);
 | |
|   build_table_filename(swap_file_name,
 | |
|                        sizeof(swap_file_name),
 | |
|                        swap_table_list->db.str,
 | |
|                        swap_table_list->table_name.str,
 | |
|                        "", 0);
 | |
| 
 | |
|   LEX_STRING tmp= { temp_name, sizeof(temp_name) };
 | |
|   make_tmp_table_name(thd, &tmp, "exchange");
 | |
| 
 | |
|   build_table_filename(temp_file_name, sizeof(temp_file_name),
 | |
|                        table_list->next_local->db.str,
 | |
|                        temp_name, "", FN_IS_TMP);
 | |
| 
 | |
|   if (unlikely(!(part_elem=
 | |
|                  part_table->part_info->get_part_elem(partition_name,
 | |
|                                                       part_file_name +
 | |
|                                                       part_file_name_len,
 | |
|                                                       sizeof(part_file_name) -
 | |
|                                                       part_file_name_len,
 | |
|                                                       &swap_part_id))))
 | |
|   {
 | |
|     DBUG_RETURN(TRUE);
 | |
|   }
 | |
| 
 | |
|   if (unlikely(swap_part_id == NOT_A_PARTITION_ID))
 | |
|   {
 | |
|     DBUG_ASSERT(part_table->part_info->is_sub_partitioned());
 | |
|     my_error(ER_PARTITION_INSTEAD_OF_SUBPARTITION, MYF(0));
 | |
|     DBUG_RETURN(TRUE);
 | |
|   }
 | |
| 
 | |
|   if (unlikely(compare_table_with_partition(thd, swap_table, part_table,
 | |
|                                             part_elem,
 | |
|                                             swap_part_id)))
 | |
|     DBUG_RETURN(TRUE);
 | |
| 
 | |
|   /* Table and partition has same structure/options, OK to exchange */
 | |
| 
 | |
|   thd_proc_info(thd, "Verifying data with partition");
 | |
| 
 | |
|   if (unlikely(verify_data_with_partition(swap_table, part_table,
 | |
|                                           swap_part_id)))
 | |
|     DBUG_RETURN(TRUE);
 | |
| 
 | |
|   /*
 | |
|     Get exclusive mdl lock on both tables, alway the non partitioned table
 | |
|     first. Remember the tickets for downgrading locks later.
 | |
|   */
 | |
|   swap_table_mdl_ticket= swap_table->mdl_ticket;
 | |
|   part_table_mdl_ticket= part_table->mdl_ticket;
 | |
| 
 | |
|   /*
 | |
|     No need to set used_partitions to only propagate
 | |
|     HA_EXTRA_PREPARE_FOR_RENAME to one part since no built in engine uses
 | |
|     that flag. And the action would probably be to force close all other
 | |
|     instances which is what we are doing any way.
 | |
|   */
 | |
|   if (wait_while_table_is_used(thd, swap_table, HA_EXTRA_PREPARE_FOR_RENAME) ||
 | |
|       wait_while_table_is_used(thd, part_table, HA_EXTRA_PREPARE_FOR_RENAME))
 | |
|     goto err;
 | |
| 
 | |
|   DEBUG_SYNC(thd, "swap_partition_after_wait");
 | |
| 
 | |
|   close_all_tables_for_name(thd, swap_table->s, HA_EXTRA_NOT_USED, NULL);
 | |
|   close_all_tables_for_name(thd, part_table->s, HA_EXTRA_NOT_USED, NULL);
 | |
| 
 | |
|   DEBUG_SYNC(thd, "swap_partition_before_rename");
 | |
| 
 | |
|   if (unlikely(exchange_name_with_ddl_log(thd, swap_file_name, part_file_name,
 | |
|                                           temp_file_name, table_hton)))
 | |
|     goto err;
 | |
| 
 | |
|   /*
 | |
|     Reopen tables under LOCK TABLES. Ignore the return value for now. It's
 | |
|     better to keep master/slave in consistent state. Alternative would be to
 | |
|     try to revert the exchange operation and issue error.
 | |
|   */
 | |
|   (void) thd->locked_tables_list.reopen_tables(thd, false);
 | |
| 
 | |
|   if (force_if_exists)
 | |
|     thd->variables.option_bits|= OPTION_IF_EXISTS;
 | |
| 
 | |
|   if (unlikely((error= write_bin_log(thd, TRUE, thd->query(),
 | |
|                                      thd->query_length()))))
 | |
|   {
 | |
|     /*
 | |
|       The error is reported in write_bin_log().
 | |
|       We try to revert to make it easier to keep the master/slave in sync.
 | |
|     */
 | |
|     (void) exchange_name_with_ddl_log(thd, part_file_name, swap_file_name,
 | |
|                                       temp_file_name, table_hton);
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     ddl_log.query= { C_STRING_WITH_LEN("EXCHANGE_PARTITION") };
 | |
|     ddl_log.org_partitioned= true;
 | |
|     ddl_log.new_partitioned= false;
 | |
|     ddl_log.org_storage_engine_name= *hton_name(table_hton);
 | |
|     ddl_log.new_storage_engine_name= *hton_name(table_hton);
 | |
|     backup_log_ddl(&ddl_log);
 | |
|   }
 | |
|   thd->variables.option_bits= save_option_bits;
 | |
| 
 | |
| err:
 | |
|   if (thd->locked_tables_mode)
 | |
|   {
 | |
|     if (swap_table_mdl_ticket)
 | |
|       swap_table_mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE);
 | |
|     if (part_table_mdl_ticket)
 | |
|       part_table_mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE);
 | |
|   }
 | |
| 
 | |
|   if (unlikely(!error))
 | |
|     my_ok(thd);
 | |
| 
 | |
|   // For query cache
 | |
|   table_list->table= NULL;
 | |
|   table_list->next_local->table= NULL;
 | |
|   query_cache_invalidate3(thd, table_list, FALSE);
 | |
| 
 | |
|   DBUG_RETURN(error);
 | |
| }
 | |
| 
 | |
| bool Sql_cmd_alter_table_analyze_partition::execute(THD *thd)
 | |
| {
 | |
|   bool res;
 | |
|   DBUG_ENTER("Sql_cmd_alter_table_analyze_partition::execute");
 | |
| 
 | |
|   /*
 | |
|     Flag that it is an ALTER command which administrates partitions, used
 | |
|     by ha_partition
 | |
|   */
 | |
|   thd->lex->alter_info.partition_flags|= ALTER_PARTITION_ADMIN;
 | |
| 
 | |
|   res= Sql_cmd_analyze_table::execute(thd);
 | |
|     
 | |
|   DBUG_RETURN(res);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Sql_cmd_alter_table_check_partition::execute(THD *thd)
 | |
| {
 | |
|   bool res;
 | |
|   DBUG_ENTER("Sql_cmd_alter_table_check_partition::execute");
 | |
| 
 | |
|   /*
 | |
|     Flag that it is an ALTER command which administrates partitions, used
 | |
|     by ha_partition
 | |
|   */
 | |
|   thd->lex->alter_info.partition_flags|= ALTER_PARTITION_ADMIN;
 | |
| 
 | |
|   res= Sql_cmd_check_table::execute(thd);
 | |
| 
 | |
|   DBUG_RETURN(res);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Sql_cmd_alter_table_optimize_partition::execute(THD *thd)
 | |
| {
 | |
|   bool res;
 | |
|   DBUG_ENTER("Alter_table_optimize_partition_statement::execute");
 | |
| 
 | |
|   /*
 | |
|     Flag that it is an ALTER command which administrates partitions, used
 | |
|     by ha_partition
 | |
|   */
 | |
|   thd->lex->alter_info.partition_flags|= ALTER_PARTITION_ADMIN;
 | |
| 
 | |
|   res= Sql_cmd_optimize_table::execute(thd);
 | |
| 
 | |
|   DBUG_RETURN(res);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Sql_cmd_alter_table_repair_partition::execute(THD *thd)
 | |
| {
 | |
|   bool res;
 | |
|   DBUG_ENTER("Sql_cmd_alter_table_repair_partition::execute");
 | |
| 
 | |
|   /*
 | |
|     Flag that it is an ALTER command which administrates partitions, used
 | |
|     by ha_partition
 | |
|   */
 | |
|   thd->lex->alter_info.partition_flags|= ALTER_PARTITION_ADMIN;
 | |
| 
 | |
|   res= Sql_cmd_repair_table::execute(thd);
 | |
| 
 | |
|   DBUG_RETURN(res);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool Sql_cmd_alter_table_truncate_partition::execute(THD *thd)
 | |
| {
 | |
|   int error;
 | |
|   ha_partition *partition;
 | |
|   ulong timeout= thd->variables.lock_wait_timeout;
 | |
|   TABLE_LIST *first_table= thd->lex->first_select_lex()->table_list.first;
 | |
|   Alter_info *alter_info= &thd->lex->alter_info;
 | |
|   uint table_counter, i;
 | |
|   List<String> partition_names_list;
 | |
|   bool binlog_stmt, force_if_exists= 0;
 | |
|   DBUG_ENTER("Sql_cmd_alter_table_truncate_partition::execute");
 | |
| 
 | |
|   /*
 | |
|     Flag that it is an ALTER command which administrates partitions, used
 | |
|     by ha_partition.
 | |
|   */
 | |
|   thd->lex->alter_info.partition_flags|= (ALTER_PARTITION_ADMIN |
 | |
|                                           ALTER_PARTITION_TRUNCATE);
 | |
| 
 | |
|   /* Fix the lock types (not the same as ordinary ALTER TABLE). */
 | |
|   first_table->lock_type= TL_WRITE;
 | |
|   first_table->mdl_request.set_type(MDL_EXCLUSIVE);
 | |
| 
 | |
|   /*
 | |
|     Check table permissions and open it with a exclusive lock.
 | |
|     Ensure it is a partitioned table and finally, upcast the
 | |
|     handler and invoke the partition truncate method. Lastly,
 | |
|     write the statement to the binary log if necessary.
 | |
|   */
 | |
| 
 | |
|   if (check_one_table_access(thd, DROP_ACL, first_table))
 | |
|     DBUG_RETURN(TRUE);
 | |
| 
 | |
| #ifdef WITH_WSREP
 | |
|   if (WSREP(thd) &&
 | |
|       (!thd->is_current_stmt_binlog_format_row() ||
 | |
|        !thd->find_temporary_table(first_table))  &&
 | |
|       wsrep_to_isolation_begin(
 | |
|           thd, first_table->db.str, first_table->table_name.str, NULL)
 | |
|       )
 | |
|   {
 | |
|     WSREP_WARN("ALTER TABLE TRUNCATE PARTITION isolation failure");
 | |
|     DBUG_RETURN(TRUE);
 | |
|   }
 | |
| #endif /* WITH_WSREP */
 | |
| 
 | |
|   if (open_tables(thd, &first_table, &table_counter, 0))
 | |
|   {
 | |
|     if (thd->lex->if_exists() &&
 | |
|         thd->get_stmt_da()->sql_errno() == ER_NO_SUCH_TABLE)
 | |
|     {
 | |
|       /*
 | |
|         ALTER TABLE IF EXISTS was used on not existing table
 | |
|         We have to log the query on a slave as the table may be a shared one
 | |
|         from the master and we need to ensure that the next slave can see
 | |
|         the statement as this slave may not have the table shared
 | |
|       */
 | |
|       thd->clear_error();
 | |
|       DBUG_RETURN(return_with_logging(thd));
 | |
|     }
 | |
|     DBUG_RETURN(TRUE);
 | |
|   }
 | |
| 
 | |
|   if (!first_table->table || first_table->view)
 | |
|   {
 | |
|     my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
 | |
|     DBUG_RETURN(TRUE);
 | |
|   }
 | |
| 
 | |
|   if (first_table->table->file->check_if_updates_are_ignored("ALTER"))
 | |
|     DBUG_RETURN(return_with_logging(thd));
 | |
| 
 | |
|   if (first_table->table->s->db_type() != partition_hton)
 | |
|   {
 | |
|     my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
 | |
|     DBUG_RETURN(TRUE);
 | |
|   }
 | |
| 
 | |
|   if (first_table->table->file->partition_ht()->flags &
 | |
|       HTON_TABLE_MAY_NOT_EXIST_ON_SLAVE)
 | |
|     force_if_exists= 1;
 | |
| 
 | |
|   /*
 | |
|     Prune all, but named partitions,
 | |
|     to avoid excessive calls to external_lock().
 | |
|   */
 | |
|   List_iterator<const char> partition_names_it(alter_info->partition_names);
 | |
|   uint num_names= alter_info->partition_names.elements;
 | |
|   for (i= 0; i < num_names; i++)
 | |
|   {
 | |
|     const char *partition_name= partition_names_it++;
 | |
|     String *str_partition_name= new (thd->mem_root)
 | |
|       String(partition_name, strlen(partition_name), system_charset_info);
 | |
|     if (!str_partition_name)
 | |
|       DBUG_RETURN(true);
 | |
|     partition_names_list.push_back(str_partition_name, thd->mem_root);
 | |
|   }
 | |
|   if (first_table->table->
 | |
|         part_info->set_partition_bitmaps(&partition_names_list))
 | |
|     DBUG_RETURN(true);
 | |
| 
 | |
|   if (lock_tables(thd, first_table, table_counter, 0))
 | |
|     DBUG_RETURN(true);
 | |
| 
 | |
|   /*
 | |
|     Under locked table modes this might still not be an exclusive
 | |
|     lock. Hence, upgrade the lock since the handler truncate method
 | |
|     mandates an exclusive metadata lock.
 | |
|   */
 | |
|   MDL_ticket *ticket= first_table->table->mdl_ticket;
 | |
|   if (thd->mdl_context.upgrade_shared_lock(ticket, MDL_EXCLUSIVE, timeout))
 | |
|     DBUG_RETURN(TRUE);
 | |
| 
 | |
|   first_table->table->s->tdc->flush(thd, true);
 | |
| 
 | |
|   partition= (ha_partition*) first_table->table->file;
 | |
|   /* Invoke the handler method responsible for truncating the partition. */
 | |
|   if (unlikely(error= partition->truncate_partition(alter_info,
 | |
|                                                     &binlog_stmt)))
 | |
|     partition->print_error(error, MYF(0));
 | |
| 
 | |
|   /*
 | |
|     All effects of a truncate operation are committed even if the
 | |
|     operation fails. Thus, the query must be written to the binary
 | |
|     log. The exception is a unimplemented truncate method or failure
 | |
|     before any call to handler::truncate() is done.
 | |
|     Also, it is logged in statement format, regardless of the binlog format.
 | |
| 
 | |
|     Since we've changed data within the table, we also have to invalidate
 | |
|     the query cache for it.
 | |
|   */
 | |
|   if (likely(error != HA_ERR_WRONG_COMMAND))
 | |
|   {
 | |
|     ulonglong save_option_bits= thd->variables.option_bits;
 | |
|     if (force_if_exists)
 | |
|       thd->variables.option_bits|= OPTION_IF_EXISTS;
 | |
| 
 | |
|     query_cache_invalidate3(thd, first_table, FALSE);
 | |
|     if (binlog_stmt)
 | |
|       error|= write_bin_log(thd, !error, thd->query(), thd->query_length());
 | |
|     thd->variables.option_bits= save_option_bits;
 | |
|   }
 | |
| 
 | |
|   /*
 | |
|     A locked table ticket was upgraded to a exclusive lock. After the
 | |
|     the query has been written to the binary log, downgrade the lock
 | |
|     to a shared one.
 | |
|   */
 | |
|   if (thd->locked_tables_mode)
 | |
|     ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE);
 | |
| 
 | |
|   if (likely(!error))
 | |
|     my_ok(thd);
 | |
| 
 | |
|   // Invalidate query cache
 | |
|   DBUG_ASSERT(!first_table->next_local);
 | |
|   query_cache_invalidate3(thd, first_table, FALSE);
 | |
| 
 | |
|   DBUG_RETURN(error);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Move a table specified in the CONVERT TABLE <table_name> TO PARTITION ...
 | |
|   to the new partition.
 | |
| 
 | |
|   @param lpt  A structure containing parameters regarding to the statement
 | |
|               ALTER TABLE ... TO PARTITION ...
 | |
|   @param part_file_name  a file name of the partition being added
 | |
| 
 | |
|   @return false on success, true on error
 | |
| */
 | |
| 
 | |
| bool alter_partition_convert_in(ALTER_PARTITION_PARAM_TYPE *lpt)
 | |
| {
 | |
|   char part_file_name[2*FN_REFLEN+1];
 | |
|   THD *thd= lpt->thd;
 | |
|   const char *path= lpt->table_list->table->s->path.str;
 | |
|   TABLE_LIST *table_from= lpt->table_list->next_local;
 | |
| 
 | |
|   const Lex_ident_partition &partition_name=
 | |
|     thd->lex->part_info->curr_part_elem->partition_name;
 | |
| 
 | |
|   if (create_partition_name(part_file_name, sizeof(part_file_name), path,
 | |
|                             partition_name, NORMAL_PART_NAME, false))
 | |
|     return true;
 | |
| 
 | |
|   char from_file_name[FN_REFLEN+1];
 | |
| 
 | |
|   build_table_filename(from_file_name, sizeof(from_file_name),
 | |
|                        table_from->db.str, table_from->table_name.str, "", 0);
 | |
| 
 | |
|   handler *file= get_new_handler(nullptr, thd->mem_root,
 | |
|                                  table_from->table->file->ht);
 | |
|   if (unlikely(!file))
 | |
|     return true;
 | |
| 
 | |
|   close_all_tables_for_name(thd, table_from->table->s,
 | |
|                             HA_EXTRA_PREPARE_FOR_RENAME, nullptr);
 | |
| 
 | |
|   bool res= file->ha_rename_table(from_file_name, part_file_name);
 | |
| 
 | |
|   if (res)
 | |
|     my_error(ER_ERROR_ON_RENAME, MYF(0), from_file_name,
 | |
|              part_file_name, my_errno);
 | |
| 
 | |
|   delete file;
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| #endif /* WITH_PARTITION_STORAGE_ENGINE */
 |