diff options
author | unknown <mikael@c-4908e253.1238-1-64736c10.cust.bredbandsbolaget.se> | 2006-04-01 16:31:53 -0500 |
---|---|---|
committer | unknown <mikael@c-4908e253.1238-1-64736c10.cust.bredbandsbolaget.se> | 2006-04-01 16:31:53 -0500 |
commit | 150d8d35641cb14058cc436747b32b6c84ce5cee (patch) | |
tree | b0ebf45311f5f6a51e2768460b3d3f3d54b4cf71 | |
parent | 4e8577550c3ee4f0a650e1928ceeb5421c9a8896 (diff) | |
parent | 79ec84f98abcc94ec267c232207f36a1669b7125 (diff) | |
download | mariadb-git-150d8d35641cb14058cc436747b32b6c84ce5cee.tar.gz |
Merge c-4908e253.1238-1-64736c10.cust.bredbandsbolaget.se:/home/pappa/clean-mysql-5.1-new
into c-4908e253.1238-1-64736c10.cust.bredbandsbolaget.se:/home/pappa/wl2826
configure.in:
Auto merged
BUILD/SETUP.sh:
Auto merged
include/my_dbug.h:
Auto merged
sql/ha_heap.cc:
Auto merged
sql/ha_myisammrg.cc:
Auto merged
sql/ha_ndbcluster.cc:
Auto merged
sql/ha_ndbcluster.h:
Auto merged
sql/ha_partition.h:
Auto merged
sql/handler.h:
Auto merged
sql/log.cc:
Auto merged
sql/mysql_priv.h:
Auto merged
sql/mysqld.cc:
Auto merged
sql/partition_info.h:
Auto merged
sql/set_var.cc:
Auto merged
sql/sql_base.cc:
Auto merged
sql/sql_class.cc:
Auto merged
sql/sql_class.h:
Auto merged
sql/sql_partition.cc:
Auto merged
sql/sql_table.cc:
Auto merged
sql/share/errmsg.txt:
Auto merged
sql/sql_yacc.yy:
Auto merged
sql/table.cc:
Auto merged
mysql-test/r/ndb_partition_key.result:
manual merge
mysql-test/t/ndb_partition_key.test:
manual merge
sql/ha_partition.cc:
manual merge
-rwxr-xr-x | BUILD/SETUP.sh | 1 | ||||
-rwxr-xr-x | BUILD/compile-pentium-debug-max | 2 | ||||
-rw-r--r-- | configure.in | 13 | ||||
-rw-r--r-- | include/my_sys.h | 2 | ||||
-rw-r--r-- | mysql-test/r/ndb_partition_key.result | 13 | ||||
-rw-r--r-- | mysql-test/t/ndb_partition_key.test | 18 | ||||
-rw-r--r-- | sql/ha_ndbcluster.cc | 8 | ||||
-rw-r--r-- | sql/ha_ndbcluster.h | 3 | ||||
-rw-r--r-- | sql/ha_partition.cc | 266 | ||||
-rw-r--r-- | sql/ha_partition.h | 3 | ||||
-rw-r--r-- | sql/handler.h | 10 | ||||
-rw-r--r-- | sql/mysql_priv.h | 197 | ||||
-rw-r--r-- | sql/mysqld.cc | 2 | ||||
-rw-r--r-- | sql/partition_element.h | 6 | ||||
-rw-r--r-- | sql/partition_info.h | 9 | ||||
-rw-r--r-- | sql/share/errmsg.txt | 2 | ||||
-rw-r--r-- | sql/sql_base.cc | 10 | ||||
-rw-r--r-- | sql/sql_class.cc | 3 | ||||
-rw-r--r-- | sql/sql_class.h | 3 | ||||
-rw-r--r-- | sql/sql_partition.cc | 1167 | ||||
-rw-r--r-- | sql/sql_table.cc | 1080 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 71 | ||||
-rw-r--r-- | sql/table.cc | 36 | ||||
-rw-r--r-- | sql/unireg.cc | 9 |
24 files changed, 2423 insertions, 511 deletions
diff --git a/BUILD/SETUP.sh b/BUILD/SETUP.sh index a6e18e26bfc..eec7b9a9c60 100755 --- a/BUILD/SETUP.sh +++ b/BUILD/SETUP.sh @@ -71,6 +71,7 @@ pentium_cflags="$check_cpu_cflags" pentium64_cflags="$check_cpu_cflags -m64" ppc_cflags="$check_cpu_cflags" sparc_cflags="" +error_inject="--with-error-inject " # be as fast as we can be without losing our ability to backtrace fast_cflags="-O3 -fno-omit-frame-pointer" diff --git a/BUILD/compile-pentium-debug-max b/BUILD/compile-pentium-debug-max index 7a11ad24c44..9aa71239bd8 100755 --- a/BUILD/compile-pentium-debug-max +++ b/BUILD/compile-pentium-debug-max @@ -6,6 +6,6 @@ path=`dirname $0` extra_flags="$pentium_cflags $debug_cflags $max_cflags" c_warnings="$c_warnings $debug_extra_warnings" cxx_warnings="$cxx_warnings $debug_extra_warnings" -extra_configs="$pentium_configs $debug_configs $max_configs" +extra_configs="$pentium_configs $debug_configs $max_configs $error_inject" . "$path/FINISH.sh" diff --git a/configure.in b/configure.in index 0253a3a6771..bfd2ec21869 100644 --- a/configure.in +++ b/configure.in @@ -666,6 +666,7 @@ else AC_MSG_RESULT([no]) fi + MYSQL_SYS_LARGEFILE # Types that must be checked AFTER large file support is checked @@ -1587,6 +1588,18 @@ then DEBUG_OPTIMIZE_CXX="" fi +# If we should allow error injection tests +AC_ARG_WITH(error-inject, + [ --with-error-inject Enable error injection in MySQL Server], + [ with_error_inject=$withval ], + [ with_error_inject=no ]) + +if test "$with_error_inject" = "yes" +then + CFLAGS="-DERROR_INJECT_SUPPORT $CFLAGS" + CXXFLAGS="-DERROR_INJECT_SUPPORT $CXXFLAGS" +fi + AC_ARG_WITH(debug, [ --with-debug Add debug code --with-debug=full Add debug code (adds memory checker, very slow)], diff --git a/include/my_sys.h b/include/my_sys.h index 2df8dbc9e89..78ec0a72ff0 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -559,7 +559,7 @@ extern File my_register_filename(File fd, const char *FileName, enum file_type type_of_file, uint error_message_number, myf MyFlags); extern File my_create(const char *FileName,int CreateFlags, - int AccsesFlags, myf MyFlags); + int AccessFlags, myf MyFlags); extern int my_close(File Filedes,myf MyFlags); extern File my_dup(File file, myf MyFlags); extern int my_mkdir(const char *dir, int Flags, myf MyFlags); diff --git a/mysql-test/r/ndb_partition_key.result b/mysql-test/r/ndb_partition_key.result index 8225fca7a54..743453eb912 100644 --- a/mysql-test/r/ndb_partition_key.result +++ b/mysql-test/r/ndb_partition_key.result @@ -165,6 +165,19 @@ ENGINE=NDB PARTITION BY KEY(c3) PARTITIONS 5; ALTER TABLE t1 COALESCE PARTITION 4; DROP TABLE t1; +CREATE TABLE t1 (a int primary key) +ENGINE=NDB +PARTITION BY KEY(a); +ALTER TABLE t1 OPTIMIZE PARTITION p0; +ERROR HY000: Table storage engine for 't1' doesn't have this option +ALTER TABLE t1 CHECK PARTITION p0; +ERROR HY000: Table storage engine for 't1' doesn't have this option +ALTER TABLE t1 REPAIR PARTITION p0; +ERROR HY000: Table storage engine for 't1' doesn't have this option +ALTER TABLE t1 ANALYZE PARTITION p0; +ERROR HY000: Table storage engine for 't1' doesn't have this option +ALTER TABLE t1 REBUILD PARTITION p0; +ERROR HY000: Table storage engine for 't1' doesn't have this option CREATE TABLE t1 ( c1 MEDIUMINT NOT NULL AUTO_INCREMENT, c2 TEXT NOT NULL, diff --git a/mysql-test/t/ndb_partition_key.test b/mysql-test/t/ndb_partition_key.test index 87933671529..ce939663ab8 100644 --- a/mysql-test/t/ndb_partition_key.test +++ b/mysql-test/t/ndb_partition_key.test @@ -155,6 +155,24 @@ ALTER TABLE t1 COALESCE PARTITION 4; DROP TABLE t1; # +# Bug 16822: OPTIMIZE TABLE hangs test +# +CREATE TABLE t1 (a int primary key) +ENGINE=NDB +PARTITION BY KEY(a); +--error 1031 +ALTER TABLE t1 OPTIMIZE PARTITION p0; +--error 1031 +ALTER TABLE t1 CHECK PARTITION p0; +--error 1031 +ALTER TABLE t1 REPAIR PARTITION p0; +--error 1031 +ALTER TABLE t1 ANALYZE PARTITION p0; +--error 1031 +ALTER TABLE t1 REBUILD PARTITION p0; +DROP TABLE t1; + +# # BUG 16806: ALTER TABLE fails # CREATE TABLE t1 ( diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index a4193e4eb33..4310f8d1c12 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -4687,7 +4687,9 @@ int ha_ndbcluster::create(const char *name, DBUG_RETURN(my_errno); } -int ha_ndbcluster::create_handler_files(const char *file) +int ha_ndbcluster::create_handler_files(const char *file, + const char *old_name, + bool rename_flag) { const char *name; Ndb* ndb; @@ -4698,6 +4700,10 @@ int ha_ndbcluster::create_handler_files(const char *file) DBUG_ENTER("create_handler_files"); + if (rename_flag) + { + DBUG_RETURN(FALSE); + } if (!(ndb= get_ndb())) DBUG_RETURN(HA_ERR_NO_CONNECTION); diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h index 0fb198ae64e..70f0c79bd20 100644 --- a/sql/ha_ndbcluster.h +++ b/sql/ha_ndbcluster.h @@ -611,7 +611,8 @@ class ha_ndbcluster: public handler int rename_table(const char *from, const char *to); int delete_table(const char *name); int create(const char *name, TABLE *form, HA_CREATE_INFO *info); - int create_handler_files(const char *file); + int create_handler_files(const char *file, const char *old_name, + bool rename_flag); int get_default_no_partitions(ulonglong max_rows); bool get_no_parts(const char *name, uint *no_parts); void set_auto_partitions(partition_info *part_info); diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 7cf841a5d71..15224a9bc55 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -404,88 +404,6 @@ int ha_partition::ha_initialise() MODULE meta data changes ****************************************************************************/ /* - Create partition names - - SYNOPSIS - create_partition_name() - out:out Created partition name string - in1 First part - in2 Second part - name_variant Normal, temporary or renamed partition name - - RETURN VALUE - NONE - - DESCRIPTION - This method is used to calculate the partition name, service routine to - the del_ren_cre_table method. -*/ - -#define NORMAL_PART_NAME 0 -#define TEMP_PART_NAME 1 -#define RENAMED_PART_NAME 2 -static void create_partition_name(char *out, const char *in1, - const char *in2, uint name_variant, - bool translate) -{ - char transl_part_name[FN_REFLEN]; - const char *transl_part; - - if (translate) - { - tablename_to_filename(in2, transl_part_name, FN_REFLEN); - transl_part= transl_part_name; - } - else - transl_part= in2; - if (name_variant == NORMAL_PART_NAME) - strxmov(out, in1, "#P#", transl_part, NullS); - else if (name_variant == TEMP_PART_NAME) - strxmov(out, in1, "#P#", transl_part, "#TMP#", NullS); - else if (name_variant == RENAMED_PART_NAME) - strxmov(out, in1, "#P#", transl_part, "#REN#", NullS); -} - -/* - Create subpartition name - - SYNOPSIS - create_subpartition_name() - out:out Created partition name string - in1 First part - in2 Second part - in3 Third part - name_variant Normal, temporary or renamed partition name - - RETURN VALUE - NONE - - DESCRIPTION - This method is used to calculate the subpartition name, service routine to - the del_ren_cre_table method. -*/ - -static void create_subpartition_name(char *out, const char *in1, - const char *in2, const char *in3, - uint name_variant) -{ - char transl_part_name[FN_REFLEN], transl_subpart_name[FN_REFLEN]; - - tablename_to_filename(in2, transl_part_name, FN_REFLEN); - tablename_to_filename(in3, transl_subpart_name, FN_REFLEN); - if (name_variant == NORMAL_PART_NAME) - strxmov(out, in1, "#P#", transl_part_name, - "#SP#", transl_subpart_name, NullS); - else if (name_variant == TEMP_PART_NAME) - strxmov(out, in1, "#P#", transl_part_name, - "#SP#", transl_subpart_name, "#TMP#", NullS); - else if (name_variant == RENAMED_PART_NAME) - strxmov(out, in1, "#P#", transl_part_name, - "#SP#", transl_subpart_name, "#REN#", NullS); -} - - -/* Delete a table SYNOPSIS @@ -575,7 +493,9 @@ int ha_partition::rename_table(const char *from, const char *to) and types of engines in the partitions. */ -int ha_partition::create_handler_files(const char *name) +int ha_partition::create_handler_files(const char *path, + const char *old_path, + bool rename_flag) { DBUG_ENTER("ha_partition::create_handler_files()"); @@ -583,10 +503,26 @@ int ha_partition::create_handler_files(const char *name) We need to update total number of parts since we might write the handler file as part of a partition management command */ - if (create_handler_file(name)) + if (rename_flag) { - my_error(ER_CANT_CREATE_HANDLER_FILE, MYF(0)); - DBUG_RETURN(1); + char name[FN_REFLEN]; + char old_name[FN_REFLEN]; + + strxmov(name, path, ha_par_ext, NullS); + strxmov(old_name, old_path, ha_par_ext, NullS); + if (my_delete(name, MYF(MY_WME)) || + my_rename(old_name, name, MYF(MY_WME))) + { + DBUG_RETURN(TRUE); + } + } + else + { + if (create_handler_file(path)) + { + my_error(ER_CANT_CREATE_HANDLER_FILE, MYF(0)); + DBUG_RETURN(1); + } } DBUG_RETURN(0); } @@ -652,45 +588,26 @@ int ha_partition::create(const char *name, TABLE *table_arg, int ha_partition::drop_partitions(const char *path) { List_iterator<partition_element> part_it(m_part_info->partitions); - List_iterator<partition_element> temp_it(m_part_info->temp_partitions); char part_name_buff[FN_REFLEN]; uint no_parts= m_part_info->partitions.elements; uint part_count= 0; uint no_subparts= m_part_info->no_subparts; uint i= 0; uint name_variant; - int error= 1; - bool reorged_parts= (m_reorged_parts > 0); - bool temp_partitions= (m_part_info->temp_partitions.elements > 0); + int ret_error; + int error= 0; DBUG_ENTER("ha_partition::drop_partitions"); - if (temp_partitions) - no_parts= m_part_info->temp_partitions.elements; do { - partition_element *part_elem; - if (temp_partitions) - { - /* - We need to remove the reorganised partitions that were put in the - temp_partitions-list. - */ - part_elem= temp_it++; - DBUG_ASSERT(part_elem->part_state == PART_TO_BE_DROPPED); - } - else - part_elem= part_it++; - if (part_elem->part_state == PART_TO_BE_DROPPED || - part_elem->part_state == PART_IS_CHANGED) + partition_element *part_elem= part_it++; + if (part_elem->part_state == PART_TO_BE_DROPPED) { handler *file; /* This part is to be dropped, meaning the part or all its subparts. */ name_variant= NORMAL_PART_NAME; - if (part_elem->part_state == PART_IS_CHANGED || - (part_elem->part_state == PART_TO_BE_DROPPED && temp_partitions)) - name_variant= RENAMED_PART_NAME; if (m_is_sub_partitioned) { List_iterator<partition_element> sub_it(part_elem->subpartitions); @@ -702,12 +619,10 @@ int ha_partition::drop_partitions(const char *path) create_subpartition_name(part_name_buff, path, part_elem->partition_name, sub_elem->partition_name, name_variant); - if (reorged_parts) - file= m_reorged_file[part_count++]; - else - file= m_file[part]; + file= m_file[part]; DBUG_PRINT("info", ("Drop subpartition %s", part_name_buff)); - error= file->delete_table((const char *) part_name_buff); + if ((ret_error= file->delete_table((const char *) part_name_buff))) + error= ret_error; } while (++j < no_subparts); } else @@ -715,12 +630,10 @@ int ha_partition::drop_partitions(const char *path) create_partition_name(part_name_buff, path, part_elem->partition_name, name_variant, TRUE); - if (reorged_parts) - file= m_reorged_file[part_count++]; - else - file= m_file[i]; + file= m_file[i]; DBUG_PRINT("info", ("Drop partition %s", part_name_buff)); - error= file->delete_table((const char *) part_name_buff); + if ((ret_error= file->delete_table((const char *) part_name_buff))) + error= ret_error; } if (part_elem->part_state == PART_IS_CHANGED) part_elem->part_state= PART_NORMAL; @@ -762,7 +675,8 @@ int ha_partition::rename_partitions(const char *path) uint no_subparts= m_part_info->no_subparts; uint i= 0; uint j= 0; - int error= 1; + int error= 0; + int ret_error; uint temp_partitions= m_part_info->temp_partitions.elements; handler *file; partition_element *part_elem, *sub_elem; @@ -770,6 +684,14 @@ int ha_partition::rename_partitions(const char *path) if (temp_partitions) { + /* + These are the reorganised partitions that have already been copied. + We delete the partitions and log the delete by inactivating the + delete log entry in the table log. We only need to synchronise + these writes before moving to the next loop since there is no + interaction among reorganised partitions, they cannot have the + same name. + */ do { part_elem= temp_it++; @@ -780,39 +702,59 @@ int ha_partition::rename_partitions(const char *path) { sub_elem= sub_it++; file= m_reorged_file[part_count++]; - create_subpartition_name(part_name_buff, path, - part_elem->partition_name, - sub_elem->partition_name, - RENAMED_PART_NAME); create_subpartition_name(norm_name_buff, path, part_elem->partition_name, sub_elem->partition_name, NORMAL_PART_NAME); - DBUG_PRINT("info", ("Rename subpartition from %s to %s", - norm_name_buff, part_name_buff)); - error= file->rename_table((const char *) norm_name_buff, - (const char *) part_name_buff); + DBUG_PRINT("info", ("Delete subpartition %s", norm_name_buff)); + if ((ret_error= file->delete_table((const char *) norm_name_buff))) + error= ret_error; + else if (deactivate_ddl_log_entry(sub_elem->log_entry->entry_pos)) + error= 1; + else + sub_elem->log_entry= NULL; /* Indicate success */ } while (++j < no_subparts); } else { file= m_reorged_file[part_count++]; - create_partition_name(part_name_buff, path, - part_elem->partition_name, RENAMED_PART_NAME, - TRUE); create_partition_name(norm_name_buff, path, part_elem->partition_name, NORMAL_PART_NAME, TRUE); - DBUG_PRINT("info", ("Rename partition from %s to %s", - norm_name_buff, part_name_buff)); - error= file->rename_table((const char *) norm_name_buff, - (const char *) part_name_buff); + DBUG_PRINT("info", ("Delete partition %s", norm_name_buff)); + if ((ret_error= file->delete_table((const char *) norm_name_buff))) + error= ret_error; + else if (deactivate_ddl_log_entry(part_elem->log_entry->entry_pos)) + error= 1; + else + part_elem->log_entry= NULL; /* Indicate success */ } } while (++i < temp_partitions); + VOID(sync_ddl_log()); } i= 0; do { + /* + When state is PART_IS_CHANGED it means that we have created a new + TEMP partition that is to be renamed to normal partition name and + we are to delete the old partition with currently the normal name. + + We perform this operation by + 1) Delete old partition with normal partition name + 2) Signal this in table log entry + 3) Synch table log to ensure we have consistency in crashes + 4) Rename temporary partition name to normal partition name + 5) Signal this to table log entry + It is not necessary to synch the last state since a new rename + should not corrupt things if there was no temporary partition. + + The only other parts we need to cater for are new parts that + replace reorganised parts. The reorganised parts were deleted + by the code above that goes through the temp_partitions list. + Thus the synch above makes it safe to simply perform step 4 and 5 + for those entries. + */ part_elem= part_it++; if (part_elem->part_state == PART_IS_CHANGED || (part_elem->part_state == PART_IS_ADDED && temp_partitions)) @@ -834,14 +776,12 @@ int ha_partition::rename_partitions(const char *path) if (part_elem->part_state == PART_IS_CHANGED) { file= m_reorged_file[part_count++]; - create_subpartition_name(part_name_buff, path, - part_elem->partition_name, - sub_elem->partition_name, - RENAMED_PART_NAME); - DBUG_PRINT("info", ("Rename subpartition from %s to %s", - norm_name_buff, part_name_buff)); - error= file->rename_table((const char *) norm_name_buff, - (const char *) part_name_buff); + DBUG_PRINT("info", ("Delete subpartition %s", norm_name_buff)); + if ((ret_error= file->delete_table((const char *) norm_name_buff))) + error= ret_error; + else if (deactivate_ddl_log_entry(sub_elem->log_entry->entry_pos)) + error= 1; + VOID(sync_ddl_log()); } file= m_new_file[part]; create_subpartition_name(part_name_buff, path, @@ -850,8 +790,13 @@ int ha_partition::rename_partitions(const char *path) TEMP_PART_NAME); DBUG_PRINT("info", ("Rename subpartition from %s to %s", part_name_buff, norm_name_buff)); - error= file->rename_table((const char *) part_name_buff, - (const char *) norm_name_buff); + if ((ret_error= file->rename_table((const char *) part_name_buff, + (const char *) norm_name_buff))) + error= ret_error; + else if (deactivate_ddl_log_entry(sub_elem->log_entry->entry_pos)) + error= 1; + else + sub_elem->log_entry= NULL; } while (++j < no_subparts); } else @@ -862,13 +807,12 @@ int ha_partition::rename_partitions(const char *path) if (part_elem->part_state == PART_IS_CHANGED) { file= m_reorged_file[part_count++]; - create_partition_name(part_name_buff, path, - part_elem->partition_name, RENAMED_PART_NAME, - TRUE); - DBUG_PRINT("info", ("Rename partition from %s to %s", - norm_name_buff, part_name_buff)); - error= file->rename_table((const char *) norm_name_buff, - (const char *) part_name_buff); + DBUG_PRINT("info", ("Delete partition %s", norm_name_buff)); + if ((ret_error= file->delete_table((const char *) norm_name_buff))) + error= ret_error; + else if (deactivate_ddl_log_entry(part_elem->log_entry->entry_pos)) + error= 1; + VOID(sync_ddl_log()); } file= m_new_file[i]; create_partition_name(part_name_buff, path, @@ -876,11 +820,17 @@ int ha_partition::rename_partitions(const char *path) TRUE); DBUG_PRINT("info", ("Rename partition from %s to %s", part_name_buff, norm_name_buff)); - error= file->rename_table((const char *) part_name_buff, - (const char *) norm_name_buff); + if ((ret_error= file->rename_table((const char *) part_name_buff, + (const char *) norm_name_buff))) + error= ret_error; + else if (deactivate_ddl_log_entry(part_elem->log_entry->entry_pos)) + error= 1; + else + part_elem->log_entry= NULL; } } } while (++i < no_parts); + VOID(sync_ddl_log()); DBUG_RETURN(error); } @@ -1872,8 +1822,8 @@ bool ha_partition::create_handler_file(const char *name) { part_elem= part_it++; if (part_elem->part_state != PART_NORMAL && - part_elem->part_state != PART_IS_ADDED && - part_elem->part_state != PART_IS_CHANGED) + part_elem->part_state != PART_TO_BE_ADDED && + part_elem->part_state != PART_CHANGED) continue; tablename_to_filename(part_elem->partition_name, part_name, FN_REFLEN); @@ -1924,8 +1874,8 @@ bool ha_partition::create_handler_file(const char *name) { part_elem= part_it++; if (part_elem->part_state != PART_NORMAL && - part_elem->part_state != PART_IS_ADDED && - part_elem->part_state != PART_IS_CHANGED) + part_elem->part_state != PART_TO_BE_ADDED && + part_elem->part_state != PART_CHANGED) continue; if (!m_is_sub_partitioned) { diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 96b615df71a..1a592bab340 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -179,7 +179,8 @@ public: virtual int rename_table(const char *from, const char *to); virtual int create(const char *name, TABLE *form, HA_CREATE_INFO *create_info); - virtual int create_handler_files(const char *name); + virtual int create_handler_files(const char *name, + const char *old_name, bool rename_flag); virtual void update_create_info(HA_CREATE_INFO *create_info); virtual char *update_table_comment(const char *comment); virtual int change_partitions(HA_CREATE_INFO *create_info, diff --git a/sql/handler.h b/sql/handler.h index 261a813bbfa..5cc2aa126c4 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -632,6 +632,7 @@ typedef struct { #define UNDEF_NODEGROUP 65535 class Item; +struct st_table_log_memory_entry; class partition_info; @@ -639,7 +640,6 @@ struct st_partition_iter; #define NOT_A_PARTITION_ID ((uint32)-1) - typedef struct st_ha_create_information { CHARSET_INFO *table_charset, *default_table_charset; @@ -1378,7 +1378,11 @@ public: virtual void drop_table(const char *name); virtual int create(const char *name, TABLE *form, HA_CREATE_INFO *info)=0; - virtual int create_handler_files(const char *name) { return FALSE;} + virtual int create_handler_files(const char *name, const char *old_name, + bool rename_flag) + { + return FALSE; + } virtual int change_partitions(HA_CREATE_INFO *create_info, const char *path, @@ -1386,7 +1390,7 @@ public: ulonglong *deleted, const void *pack_frm_data, uint pack_frm_len) - { return HA_ERR_WRONG_COMMAND; } + { print_error(HA_ERR_WRONG_COMMAND, MYF(0)); return TRUE; } virtual int drop_partitions(const char *path) { return HA_ERR_WRONG_COMMAND; } virtual int rename_partitions(const char *path) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index f79472a54f8..df7a88ccfbb 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -617,6 +617,101 @@ struct Query_cache_query_flags #define query_cache_invalidate_by_MyISAM_filename_ref NULL #endif /*HAVE_QUERY_CACHE*/ +/* + Error injector Macros to enable easy testing of recovery after failures + in various error cases. +*/ +#ifndef ERROR_INJECT_SUPPORT + +#define ERROR_INJECT(x) 0 +#define ERROR_INJECT_ACTION(x,action) 0 +#define ERROR_INJECT_CRASH(x) 0 +#define ERROR_INJECT_VALUE(x) 0 +#define ERROR_INJECT_VALUE_ACTION(x,action) 0 +#define ERROR_INJECT_VALUE_CRASH(x) 0 +#define SET_ERROR_INJECT_VALUE(x) + +#else + +inline bool +my_error_inject_name(const char *dbug_str) +{ + const char *extra_str= "-d,"; + char total_str[200]; + if (_db_strict_keyword_ (dbug_str)) + { + strxmov(total_str, extra_str, dbug_str, NullS); + DBUG_SET(total_str); + return 1; + } + return 0; +} + + +inline bool +my_error_inject(int value) +{ + THD *thd= current_thd; + if (thd->error_inject_value == (uint)value) + { + thd->error_inject_value= 0; + return 1; + } + return 0; +} + +/* + ERROR INJECT MODULE: + -------------------- + These macros are used to insert macros from the application code. + The event that activates those error injections can be activated + from SQL by using: + SET SESSION dbug=+d,code; + + After the error has been injected, the macros will automatically + remove the debug code, thus similar to using: + SET SESSION dbug=-d,code + from SQL. + + ERROR_INJECT_CRASH will inject a crash of the MySQL Server if code + is set when macro is called. ERROR_INJECT_CRASH can be used in + if-statements, it will always return FALSE unless of course it + crashes in which case it doesn't return at all. + + ERROR_INJECT_ACTION will inject the action specified in the action + parameter of the macro, before performing the action the code will + be removed such that no more events occur. ERROR_INJECT_ACTION + can also be used in if-statements and always returns FALSE. + ERROR_INJECT can be used in a normal if-statement, where the action + part is performed in the if-block. The macro returns TRUE if the + error was activated and otherwise returns FALSE. If activated the + code is removed. + + Sometimes it is necessary to perform error inject actions as a serie + of events. In this case one can use one variable on the THD object. + Thus one sets this value by using e.g. SET_ERROR_INJECT_VALUE(100). + Then one can later test for it by using ERROR_INJECT_CRASH_VALUE, + ERROR_INJECT_ACTION_VALUE and ERROR_INJECT_VALUE. This have the same + behaviour as the above described macros except that they use the + error inject value instead of a code used by DBUG macros. +*/ +#define SET_ERROR_INJECT_VALUE(x) \ + current_thd->error_inject_value= (x) +#define ERROR_INJECT_CRASH(code) \ + DBUG_EVALUATE_IF(code, (abort(), 0), 0) +#define ERROR_INJECT_ACTION(code, action) \ + (my_error_inject_name(code) ? ((action), 0) : 0) +#define ERROR_INJECT(code) \ + my_error_inject_name(code) +#define ERROR_INJECT_VALUE(value) \ + my_error_inject(value) +#define ERROR_INJECT_VALUE_ACTION(value,action) \ + (my_error_inject(value) ? (action) : 0) +#define ERROR_INJECT_VALUE_CRASH(value) \ + (my_error_inject(value) ? abort() : 0) + +#endif + uint build_table_path(char *buff, size_t bufflen, const char *db, const char *table, const char *ext); void write_bin_log(THD *thd, bool clear_error, @@ -1093,6 +1188,16 @@ uint prep_alter_part_table(THD *thd, TABLE *table, ALTER_INFO *alter_info, bool remove_table_from_cache(THD *thd, const char *db, const char *table, uint flags); +#define NORMAL_PART_NAME 0 +#define TEMP_PART_NAME 1 +#define RENAMED_PART_NAME 2 +void create_partition_name(char *out, const char *in1, + const char *in2, uint name_variant, + bool translate); +void create_subpartition_name(char *out, const char *in1, + const char *in2, const char *in3, + uint name_variant); + typedef struct st_lock_param_type { ulonglong copied; @@ -1112,14 +1217,97 @@ typedef struct st_lock_param_type uint key_count; uint db_options; uint pack_frm_len; + partition_info *part_info; } ALTER_PARTITION_PARAM_TYPE; void mem_alloc_error(size_t size); -#define WFRM_INITIAL_WRITE 1 -#define WFRM_CREATE_HANDLER_FILES 2 + +enum ddl_log_entry_code +{ + /* + DDL_LOG_EXECUTE_CODE: + This is a code that indicates that this is a log entry to + be executed, from this entry a linked list of log entries + can be found and executed. + DDL_LOG_ENTRY_CODE: + An entry to be executed in a linked list from an execute log + entry. + DDL_IGNORE_LOG_ENTRY_CODE: + An entry that is to be ignored + */ + DDL_LOG_EXECUTE_CODE = 'e', + DDL_LOG_ENTRY_CODE = 'l', + DDL_IGNORE_LOG_ENTRY_CODE = 'i' +}; + +enum ddl_log_action_code +{ + /* + The type of action that a DDL_LOG_ENTRY_CODE entry is to + perform. + DDL_LOG_DELETE_ACTION: + Delete an entity + DDL_LOG_RENAME_ACTION: + Rename an entity + DDL_LOG_REPLACE_ACTION: + Rename an entity after removing the previous entry with the + new name, that is replace this entry. + */ + DDL_LOG_DELETE_ACTION = 'd', + DDL_LOG_RENAME_ACTION = 'r', + DDL_LOG_REPLACE_ACTION = 's' +}; + + +typedef struct st_ddl_log_entry +{ + const char *name; + const char *from_name; + const char *handler_name; + uint next_entry; + uint entry_pos; + enum ddl_log_entry_code entry_type; + enum ddl_log_action_code action_type; + /* + Most actions have only one phase. REPLACE does however have two + phases. The first phase removes the file with the new name if + there was one there before and the second phase renames the + old name to the new name. + */ + char phase; +} DDL_LOG_ENTRY; + +typedef struct st_ddl_log_memory_entry +{ + uint entry_pos; + struct st_ddl_log_memory_entry *next_log_entry; + struct st_ddl_log_memory_entry *prev_log_entry; + struct st_ddl_log_memory_entry *next_active_log_entry; +} DDL_LOG_MEMORY_ENTRY; + + + +#define DDL_LOG_HANDLER_TYPE_LEN 32 + +bool write_ddl_log_entry(DDL_LOG_ENTRY *ddl_log_entry, + DDL_LOG_MEMORY_ENTRY **active_entry); +bool write_execute_ddl_log_entry(uint first_entry, + bool complete, + DDL_LOG_MEMORY_ENTRY **active_entry); +bool deactivate_ddl_log_entry(uint entry_no); +void release_ddl_log_memory_entry(DDL_LOG_MEMORY_ENTRY *log_entry); +bool sync_ddl_log(); +void release_ddl_log(); +void execute_ddl_log_recovery(); +bool execute_ddl_log_entry(uint first_entry); +void lock_global_ddl_log(); +void unlock_global_ddl_log(); + +#define WFRM_WRITE_SHADOW 1 +#define WFRM_INSTALL_SHADOW 2 #define WFRM_PACK_FRM 4 bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags); -bool abort_and_upgrade_lock(ALTER_PARTITION_PARAM_TYPE *lpt); +void abort_and_upgrade_lock(ALTER_PARTITION_PARAM_TYPE *lpt); void close_open_tables_and_downgrade(ALTER_PARTITION_PARAM_TYPE *lpt); void mysql_wait_completed_table(ALTER_PARTITION_PARAM_TYPE *lpt, TABLE *my_table); @@ -1290,6 +1478,9 @@ extern ulong delayed_insert_timeout; extern ulong delayed_insert_limit, delayed_queue_size; extern ulong delayed_insert_threads, delayed_insert_writes; extern ulong delayed_rows_in_use,delayed_insert_errors; +#ifdef ERROR_INJECT_SUPPORT +extern ulong error_inject_value; +#endif extern ulong slave_open_temp_tables; extern ulong query_cache_size, query_cache_min_res_unit; extern ulong slow_launch_threads, slow_launch_time; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 584c3727dd6..73990a950eb 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3665,6 +3665,7 @@ we force server id to 2, but this MySQL server will not act as a slave."); unireg_abort(1); } } + execute_ddl_log_recovery(); create_shutdown_thread(); create_maintenance_thread(); @@ -3695,6 +3696,7 @@ we force server id to 2, but this MySQL server will not act as a slave."); /* (void) pthread_attr_destroy(&connection_attrib); */ DBUG_PRINT("quit",("Exiting main thread")); + release_ddl_log(); #ifndef __WIN__ #ifdef EXTRA_DEBUG2 diff --git a/sql/partition_element.h b/sql/partition_element.h index d20715d2408..13693934c0f 100644 --- a/sql/partition_element.h +++ b/sql/partition_element.h @@ -36,6 +36,8 @@ enum partition_state { PART_IS_ADDED= 8 }; +struct st_ddl_log_memory_entry; + class partition_element :public Sql_alloc { public: List<partition_element> subpartitions; @@ -44,6 +46,7 @@ public: ulonglong part_min_rows; char *partition_name; char *tablespace_name; + struct st_ddl_log_memory_entry *log_entry; longlong range_value; char* part_comment; char* data_file_name; @@ -55,7 +58,8 @@ public: partition_element() : part_max_rows(0), part_min_rows(0), partition_name(NULL), - tablespace_name(NULL), range_value(0), part_comment(NULL), + tablespace_name(NULL), log_entry(NULL), + range_value(0), part_comment(NULL), data_file_name(NULL), index_file_name(NULL), engine_type(NULL),part_state(PART_NORMAL), nodegroup_id(UNDEF_NODEGROUP), has_null_value(FALSE) diff --git a/sql/partition_info.h b/sql/partition_info.h index 08669f8001e..084e9b7fadc 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -28,7 +28,7 @@ typedef int (*get_part_id_func)(partition_info *part_info, longlong *func_value); typedef uint32 (*get_subpart_id_func)(partition_info *part_info); - +struct st_ddl_log_memory_entry; class partition_info : public Sql_alloc { @@ -76,7 +76,11 @@ public: Item *subpart_expr; Item *item_free_list; - + + struct st_ddl_log_memory_entry *first_log_entry; + struct st_ddl_log_memory_entry *exec_log_entry; + struct st_ddl_log_memory_entry *frm_log_entry; + /* A bitmap of partitions used by the current query. Usage pattern: @@ -191,6 +195,7 @@ public: part_field_array(NULL), subpart_field_array(NULL), full_part_field_array(NULL), part_expr(NULL), subpart_expr(NULL), item_free_list(NULL), + first_log_entry(NULL), exec_log_entry(NULL), frm_log_entry(NULL), list_array(NULL), part_info_string(NULL), part_func_string(NULL), subpart_func_string(NULL), diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index e8766b3d882..9aa4d2f74eb 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5826,3 +5826,5 @@ ER_NDB_CANT_SWITCH_BINLOG_FORMAT eng "The NDB cluster engine does not support changing the binlog format on the fly yet" ER_PARTITION_NO_TEMPORARY eng "Cannot create temporary table with partitions" +ER_DDL_LOG_ERROR + eng "Error in DDL log" diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 7499cec6147..4d5eb070d51 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -6156,23 +6156,17 @@ bool is_equal(const LEX_STRING *a, const LEX_STRING *b) old_lock_level Old lock level */ -bool abort_and_upgrade_lock(ALTER_PARTITION_PARAM_TYPE *lpt) +void abort_and_upgrade_lock(ALTER_PARTITION_PARAM_TYPE *lpt) { uint flags= RTFC_WAIT_OTHER_THREAD_FLAG | RTFC_CHECK_KILLED_FLAG; - int error= FALSE; DBUG_ENTER("abort_and_upgrade_locks"); lpt->old_lock_type= lpt->table->reginfo.lock_type; VOID(pthread_mutex_lock(&LOCK_open)); mysql_lock_abort(lpt->thd, lpt->table, TRUE); VOID(remove_table_from_cache(lpt->thd, lpt->db, lpt->table_name, flags)); - if (lpt->thd->killed) - { - lpt->thd->no_warnings_for_error= 0; - error= TRUE; - } VOID(pthread_mutex_unlock(&LOCK_open)); - DBUG_RETURN(error); + DBUG_VOID_RETURN; } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 123152c95ec..d8d81af72b9 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -223,6 +223,9 @@ THD::THD() cuted_fields= sent_row_count= 0L; limit_found_rows= 0; statement_id_counter= 0UL; +#ifdef ERROR_INJECT_SUPPORT + error_inject_value= 0UL; +#endif // Must be reset to handle error with THD's created for init of mysqld lex->current_select= 0; start_time=(time_t) 0; diff --git a/sql/sql_class.h b/sql/sql_class.h index cc1c5488ae5..1aa48841a41 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1119,6 +1119,9 @@ public: query_id_t query_id, warn_id; ulong thread_id, col_access; +#ifdef ERROR_INJECT_SUPPORT + ulong error_inject_value; +#endif /* Statement id is thread-wide. This counter is used to generate ids */ ulong statement_id_counter; ulong rand_saved_seed1, rand_saved_seed2; diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index c13c8c90103..351ed0dd5a6 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -1777,15 +1777,10 @@ char *generate_partition_syntax(partition_info *part_info, char path[FN_REFLEN]; int err= 0; List_iterator<partition_element> part_it(part_info->partitions); - List_iterator<partition_element> temp_it(part_info->temp_partitions); File fptr; char *buf= NULL; //Return buffer - uint use_temp= 0; - uint no_temp_parts= part_info->temp_partitions.elements; - bool write_part_state; DBUG_ENTER("generate_partition_syntax"); - write_part_state= (part_info->part_state && !part_info->part_state_len); if (unlikely(((fptr= create_temp_file(path,mysql_tmpdir,"psy", O_RDWR | O_BINARY | O_TRUNC | O_TEMPORARY, MYF(MY_WME)))) < 0)) @@ -1850,67 +1845,26 @@ char *generate_partition_syntax(partition_info *part_info, err+= add_space(fptr); } } - no_parts= part_info->no_parts; - tot_no_parts= no_parts + no_temp_parts; + tot_no_parts= part_info->partitions.elements; no_subparts= part_info->no_subparts; if (write_all || (!part_info->use_default_partitions)) { + bool first= TRUE; err+= add_begin_parenthesis(fptr); i= 0; do { - /* - We need to do some clever list manipulation here since we have two - different needs for our list processing and here we take some of the - cost of using a simpler list processing for the other parts of the - code. - - ALTER TABLE REORGANIZE PARTITIONS has the list of partitions to be - the final list as the main list and the reorganised partitions is in - the temporary partition list. Thus when finding the first part added - we insert the temporary list if there is such a list. If there is no - temporary list we are performing an ADD PARTITION. - */ - if (use_temp && use_temp <= no_temp_parts) - { - part_elem= temp_it++; - DBUG_ASSERT(no_temp_parts); - no_temp_parts--; - } - else if (use_temp) - { - DBUG_ASSERT(no_parts); - part_elem= save_part_elem; - use_temp= 0; - no_parts--; - } - else + part_elem= part_it++; + if (part_elem->part_state != PART_TO_BE_DROPPED && + part_elem->part_state != PART_REORGED_DROPPED) { - part_elem= part_it++; - if ((part_elem->part_state == PART_TO_BE_ADDED || - part_elem->part_state == PART_IS_ADDED) && no_temp_parts) - { - save_part_elem= part_elem; - part_elem= temp_it++; - no_temp_parts--; - use_temp= 1; - } - else + if (!first) { - DBUG_ASSERT(no_parts); - no_parts--; - } - } - - if (part_elem->part_state != PART_IS_DROPPED) - { - if (write_part_state) - { - uint32 part_state_id= part_info->part_state_len; - part_info->part_state[part_state_id]= (uchar)part_elem->part_state; - part_info->part_state_len= part_state_id+1; + err+= add_comma(fptr); + err+= add_space(fptr); } + first= FALSE; err+= add_partition(fptr); err+= add_string(fptr, part_elem->partition_name); err+= add_space(fptr); @@ -1940,16 +1894,10 @@ char *generate_partition_syntax(partition_info *part_info, err+= add_end_parenthesis(fptr); } while (++j < no_subparts); } - if (i != (tot_no_parts-1)) - { - err+= add_comma(fptr); - err+= add_space(fptr); - } } if (i == (tot_no_parts-1)) err+= add_end_parenthesis(fptr); } while (++i < tot_no_parts); - DBUG_ASSERT(!no_parts && !no_temp_parts); } if (err) goto close_file; @@ -3512,27 +3460,6 @@ set_engine_all_partitions(partition_info *part_info, } /* SYNOPSIS - fast_alter_partition_error_handler() - lpt Container for parameters - - RETURN VALUES - None - - DESCRIPTION - Support routine to clean up after failures of on-line ALTER TABLE - for partition management. -*/ - -static void fast_alter_partition_error_handler(ALTER_PARTITION_PARAM_TYPE *lpt) -{ - DBUG_ENTER("fast_alter_partition_error_handler"); - /* TODO: WL 2826 Error handling */ - DBUG_VOID_RETURN; -} - - -/* - SYNOPSIS fast_end_partition() thd Thread object out:copied Number of records copied @@ -3552,6 +3479,7 @@ static void fast_alter_partition_error_handler(ALTER_PARTITION_PARAM_TYPE *lpt) static int fast_end_partition(THD *thd, ulonglong copied, ulonglong deleted, + TABLE *table, TABLE_LIST *table_list, bool is_empty, ALTER_PARTITION_PARAM_TYPE *lpt, bool written_bin_log) @@ -3579,7 +3507,7 @@ static int fast_end_partition(THD *thd, ulonglong copied, send_ok(thd,copied+deleted,0L,tmp_name); DBUG_RETURN(FALSE); } - fast_alter_partition_error_handler(lpt); + table->file->print_error(error, MYF(0)); DBUG_RETURN(TRUE); } @@ -3828,7 +3756,8 @@ uint prep_alter_part_table(THD *thd, TABLE *table, ALTER_INFO *alter_info, after the change as before. Thus we can reply ok immediately without any changes at all. */ - DBUG_RETURN(fast_end_partition(thd, ULL(0), ULL(0), NULL, + DBUG_RETURN(fast_end_partition(thd, ULL(0), ULL(0), + table, NULL, TRUE, NULL, FALSE)); } else if (new_part_no > curr_part_no) @@ -4180,6 +4109,7 @@ that are reorganised. my_error(ER_ROW_IS_REFERENCED, MYF(0)); DBUG_RETURN(TRUE); } + tab_part_info->no_parts-= no_parts_dropped; } else if ((alter_info->flags & ALTER_OPTIMIZE_PARTITION) || (alter_info->flags & ALTER_ANALYZE_PARTITION) || @@ -4224,6 +4154,11 @@ that are reorganised. my_error(ER_DROP_PARTITION_NON_EXISTENT, MYF(0), ptr); DBUG_RETURN(TRUE); } + if (!(*fast_alter_partition)) + { + table->file->print_error(HA_ERR_WRONG_COMMAND, MYF(0)); + DBUG_RETURN(TRUE); + } } else if (alter_info->flags & ALTER_COALESCE_PARTITION) { @@ -4648,14 +4583,17 @@ the generated partition syntax in a correct manner. static bool mysql_change_partitions(ALTER_PARTITION_PARAM_TYPE *lpt) { char path[FN_REFLEN+1]; + int error; + handler *file= lpt->table->file; DBUG_ENTER("mysql_change_partitions"); build_table_filename(path, sizeof(path), lpt->db, lpt->table_name, ""); - DBUG_RETURN(lpt->table->file->change_partitions(lpt->create_info, path, - &lpt->copied, - &lpt->deleted, - lpt->pack_frm_data, - lpt->pack_frm_len)); + DBUG_RETURN(file->change_partitions(lpt->create_info, + path, + &lpt->copied, + &lpt->deleted, + lpt->pack_frm_data, + lpt->pack_frm_len)); } @@ -4681,10 +4619,17 @@ static bool mysql_change_partitions(ALTER_PARTITION_PARAM_TYPE *lpt) static bool mysql_rename_partitions(ALTER_PARTITION_PARAM_TYPE *lpt) { char path[FN_REFLEN+1]; + int error; DBUG_ENTER("mysql_rename_partitions"); build_table_filename(path, sizeof(path), lpt->db, lpt->table_name, ""); - DBUG_RETURN(lpt->table->file->rename_partitions(path)); + if ((error= lpt->table->file->rename_partitions(path))) + { + if (error != 1) + lpt->table->file->print_error(error, MYF(0)); + DBUG_RETURN(TRUE); + } + DBUG_RETURN(FALSE); } @@ -4715,11 +4660,13 @@ static bool mysql_drop_partitions(ALTER_PARTITION_PARAM_TYPE *lpt) List_iterator<partition_element> part_it(part_info->partitions); uint i= 0; uint remove_count= 0; + int error; DBUG_ENTER("mysql_drop_partitions"); build_table_filename(path, sizeof(path), lpt->db, lpt->table_name, ""); - if (lpt->table->file->drop_partitions(path)) + if ((error= lpt->table->file->drop_partitions(path))) { + lpt->table->file->print_error(error, MYF(0)); DBUG_RETURN(TRUE); } do @@ -4737,6 +4684,765 @@ static bool mysql_drop_partitions(ALTER_PARTITION_PARAM_TYPE *lpt) /* + Insert log entry into list + SYNOPSIS + insert_part_info_log_entry_list() + log_entry + RETURN VALUES + NONE +*/ + +static void insert_part_info_log_entry_list(partition_info *part_info, + DDL_LOG_MEMORY_ENTRY *log_entry) +{ + log_entry->next_active_log_entry= part_info->first_log_entry; + part_info->first_log_entry= log_entry; +} + + +/* + Release all log entries for this partition info struct + SYNOPSIS + release_part_info_log_entries() + first_log_entry First log entry in list to release + RETURN VALUES + NONE +*/ + +static void release_part_info_log_entries(DDL_LOG_MEMORY_ENTRY *log_entry) +{ + DBUG_ENTER("release_part_info_log_entries"); + + while (log_entry) + { + release_ddl_log_memory_entry(log_entry); + log_entry= log_entry->next_active_log_entry; + } + DBUG_VOID_RETURN; +} + + +/* + Log an delete/rename frm file + SYNOPSIS + write_log_replace_delete_frm() + lpt Struct for parameters + next_entry Next reference to use in log record + path Name to rename from + rename_flag TRUE if rename, else delete + RETURN VALUES + TRUE Error + FALSE Success + DESCRIPTION + Support routine that writes a replace or delete of an frm file into the + ddl log. It also inserts an entry that keeps track of used space into + the partition info object +*/ + +static bool write_log_replace_delete_frm(ALTER_PARTITION_PARAM_TYPE *lpt, + uint next_entry, + const char *from_path, + const char *to_path, + bool replace_flag) +{ + DDL_LOG_ENTRY ddl_log_entry; + DDL_LOG_MEMORY_ENTRY *log_entry; + DBUG_ENTER("write_log_replace_delete_frm"); + + if (replace_flag) + ddl_log_entry.action_type= DDL_LOG_REPLACE_ACTION; + else + ddl_log_entry.action_type= DDL_LOG_DELETE_ACTION; + ddl_log_entry.next_entry= next_entry; + ddl_log_entry.handler_name= "frm"; + ddl_log_entry.name= to_path; + if (replace_flag) + ddl_log_entry.from_name= from_path; + if (write_ddl_log_entry(&ddl_log_entry, &log_entry)) + { + DBUG_RETURN(TRUE); + } + insert_part_info_log_entry_list(lpt->part_info, log_entry); + DBUG_RETURN(FALSE); +} + + +/* + Log final partition changes in change partition + SYNOPSIS + write_log_changed_partitions() + lpt Struct containing parameters + RETURN VALUES + TRUE Error + FALSE Success + DESCRIPTION + This code is used to perform safe ADD PARTITION for HASH partitions + and COALESCE for HASH partitions and REORGANIZE for any type of + partitions. + We prepare entries for all partitions except the reorganised partitions + in REORGANIZE partition, those are handled by + write_log_dropped_partitions. For those partitions that are replaced + special care is needed to ensure that this is performed correctly and + this requires a two-phased approach with this log as a helper for this. + + This code is closely intertwined with the code in rename_partitions in + the partition handler. +*/ + +static bool write_log_changed_partitions(ALTER_PARTITION_PARAM_TYPE *lpt, + uint *next_entry, const char *path) +{ + DDL_LOG_ENTRY ddl_log_entry; + partition_info *part_info= lpt->part_info; + DDL_LOG_MEMORY_ENTRY *log_entry; + char tmp_path[FN_LEN]; + char normal_path[FN_LEN]; + List_iterator<partition_element> part_it(part_info->partitions); + uint temp_partitions= part_info->temp_partitions.elements; + uint no_elements= part_info->partitions.elements; + uint i= 0; + DBUG_ENTER("write_log_changed_partitions"); + + do + { + partition_element *part_elem= part_it++; + if (part_elem->part_state == PART_IS_CHANGED || + (part_elem->part_state == PART_IS_ADDED && temp_partitions)) + { + if (part_info->is_sub_partitioned()) + { + List_iterator<partition_element> sub_it(part_elem->subpartitions); + uint no_subparts= part_info->no_subparts; + uint j= 0; + do + { + partition_element *sub_elem= sub_it++; + ddl_log_entry.next_entry= *next_entry; + ddl_log_entry.handler_name= + ha_resolve_storage_engine_name(sub_elem->engine_type); + create_subpartition_name(tmp_path, path, + part_elem->partition_name, + sub_elem->partition_name, + TEMP_PART_NAME); + create_subpartition_name(normal_path, path, + part_elem->partition_name, + sub_elem->partition_name, + NORMAL_PART_NAME); + ddl_log_entry.name= normal_path; + ddl_log_entry.from_name= tmp_path; + if (part_elem->part_state == PART_IS_CHANGED) + ddl_log_entry.action_type= DDL_LOG_REPLACE_ACTION; + else + ddl_log_entry.action_type= DDL_LOG_RENAME_ACTION; + if (write_ddl_log_entry(&ddl_log_entry, &log_entry)) + { + DBUG_RETURN(TRUE); + } + *next_entry= log_entry->entry_pos; + sub_elem->log_entry= log_entry; + insert_part_info_log_entry_list(part_info, log_entry); + } while (++j < no_subparts); + } + else + { + ddl_log_entry.next_entry= *next_entry; + ddl_log_entry.handler_name= + ha_resolve_storage_engine_name(part_elem->engine_type); + create_partition_name(tmp_path, path, + part_elem->partition_name, + TEMP_PART_NAME, TRUE); + create_partition_name(normal_path, path, + part_elem->partition_name, + NORMAL_PART_NAME, TRUE); + ddl_log_entry.name= normal_path; + ddl_log_entry.from_name= tmp_path; + if (part_elem->part_state == PART_IS_CHANGED) + ddl_log_entry.action_type= DDL_LOG_REPLACE_ACTION; + else + ddl_log_entry.action_type= DDL_LOG_RENAME_ACTION; + if (write_ddl_log_entry(&ddl_log_entry, &log_entry)) + { + DBUG_RETURN(TRUE); + } + *next_entry= log_entry->entry_pos; + part_elem->log_entry= log_entry; + insert_part_info_log_entry_list(part_info, log_entry); + } + } + } while (++i < no_elements); + DBUG_RETURN(FALSE); +} + + +/* + Log dropped partitions + SYNOPSIS + write_log_dropped_partitions() + lpt Struct containing parameters + RETURN VALUES + TRUE Error + FALSE Success +*/ + +static bool write_log_dropped_partitions(ALTER_PARTITION_PARAM_TYPE *lpt, + uint *next_entry, + const char *path, + bool temp_list) +{ + DDL_LOG_ENTRY ddl_log_entry; + partition_info *part_info= lpt->part_info; + DDL_LOG_MEMORY_ENTRY *log_entry; + char tmp_path[FN_LEN]; + List_iterator<partition_element> part_it(part_info->partitions); + List_iterator<partition_element> temp_it(part_info->temp_partitions); + uint no_temp_partitions= part_info->temp_partitions.elements; + uint no_elements= part_info->partitions.elements; + uint i= 0; + DBUG_ENTER("write_log_dropped_partitions"); + + ddl_log_entry.action_type= DDL_LOG_DELETE_ACTION; + if (temp_list) + no_elements= no_temp_partitions; + while (no_elements--) + { + partition_element *part_elem; + if (temp_list) + part_elem= temp_it++; + else + part_elem= part_it++; + if (part_elem->part_state == PART_TO_BE_DROPPED || + part_elem->part_state == PART_TO_BE_ADDED || + part_elem->part_state == PART_CHANGED) + { + uint name_variant; + if (part_elem->part_state == PART_CHANGED || + (part_elem->part_state == PART_TO_BE_ADDED && + no_temp_partitions)) + name_variant= TEMP_PART_NAME; + else + name_variant= NORMAL_PART_NAME; + if (part_info->is_sub_partitioned()) + { + List_iterator<partition_element> sub_it(part_elem->subpartitions); + uint no_subparts= part_info->no_subparts; + uint j= 0; + do + { + partition_element *sub_elem= sub_it++; + ddl_log_entry.next_entry= *next_entry; + ddl_log_entry.handler_name= + ha_resolve_storage_engine_name(sub_elem->engine_type); + create_subpartition_name(tmp_path, path, + part_elem->partition_name, + sub_elem->partition_name, + name_variant); + ddl_log_entry.name= tmp_path; + if (write_ddl_log_entry(&ddl_log_entry, &log_entry)) + { + DBUG_RETURN(TRUE); + } + *next_entry= log_entry->entry_pos; + if (temp_list) + sub_elem->log_entry= log_entry; + insert_part_info_log_entry_list(part_info, log_entry); + } while (++j < no_subparts); + } + else + { + ddl_log_entry.next_entry= *next_entry; + ddl_log_entry.handler_name= + ha_resolve_storage_engine_name(part_elem->engine_type); + create_partition_name(tmp_path, path, + part_elem->partition_name, + name_variant, TRUE); + ddl_log_entry.name= tmp_path; + if (write_ddl_log_entry(&ddl_log_entry, &log_entry)) + { + DBUG_RETURN(TRUE); + } + *next_entry= log_entry->entry_pos; + if (temp_list) + part_elem->log_entry= log_entry; + insert_part_info_log_entry_list(part_info, log_entry); + } + } + } + DBUG_RETURN(FALSE); +} + + +/* + Set execute log entry in ddl log for this partitioned table + SYNOPSIS + set_part_info_exec_log_entry() + part_info Partition info object + exec_log_entry Log entry + RETURN VALUES + NONE +*/ + +static void set_part_info_exec_log_entry(partition_info *part_info, + DDL_LOG_MEMORY_ENTRY *exec_log_entry) +{ + part_info->exec_log_entry= exec_log_entry; + exec_log_entry->next_active_log_entry= NULL; +} + + +/* + Write the log entry to ensure that the shadow frm file is removed at + crash. + SYNOPSIS + write_log_drop_shadow_frm() + lpt Struct containing parameters + install_frm Should we log action to install shadow frm or should + the action be to remove the shadow frm file. + RETURN VALUES + TRUE Error + FALSE Success + DESCRIPTION + Prepare an entry to the ddl log indicating a drop/install of the shadow frm + file and its corresponding handler file. +*/ + +static bool write_log_drop_shadow_frm(ALTER_PARTITION_PARAM_TYPE *lpt) +{ + DDL_LOG_ENTRY ddl_log_entry; + partition_info *part_info= lpt->part_info; + DDL_LOG_MEMORY_ENTRY *log_entry; + DDL_LOG_MEMORY_ENTRY *exec_log_entry= NULL; + char shadow_path[FN_LEN]; + DBUG_ENTER("write_log_drop_shadow_frm"); + + build_table_filename(shadow_path, sizeof(shadow_path), lpt->db, + lpt->table_name, "#"); + lock_global_ddl_log(); + if (write_log_replace_delete_frm(lpt, 0UL, NULL, + (const char*)shadow_path, FALSE)) + goto error; + log_entry= part_info->first_log_entry; + if (write_execute_ddl_log_entry(log_entry->entry_pos, + FALSE, &exec_log_entry)) + goto error; + unlock_global_ddl_log(); + set_part_info_exec_log_entry(part_info, exec_log_entry); + DBUG_RETURN(FALSE); + +error: + release_part_info_log_entries(part_info->first_log_entry); + unlock_global_ddl_log(); + part_info->first_log_entry= NULL; + my_error(ER_DDL_LOG_ERROR, MYF(0)); + DBUG_RETURN(TRUE); +} + + +/* + Log renaming of shadow frm to real frm name and dropping of old frm + SYNOPSIS + write_log_rename_frm() + lpt Struct containing parameters + RETURN VALUES + TRUE Error + FALSE Success + DESCRIPTION + Prepare an entry to ensure that we complete the renaming of the frm + file if failure occurs in the middle of the rename process. +*/ + +static bool write_log_rename_frm(ALTER_PARTITION_PARAM_TYPE *lpt) +{ + DDL_LOG_ENTRY ddl_log_entry; + partition_info *part_info= lpt->part_info; + DDL_LOG_MEMORY_ENTRY *log_entry; + DDL_LOG_MEMORY_ENTRY *exec_log_entry= part_info->exec_log_entry; + char path[FN_LEN]; + char shadow_path[FN_LEN]; + DDL_LOG_MEMORY_ENTRY *old_first_log_entry= part_info->first_log_entry; + DBUG_ENTER("write_log_rename_frm"); + + part_info->first_log_entry= NULL; + build_table_filename(path, sizeof(path), lpt->db, + lpt->table_name, ""); + build_table_filename(shadow_path, sizeof(shadow_path), lpt->db, + lpt->table_name, "#"); + lock_global_ddl_log(); + if (write_log_replace_delete_frm(lpt, 0UL, path, shadow_path, FALSE)) + goto error; + log_entry= part_info->first_log_entry; + part_info->frm_log_entry= log_entry; + if (write_execute_ddl_log_entry(log_entry->entry_pos, + FALSE, &exec_log_entry)) + goto error; + release_part_info_log_entries(old_first_log_entry); + unlock_global_ddl_log(); + DBUG_RETURN(FALSE); + +error: + release_part_info_log_entries(part_info->first_log_entry); + unlock_global_ddl_log(); + part_info->first_log_entry= old_first_log_entry; + part_info->frm_log_entry= NULL; + my_error(ER_DDL_LOG_ERROR, MYF(0)); + DBUG_RETURN(TRUE); +} + + +/* + Write the log entries to ensure that the drop partition command is completed + even in the presence of a crash. + + SYNOPSIS + write_log_drop_partition() + lpt Struct containing parameters + RETURN VALUES + TRUE Error + FALSE Success + DESCRIPTION + Prepare entries to the ddl log indicating all partitions to drop and to + install the shadow frm file and remove the old frm file. +*/ + +static bool write_log_drop_partition(ALTER_PARTITION_PARAM_TYPE *lpt) +{ + DDL_LOG_ENTRY ddl_log_entry; + partition_info *part_info= lpt->part_info; + DDL_LOG_MEMORY_ENTRY *log_entry; + DDL_LOG_MEMORY_ENTRY *exec_log_entry= part_info->exec_log_entry; + char tmp_path[FN_LEN]; + char path[FN_LEN]; + uint next_entry= 0; + DDL_LOG_MEMORY_ENTRY *old_first_log_entry= part_info->first_log_entry; + DBUG_ENTER("write_log_drop_partition"); + + part_info->first_log_entry= NULL; + build_table_filename(path, sizeof(path), lpt->db, + lpt->table_name, ""); + build_table_filename(tmp_path, sizeof(tmp_path), lpt->db, + lpt->table_name, "#"); + lock_global_ddl_log(); + if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path, + FALSE)) + goto error; + if (write_log_replace_delete_frm(lpt, next_entry, (const char*)path, + (const char*)tmp_path, TRUE)) + goto error; + log_entry= part_info->first_log_entry; + part_info->frm_log_entry= log_entry; + if (write_execute_ddl_log_entry(log_entry->entry_pos, + FALSE, &exec_log_entry)) + goto error; + release_part_info_log_entries(old_first_log_entry); + unlock_global_ddl_log(); + DBUG_RETURN(FALSE); + +error: + release_part_info_log_entries(part_info->first_log_entry); + unlock_global_ddl_log(); + part_info->first_log_entry= old_first_log_entry; + part_info->frm_log_entry= NULL; + my_error(ER_DDL_LOG_ERROR, MYF(0)); + DBUG_RETURN(TRUE); +} + + +/* + Write the log entries to ensure that the add partition command is not + executed at all if a crash before it has completed + + SYNOPSIS + write_log_add_change_partition() + lpt Struct containing parameters + RETURN VALUES + TRUE Error + FALSE Success + DESCRIPTION + Prepare entries to the ddl log indicating all partitions to drop and to + remove the shadow frm file. + We always inject entries backwards in the list in the ddl log since we + don't know the entry position until we have written it. +*/ + +static bool write_log_add_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt) +{ + partition_info *part_info= lpt->part_info; + DDL_LOG_MEMORY_ENTRY *log_entry; + DDL_LOG_MEMORY_ENTRY *exec_log_entry= NULL; + char tmp_path[FN_LEN]; + char path[FN_LEN]; + uint next_entry= 0; + DBUG_ENTER("write_log_add_change_partition"); + + build_table_filename(path, sizeof(path), lpt->db, + lpt->table_name, ""); + build_table_filename(tmp_path, sizeof(tmp_path), lpt->db, + lpt->table_name, "#"); + lock_global_ddl_log(); + if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path, + FALSE)) + goto error; + if (write_log_replace_delete_frm(lpt, next_entry, NULL, tmp_path, + FALSE)) + goto error; + log_entry= part_info->first_log_entry; + if (write_execute_ddl_log_entry(log_entry->entry_pos, + FALSE, &exec_log_entry)) + goto error; + unlock_global_ddl_log(); + set_part_info_exec_log_entry(part_info, exec_log_entry); + DBUG_RETURN(FALSE); + +error: + release_part_info_log_entries(part_info->first_log_entry); + unlock_global_ddl_log(); + part_info->first_log_entry= NULL; + my_error(ER_DDL_LOG_ERROR, MYF(0)); + DBUG_RETURN(TRUE); +} + + +/* + Write description of how to complete the operation after first phase of + change partitions. + + SYNOPSIS + write_log_final_change_partition() + lpt Struct containing parameters + RETURN VALUES + TRUE Error + FALSE Success + DESCRIPTION + We will write log entries that specify to remove all partitions reorganised, + to rename others to reflect the new naming scheme and to install the shadow + frm file. +*/ + +static bool write_log_final_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt) +{ + DDL_LOG_ENTRY ddl_log_entry; + partition_info *part_info= lpt->part_info; + DDL_LOG_MEMORY_ENTRY *log_entry; + DDL_LOG_MEMORY_ENTRY *exec_log_entry= part_info->exec_log_entry; + char path[FN_LEN]; + char shadow_path[FN_LEN]; + DDL_LOG_MEMORY_ENTRY *old_first_log_entry= part_info->first_log_entry; + uint next_entry= 0; + DBUG_ENTER("write_log_final_change_partition"); + + part_info->first_log_entry= NULL; + build_table_filename(path, sizeof(path), lpt->db, + lpt->table_name, ""); + build_table_filename(shadow_path, sizeof(shadow_path), lpt->db, + lpt->table_name, "#"); + lock_global_ddl_log(); + if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path, + TRUE)) + goto error; + if (write_log_changed_partitions(lpt, &next_entry, (const char*)path)) + goto error; + if (write_log_replace_delete_frm(lpt, 0UL, path, shadow_path, FALSE)) + goto error; + log_entry= part_info->first_log_entry; + part_info->frm_log_entry= log_entry; + if (write_execute_ddl_log_entry(log_entry->entry_pos, + FALSE, &exec_log_entry)) + goto error; + release_part_info_log_entries(old_first_log_entry); + unlock_global_ddl_log(); + DBUG_RETURN(FALSE); + +error: + release_part_info_log_entries(part_info->first_log_entry); + unlock_global_ddl_log(); + part_info->first_log_entry= old_first_log_entry; + part_info->frm_log_entry= NULL; + my_error(ER_DDL_LOG_ERROR, MYF(0)); + DBUG_RETURN(TRUE); +} + + +/* + Remove entry from ddl log and release resources for others to use + + SYNOPSIS + write_log_completed() + lpt Struct containing parameters + RETURN VALUES + TRUE Error + FALSE Success +*/ + +static void write_log_completed(ALTER_PARTITION_PARAM_TYPE *lpt, + bool dont_crash) +{ + partition_info *part_info= lpt->part_info; + uint count_loop= 0; + bool not_success; + DDL_LOG_MEMORY_ENTRY *log_entry= part_info->exec_log_entry; + DBUG_ENTER("write_log_completed"); + + DBUG_ASSERT(log_entry); + lock_global_ddl_log(); + if (write_execute_ddl_log_entry(0UL, TRUE, &log_entry)) + { + /* + Failed to write, Bad... + We have completed the operation but have log records to REMOVE + stuff that shouldn't be removed. What clever things could one do + here? An error output was written to the error output by the + above method so we don't do anything here. + */ + ; + } + release_part_info_log_entries(part_info->first_log_entry); + release_part_info_log_entries(part_info->exec_log_entry); + unlock_global_ddl_log(); + part_info->exec_log_entry= NULL; + part_info->first_log_entry= NULL; + DBUG_VOID_RETURN; +} + + +/* + Release all log entries + SYNOPSIS + release_log_entries() + part_info Partition info struct + RETURN VALUES + NONE +*/ + +static void release_log_entries(partition_info *part_info) +{ + lock_global_ddl_log(); + release_part_info_log_entries(part_info->first_log_entry); + release_part_info_log_entries(part_info->exec_log_entry); + unlock_global_ddl_log(); + part_info->first_log_entry= NULL; + part_info->exec_log_entry= NULL; +} + + +/* + Handle errors for ALTER TABLE for partitioning + SYNOPSIS + handle_alter_part_error() + lpt Struct carrying parameters + not_completed Was request in complete phase when error occurred + RETURN VALUES + NONE +*/ + +void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt, + bool not_completed, + bool drop_partition, + bool frm_install) +{ + partition_info *part_info= lpt->part_info; + DBUG_ENTER("handle_alter_part_error"); + + if (!part_info->first_log_entry && + execute_ddl_log_entry(part_info->first_log_entry->entry_pos)) + { + /* + We couldn't recover from error, most likely manual interaction + is required. + */ + write_log_completed(lpt, FALSE); + release_log_entries(part_info); + if (not_completed) + { + if (drop_partition) + { + /* Table is still ok, but we left a shadow frm file behind. */ + push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1, + "%s %s", + "Operation was unsuccessful, table is still intact,", + "but it is possible that a shadow frm file was left behind"); + } + else + { + push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1, + "%s %s %s %s", + "Operation was unsuccessful, table is still intact,", + "but it is possible that a shadow frm file was left behind.", + "It is also possible that temporary partitions are left behind,", + "these could be empty or more or less filled with records"); + } + } + else + { + if (frm_install) + { + /* + Failed during install of shadow frm file, table isn't intact + and dropped partitions are still there + */ + push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1, + "%s %s %s", + "Failed during alter of partitions, table is no longer intact.", + "The frm file is in an unknown state, and a backup", + "is required."); + } + else if (drop_partition) + { + /* + Table is ok, we have switched to new table but left dropped + partitions still in their places. We remove the log records and + ask the user to perform the action manually. We remove the log + records and ask the user to perform the action manually. + */ + push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1, + "%s %s", + "Failed during drop of partitions, table is intact.", + "Manual drop of remaining partitions is required"); + } + else + { + /* + We failed during renaming of partitions. The table is most + certainly in a very bad state so we give user warning and disable + the table by writing an ancient frm version into it. + */ + push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1, + "%s %s %s", + "Failed during renaming of partitions. We are now in a position", + "where table is not reusable", + "Table is disabled by writing ancient frm file version into it"); + } + } + } + else + { + release_log_entries(part_info); + if (not_completed) + { + /* + We hit an error before things were completed but managed + to recover from the error. An error occurred and we have + restored things to original so no need for further action. + */ + ; + } + else + { + /* + We hit an error after we had completed most of the operation + and were successful in a second attempt so the operation + actually is successful now. We need to issue a warning that + even though we reported an error the operation was successfully + completed. + */ + push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,"%s %s", + "Operation was successfully completed by failure handling,", + "after failure of normal operation"); + } + } + DBUG_VOID_RETURN; +} + + +/* Actually perform the change requested by ALTER TABLE of partitions previously prepared. @@ -4777,9 +5483,12 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, ALTER_PARTITION_PARAM_TYPE lpt_obj; ALTER_PARTITION_PARAM_TYPE *lpt= &lpt_obj; bool written_bin_log= TRUE; + bool not_completed= TRUE; + bool frm_install= FALSE; DBUG_ENTER("fast_alter_partition_table"); lpt->thd= thd; + lpt->part_info= part_info; lpt->create_info= create_info; lpt->create_list= create_list; lpt->key_list= key_list; @@ -4811,17 +5520,18 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, In this case it is enough to call optimise_partitions, there is no need to change frm files or anything else. */ + int error; written_bin_log= FALSE; if (((alter_info->flags & ALTER_OPTIMIZE_PARTITION) && - (table->file->optimize_partitions(thd))) || + (error= table->file->optimize_partitions(thd))) || ((alter_info->flags & ALTER_ANALYZE_PARTITION) && - (table->file->analyze_partitions(thd))) || + (error= table->file->analyze_partitions(thd))) || ((alter_info->flags & ALTER_CHECK_PARTITION) && - (table->file->check_partitions(thd))) || + (error= table->file->check_partitions(thd))) || ((alter_info->flags & ALTER_REPAIR_PARTITION) && - (table->file->repair_partitions(thd)))) + (error= table->file->repair_partitions(thd)))) { - fast_alter_partition_error_handler(lpt); + table->file->print_error(error, MYF(0)); DBUG_RETURN(TRUE); } } @@ -4866,10 +5576,9 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, 1) Write the new frm, pack it and then delete it 2) Perform the change within the handler */ - if ((mysql_write_frm(lpt, WFRM_INITIAL_WRITE | WFRM_PACK_FRM)) || - (mysql_change_partitions(lpt))) + if (mysql_write_frm(lpt, WFRM_WRITE_SHADOW | WFRM_PACK_FRM) || + mysql_change_partitions(lpt)) { - fast_alter_partition_error_handler(lpt); DBUG_RETURN(TRUE); } } @@ -4897,32 +5606,62 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, after a DROP PARTITION) if one ensured that failed accesses to the dropped partitions was aborted for sure (thus only possible for transactional engines). - - 1) Lock the table in TL_WRITE_ONLY to ensure all other accesses to + + 0) Write an entry that removes the shadow frm file if crash occurs + 1) Write the new frm file as a shadow frm + 2) Write the ddl log to ensure that the operation is completed + even in the presence of a MySQL Server crash + 3) Lock the table in TL_WRITE_ONLY to ensure all other accesses to the table have completed - 2) Write the new frm file where the partitions have changed but are - still remaining with the state PART_TO_BE_DROPPED - 3) Write the bin log - 4) Prepare MyISAM handlers for drop of partitions - 5) Ensure that any users that has opened the table but not yet + 4) Write the bin log + Unfortunately the writing of the binlog is not synchronised with + other logging activities. So no matter in which order the binlog + is written compared to other activities there will always be cases + where crashes make strange things occur. In this placement it can + happen that the ALTER TABLE DROP PARTITION gets performed in the + master but not in the slaves if we have a crash, after writing the + ddl log but before writing the binlog. A solution to this would + require writing the statement first in the ddl log and then + when recovering from the crash read the binlog and insert it into + the binlog if not written already. + 5) Install the previously written shadow frm file + 6) Ensure that any users that has opened the table but not yet reached the abort lock do that before downgrading the lock. - 6) Drop the partitions - 7) Write the frm file that the partition has been dropped - 8) Wait until all accesses using the old frm file has completed - 9) Complete query + 7) Prepare MyISAM handlers for drop of partitions + 8) Drop the partitions + 9) Remove entries from ddl log + 10) Wait until all accesses using the old frm file has completed + 11) Complete query + + We insert Error injections at all places where it could be interesting + to test if recovery is properly done. */ - if ((abort_and_upgrade_lock(lpt)) || - (mysql_write_frm(lpt, WFRM_INITIAL_WRITE)) || + if (write_log_drop_shadow_frm(lpt) || + ERROR_INJECT_CRASH("crash_drop_partition_1") || + mysql_write_frm(lpt, WFRM_WRITE_SHADOW) || + ERROR_INJECT_CRASH("crash_drop_partition_2") || + write_log_drop_partition(lpt) || + ERROR_INJECT_CRASH("crash_drop_partition_3") || + ((not_completed= FALSE), FALSE) || + (abort_and_upgrade_lock(lpt), FALSE) || ((!thd->lex->no_write_to_binlog) && (write_bin_log(thd, FALSE, - thd->query, thd->query_length), FALSE)) || - (table->file->extra(HA_EXTRA_PREPARE_FOR_DELETE)) || + thd->query, thd->query_length), FALSE)) || + ERROR_INJECT_CRASH("crash_drop_partition_4") || + (table->file->extra(HA_EXTRA_PREPARE_FOR_DELETE), FALSE) || + ERROR_INJECT_CRASH("crash_drop_partition_5") || + ((frm_install= TRUE), FALSE) || + mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) || + ((frm_install= FALSE), FALSE) || (close_open_tables_and_downgrade(lpt), FALSE) || - (mysql_drop_partitions(lpt)) || - (mysql_write_frm(lpt, WFRM_CREATE_HANDLER_FILES)) || + ERROR_INJECT_CRASH("crash_drop_partition_6") || + mysql_drop_partitions(lpt) || + ERROR_INJECT_CRASH("crash_drop_partition_7") || + (write_log_completed(lpt, FALSE), FALSE) || + ERROR_INJECT_CRASH("crash_drop_partition_8") || (mysql_wait_completed_table(lpt, table), FALSE)) { - fast_alter_partition_error_handler(lpt); + handle_alter_part_error(lpt, not_completed, TRUE, frm_install); DBUG_RETURN(TRUE); } } @@ -4939,28 +5678,45 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, miss updates made by a transaction serialised before it that are inserted into the new partition. - 1) Write the new frm file where state of added partitions is - changed to PART_TO_BE_ADDED + 0) Write an entry that removes the shadow frm file if crash occurs + 1) Write the new frm file as a shadow frm file + 2) Log the changes to happen in ddl log 2) Add the new partitions 3) Lock all partitions in TL_WRITE_ONLY to ensure that no users are still using the old partitioning scheme. Wait until all ongoing users have completed before progressing. - 4) Write a new frm file of the table where the partitions are added - to the table. - 5) Write binlog - 6) Wait until all accesses using the old frm file has completed - 7) Complete query + 4) Write binlog + 5) Now the change is completed except for the installation of the + new frm file. We thus write an action in the log to change to + the shadow frm file + 6) Install the new frm file of the table where the partitions are + added to the table. + 7) Wait until all accesses using the old frm file has completed + 8) Remove entries from ddl log + 9) Complete query */ - if ((mysql_write_frm(lpt, WFRM_INITIAL_WRITE)) || - (mysql_change_partitions(lpt)) || - (abort_and_upgrade_lock(lpt)) || - (mysql_write_frm(lpt, WFRM_CREATE_HANDLER_FILES)) || + if (write_log_add_change_partition(lpt) || + ERROR_INJECT_CRASH("crash_add_partition_1") || + mysql_write_frm(lpt, WFRM_WRITE_SHADOW) || + ERROR_INJECT_CRASH("crash_add_partition_2") || + mysql_change_partitions(lpt) || + ERROR_INJECT_CRASH("crash_add_partition_3") || + (abort_and_upgrade_lock(lpt), FALSE) || ((!thd->lex->no_write_to_binlog) && (write_bin_log(thd, FALSE, thd->query, thd->query_length), FALSE)) || - (close_open_tables_and_downgrade(lpt), FALSE)) + ERROR_INJECT_CRASH("crash_add_partition_4") || + write_log_rename_frm(lpt) || + ((not_completed= FALSE), FALSE) || + ERROR_INJECT_CRASH("crash_add_partition_5") || + ((frm_install= TRUE), FALSE) || + mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) || + ERROR_INJECT_CRASH("crash_add_partition_6") || + (close_open_tables_and_downgrade(lpt), FALSE) || + (write_log_completed(lpt, FALSE), FALSE) || + ERROR_INJECT_CRASH("crash_add_partition_7")) { - fast_alter_partition_error_handler(lpt); + handle_alter_part_error(lpt, not_completed, FALSE, frm_install); DBUG_RETURN(TRUE); } } @@ -4997,44 +5753,57 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, use a lower lock level. This can be handled inside store_lock in the respective handler. - 1) Write the new frm file where state of added partitions is - changed to PART_TO_BE_ADDED and the reorganised partitions - are set in state PART_TO_BE_REORGED. - 2) Add the new partitions + 0) Write an entry that removes the shadow frm file if crash occurs + 1) Write the shadow frm file of new partitioning + 2) Log such that temporary partitions added in change phase are + removed in a crash situation + 3) Add the new partitions Copy from the reorganised partitions to the new partitions - 3) Lock all partitions in TL_WRITE_ONLY to ensure that no users + 4) Log that operation is completed and log all complete actions + needed to complete operation from here + 5) Lock all partitions in TL_WRITE_ONLY to ensure that no users are still using the old partitioning scheme. Wait until all ongoing users have completed before progressing. - 4) Prepare MyISAM handlers for rename and delete of partitions - 5) Write a new frm file of the table where the partitions are - reorganised. - 6) Rename the reorged partitions such that they are no longer + 6) Prepare MyISAM handlers for rename and delete of partitions + 7) Rename the reorged partitions such that they are no longer used and rename those added to their real new names. - 7) Write bin log - 8) Wait until all accesses using the old frm file has completed - 9) Drop the reorganised partitions - 10)Write a new frm file of the table where the partitions are - reorganised. - 11)Wait until all accesses using the old frm file has completed - 12)Complete query + 8) Write bin log + 9) Install the shadow frm file + 10) Wait until all accesses using the old frm file has completed + 11) Drop the reorganised partitions + 12) Remove log entry + 13)Wait until all accesses using the old frm file has completed + 14)Complete query */ - - if ((mysql_write_frm(lpt, WFRM_INITIAL_WRITE)) || - (mysql_change_partitions(lpt)) || - (abort_and_upgrade_lock(lpt)) || - (mysql_write_frm(lpt, WFRM_CREATE_HANDLER_FILES)) || - (table->file->extra(HA_EXTRA_PREPARE_FOR_DELETE)) || - (mysql_rename_partitions(lpt)) || + if (write_log_add_change_partition(lpt) || + ERROR_INJECT_CRASH("crash_change_partition_1") || + mysql_write_frm(lpt, WFRM_WRITE_SHADOW) || + ERROR_INJECT_CRASH("crash_change_partition_2") || + mysql_change_partitions(lpt) || + ERROR_INJECT_CRASH("crash_change_partition_3") || + write_log_final_change_partition(lpt) || + ERROR_INJECT_CRASH("crash_change_partition_4") || + ((not_completed= FALSE), FALSE) || + (abort_and_upgrade_lock(lpt), FALSE) || ((!thd->lex->no_write_to_binlog) && (write_bin_log(thd, FALSE, thd->query, thd->query_length), FALSE)) || + ERROR_INJECT_CRASH("crash_change_partition_5") || + (table->file->extra(HA_EXTRA_PREPARE_FOR_DELETE), FALSE) || + ERROR_INJECT_CRASH("crash_change_partition_6") || + mysql_rename_partitions(lpt) || + ((frm_install= TRUE), FALSE) || + ERROR_INJECT_CRASH("crash_change_partition_7") || + mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) || + ERROR_INJECT_CRASH("crash_change_partition_8") || (close_open_tables_and_downgrade(lpt), FALSE) || - (mysql_drop_partitions(lpt)) || - (mysql_write_frm(lpt, 0UL)) || + ERROR_INJECT_CRASH("crash_change_partition_9") || + (write_log_completed(lpt, FALSE), FALSE) || + ERROR_INJECT_CRASH("crash_change_partition_10") || (mysql_wait_completed_table(lpt, table), FALSE)) { - fast_alter_partition_error_handler(lpt); - DBUG_RETURN(TRUE); + handle_alter_part_error(lpt, not_completed, FALSE, frm_install); + DBUG_RETURN(TRUE); } } /* @@ -5042,7 +5811,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, user */ DBUG_RETURN(fast_end_partition(thd, lpt->copied, lpt->deleted, - table_list, FALSE, lpt, + table, table_list, FALSE, lpt, written_bin_log)); } #endif @@ -5665,5 +6434,87 @@ static uint32 get_next_subpartition_via_walking(PARTITION_ITERATOR *part_iter) part_iter->field_vals.start++; return part_iter->part_info->get_subpartition_id(part_iter->part_info); } + + +/* + Create partition names + + SYNOPSIS + create_partition_name() + out:out Created partition name string + in1 First part + in2 Second part + name_variant Normal, temporary or renamed partition name + + RETURN VALUE + NONE + + DESCRIPTION + This method is used to calculate the partition name, service routine to + the del_ren_cre_table method. +*/ + +void +create_partition_name(char *out, const char *in1, + const char *in2, uint name_variant, + bool translate) +{ + char transl_part_name[FN_REFLEN]; + const char *transl_part; + + if (translate) + { + tablename_to_filename(in2, transl_part_name, FN_REFLEN); + transl_part= transl_part_name; + } + else + transl_part= in2; + if (name_variant == NORMAL_PART_NAME) + strxmov(out, in1, "#P#", transl_part, NullS); + else if (name_variant == TEMP_PART_NAME) + strxmov(out, in1, "#P#", transl_part, "#TMP#", NullS); + else if (name_variant == RENAMED_PART_NAME) + strxmov(out, in1, "#P#", transl_part, "#REN#", NullS); +} + + +/* + Create subpartition name + + SYNOPSIS + create_subpartition_name() + out:out Created partition name string + in1 First part + in2 Second part + in3 Third part + name_variant Normal, temporary or renamed partition name + + RETURN VALUE + NONE + + DESCRIPTION + This method is used to calculate the subpartition name, service routine to + the del_ren_cre_table method. +*/ + +void +create_subpartition_name(char *out, const char *in1, + const char *in2, const char *in3, + uint name_variant) +{ + char transl_part_name[FN_REFLEN], transl_subpart_name[FN_REFLEN]; + + tablename_to_filename(in2, transl_part_name, FN_REFLEN); + tablename_to_filename(in3, transl_subpart_name, FN_REFLEN); + if (name_variant == NORMAL_PART_NAME) + strxmov(out, in1, "#P#", transl_part_name, + "#SP#", transl_subpart_name, NullS); + else if (name_variant == TEMP_PART_NAME) + strxmov(out, in1, "#P#", transl_part_name, + "#SP#", transl_subpart_name, "#TMP#", NullS); + else if (name_variant == RENAMED_PART_NAME) + strxmov(out, in1, "#P#", transl_part_name, + "#SP#", transl_subpart_name, "#REN#", NullS); +} #endif diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 633558de914..90251184120 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -241,6 +241,929 @@ static int mysql_copy_key_list(List<Key> *orig_key, DBUG_RETURN(FALSE); } +/* +-------------------------------------------------------------------------- + + MODULE: DDL log + ----------------- + + This module is used to ensure that we can recover from crashes that occur + in the middle of a meta-data operation in MySQL. E.g. DROP TABLE t1, t2; + We need to ensure that both t1 and t2 are dropped and not only t1 and + also that each table drop is entirely done and not "half-baked". + + To support this we create log entries for each meta-data statement in the + ddl log while we are executing. These entries are dropped when the + operation is completed. + + At recovery those entries that were not completed will be executed. + + There is only one ddl log in the system and it is protected by a mutex + and there is a global struct that contains information about its current + state. + + History: + First version written in 2006 by Mikael Ronstrom +-------------------------------------------------------------------------- +*/ + + +typedef struct st_global_ddl_log +{ + char file_entry_buf[4*IO_SIZE]; + char file_name_str[FN_REFLEN]; + char *file_name; + DDL_LOG_MEMORY_ENTRY *first_free; + DDL_LOG_MEMORY_ENTRY *first_used; + uint num_entries; + File file_id; + uint name_len; + uint handler_name_len; + uint io_size; + bool inited; + bool recovery_phase; +} GLOBAL_DDL_LOG; + +GLOBAL_DDL_LOG global_ddl_log; + +pthread_mutex_t LOCK_gdl; + +#define DDL_LOG_ENTRY_TYPE_POS 0 +#define DDL_LOG_ACTION_TYPE_POS 1 +#define DDL_LOG_PHASE_POS 2 +#define DDL_LOG_NEXT_ENTRY_POS 4 +#define DDL_LOG_NAME_POS 8 + +#define DDL_LOG_NUM_ENTRY_POS 0 +#define DDL_LOG_NAME_LEN_POS 4 +#define DDL_LOG_HANDLER_TYPE_POS 8 +#define DDL_LOG_IO_SIZE_POS 12 + +/* + Read one entry from ddl log file + SYNOPSIS + read_ddl_log_file_entry() + entry_no Entry number to read + RETURN VALUES + TRUE Error + FALSE Success +*/ + +static bool read_ddl_log_file_entry(uint entry_no) +{ + bool error= FALSE; + File file_id= global_ddl_log.file_id; + char *file_entry_buf= (char*)global_ddl_log.file_entry_buf; + uint io_size= global_ddl_log.io_size; + DBUG_ENTER("read_ddl_log_file_entry"); + + if (my_pread(file_id, file_entry_buf, io_size, io_size * entry_no, + MYF(MY_WME)) != io_size) + error= TRUE; + DBUG_RETURN(error); +} + + +/* + Write one entry from ddl log file + SYNOPSIS + write_ddl_log_file_entry() + entry_no Entry number to read + RETURN VALUES + TRUE Error + FALSE Success +*/ + +static bool write_ddl_log_file_entry(uint entry_no) +{ + bool error= FALSE; + File file_id= global_ddl_log.file_id; + char *file_entry_buf= (char*)global_ddl_log.file_entry_buf; + DBUG_ENTER("write_ddl_log_file_entry"); + + if (my_pwrite(file_id, file_entry_buf, + IO_SIZE, IO_SIZE * entry_no, MYF(MY_WME)) != IO_SIZE) + error= TRUE; + DBUG_RETURN(error); +} + + +/* + Write ddl log header + SYNOPSIS + write_ddl_log_header() + RETURN VALUES + TRUE Error + FALSE Success +*/ + +static bool write_ddl_log_header() +{ + uint16 const_var; + bool error= FALSE; + DBUG_ENTER("write_ddl_log_header"); + + int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NUM_ENTRY_POS], + global_ddl_log.num_entries); + const_var= FN_LEN; + int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_LEN_POS], + const_var); + const_var= DDL_LOG_HANDLER_TYPE_LEN; + int4store(&global_ddl_log.file_entry_buf[DDL_LOG_HANDLER_TYPE_POS], + const_var); + const_var= IO_SIZE; + int4store(&global_ddl_log.file_entry_buf[DDL_LOG_IO_SIZE_POS], + const_var); + if (write_ddl_log_file_entry(0UL)) + { + sql_print_error("Error writing ddl log header"); + DBUG_RETURN(TRUE); + } + VOID(sync_ddl_log()); + DBUG_RETURN(error); +} + + +/* + Create ddl log file name + SYNOPSIS + create_ddl_log_file_name() + file_name Filename setup + RETURN VALUES + NONE +*/ + +static inline void create_ddl_log_file_name(char *file_name) +{ + strxmov(file_name, mysql_data_home, "/", "ddl_log.log", NullS); +} + + +/* + Read header of ddl log file + SYNOPSIS + read_ddl_log_header() + RETURN VALUES + > 0 Last entry in ddl log + 0 No entries in ddl log + DESCRIPTION + When we read the ddl log header we get information about maximum sizes + of names in the ddl log and we also get information about the number + of entries in the ddl log. +*/ + +static uint read_ddl_log_header() +{ + char *file_entry_buf= (char*)global_ddl_log.file_entry_buf; + char file_name[FN_REFLEN]; + uint entry_no; + bool successful_open= FALSE; + DBUG_ENTER("read_ddl_log_header"); + + bzero(file_entry_buf, sizeof(global_ddl_log.file_entry_buf)); + global_ddl_log.inited= FALSE; + global_ddl_log.recovery_phase= TRUE; + create_ddl_log_file_name(file_name); + if (!(my_open(file_name, O_RDWR | O_BINARY, MYF(MY_WME)))) + { + if (read_ddl_log_file_entry(0UL)) + { + /* Write message into error log */ + sql_print_error("Failed to read ddl log file in recovery"); + } + else + successful_open= TRUE; + } + else + sql_print_error("Failed to open ddl log file in recovery"); + entry_no= uint4korr(&file_entry_buf[DDL_LOG_NUM_ENTRY_POS]); + global_ddl_log.name_len= uint4korr(&file_entry_buf[DDL_LOG_NAME_LEN_POS]); + global_ddl_log.handler_name_len= + uint4korr(&file_entry_buf[DDL_LOG_HANDLER_TYPE_POS]); + if (successful_open) + global_ddl_log.io_size= uint4korr(&file_entry_buf[DDL_LOG_IO_SIZE_POS]); + else + { + global_ddl_log.io_size= IO_SIZE; + entry_no= 0; + } + global_ddl_log.first_free= NULL; + global_ddl_log.first_used= NULL; + global_ddl_log.num_entries= 0; + VOID(pthread_mutex_init(&LOCK_gdl, MY_MUTEX_INIT_FAST)); + DBUG_RETURN(entry_no); +} + + +/* + Read a ddl log entry + SYNOPSIS + read_ddl_log_entry() + read_entry Number of entry to read + out:entry_info Information from entry + RETURN VALUES + TRUE Error + FALSE Success + DESCRIPTION + Read a specified entry in the ddl log +*/ + +bool read_ddl_log_entry(uint read_entry, DDL_LOG_ENTRY *ddl_log_entry) +{ + char *file_entry_buf= (char*)&global_ddl_log.file_entry_buf; + uint inx; + DBUG_ENTER("read_ddl_log_entry"); + + if (read_ddl_log_file_entry(read_entry)) + { + DBUG_RETURN(TRUE); + } + ddl_log_entry->entry_pos= read_entry; + ddl_log_entry->entry_type= + (enum ddl_log_entry_code)file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]; + ddl_log_entry->action_type= + (enum ddl_log_action_code)file_entry_buf[DDL_LOG_ACTION_TYPE_POS]; + ddl_log_entry->phase= file_entry_buf[DDL_LOG_PHASE_POS]; + ddl_log_entry->next_entry= uint4korr(&file_entry_buf[DDL_LOG_NEXT_ENTRY_POS]); + ddl_log_entry->name= &file_entry_buf[DDL_LOG_NAME_POS]; + inx= DDL_LOG_NAME_POS + global_ddl_log.name_len; + ddl_log_entry->from_name= &file_entry_buf[inx]; + inx+= global_ddl_log.name_len; + ddl_log_entry->handler_name= &file_entry_buf[inx]; + DBUG_RETURN(FALSE); +} + + +/* + Initialise ddl log + SYNOPSIS + init_ddl_log() + RETURN VALUES + TRUE Error + FALSE Success + DESCRIPTION + Write the header of the ddl log file and length of names. Also set + number of entries to zero. +*/ + +static bool init_ddl_log() +{ + bool error= FALSE; + char file_name[FN_REFLEN]; + DBUG_ENTER("init_ddl_log"); + + if (global_ddl_log.inited) + { + DBUG_RETURN(FALSE); + } + global_ddl_log.io_size= IO_SIZE; + if ((global_ddl_log.file_id= my_create(file_name, + CREATE_MODE, + O_RDWR | O_TRUNC | O_BINARY, + MYF(MY_WME))) < 0) + { + /* Couldn't create ddl log file, this is serious error */ + sql_print_error("Failed to open ddl log file"); + DBUG_RETURN(TRUE); + } + if (write_ddl_log_header()) + { + DBUG_RETURN(TRUE); + } + global_ddl_log.inited= TRUE; + DBUG_RETURN(FALSE); +} + + +/* + Execute one action in a ddl log entry + SYNOPSIS + execute_ddl_log_action() + ddl_log_entry Information in action entry to execute + RETURN VALUES + TRUE Error + FALSE Success +*/ + +static bool execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry) +{ + bool frm_action= FALSE; + LEX_STRING handler_name; + handler *file; + MEM_ROOT mem_root; + bool error= TRUE; + char path[FN_REFLEN]; + char from_path[FN_REFLEN]; + char *par_ext= (char*)".par"; + handlerton *hton; + DBUG_ENTER("execute_ddl_log_action"); + + if (ddl_log_entry->entry_type == DDL_IGNORE_LOG_ENTRY_CODE) + { + DBUG_RETURN(FALSE); + } + handler_name.str= (char*)ddl_log_entry->handler_name; + handler_name.length= strlen(ddl_log_entry->handler_name); + hton= ha_resolve_by_name(thd, &handler_name); + if (!hton) + { + my_error(ER_ILLEGAL_HA, MYF(0), ddl_log_entry->handler_name); + DBUG_RETURN(TRUE); + } + init_sql_alloc(&mem_root, TABLE_ALLOC_BLOCK_SIZE, 0); + if (strcmp("frm", ddl_log_entry->handler_name)) + frm_action= TRUE; + else + { + TABLE_SHARE dummy; + bzero(&dummy, sizeof(TABLE_SHARE)); + file= get_new_handler(&dummy, &mem_root, hton); + if (!file) + { + mem_alloc_error(sizeof(handler)); + goto error; + } + } + switch (ddl_log_entry->action_type) + { + case DDL_LOG_REPLACE_ACTION: + case DDL_LOG_DELETE_ACTION: + { + if (ddl_log_entry->phase == 0) + { + if (frm_action) + { + strxmov(path, ddl_log_entry->name, reg_ext, NullS); + if (my_delete(path, MYF(MY_WME))) + break; +#ifdef WITH_PARTITION_STORAGE_ENGINE + strxmov(path, ddl_log_entry->name, par_ext, NullS); + VOID(my_delete(path, MYF(MY_WME))); +#endif + } + else + { + if (file->delete_table(ddl_log_entry->name)) + break; + } + if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos))) + { + VOID(sync_ddl_log()); + error= FALSE; + } + if (ddl_log_entry->action_type == DDL_LOG_DELETE_ACTION) + break; + } + DBUG_ASSERT(ddl_log_entry->action_type == DDL_LOG_REPLACE_ACTION); + /* + Fall through and perform the rename action of the replace + action. We have already indicated the success of the delete + action in the log entry by stepping up the phase. + */ + } + case DDL_LOG_RENAME_ACTION: + { + error= TRUE; + if (frm_action) + { + strxmov(path, ddl_log_entry->name, reg_ext, NullS); + strxmov(from_path, ddl_log_entry->from_name, reg_ext, NullS); + if (my_rename(path, from_path, MYF(MY_WME))) + break; +#ifdef WITH_PARTITION_STORAGE_ENGINE + strxmov(path, ddl_log_entry->name, par_ext, NullS); + strxmov(from_path, ddl_log_entry->from_name, par_ext, NullS); + VOID(my_rename(path, from_path, MYF(MY_WME))); +#endif + } + else + { + if (file->rename_table(ddl_log_entry->name, + ddl_log_entry->from_name)) + break; + } + if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos))) + { + VOID(sync_ddl_log()); + error= FALSE; + } + break; + } + default: + DBUG_ASSERT(0); + break; + } + delete file; +error: + free_root(&mem_root, MYF(0)); + DBUG_RETURN(error); +} + + +/* + Get a free entry in the ddl log + SYNOPSIS + get_free_ddl_log_entry() + out:active_entry A ddl log memory entry returned + RETURN VALUES + TRUE Error + FALSE Success +*/ + +static bool get_free_ddl_log_entry(DDL_LOG_MEMORY_ENTRY **active_entry, + bool *write_header) +{ + DDL_LOG_MEMORY_ENTRY *used_entry; + DDL_LOG_MEMORY_ENTRY *first_used= global_ddl_log.first_used; + DBUG_ENTER("get_free_ddl_log_entry"); + + if (global_ddl_log.first_free == NULL) + { + if (!(used_entry= (DDL_LOG_MEMORY_ENTRY*)my_malloc( + sizeof(DDL_LOG_MEMORY_ENTRY), MYF(MY_WME)))) + { + sql_print_error("Failed to allocate memory for ddl log free list"); + DBUG_RETURN(TRUE); + } + global_ddl_log.num_entries++; + used_entry->entry_pos= global_ddl_log.num_entries; + *write_header= TRUE; + } + else + { + used_entry= global_ddl_log.first_free; + global_ddl_log.first_free= used_entry->next_log_entry; + *write_header= FALSE; + } + /* + Move from free list to used list + */ + used_entry->next_log_entry= first_used; + used_entry->prev_log_entry= NULL; + global_ddl_log.first_used= used_entry; + if (first_used) + first_used->prev_log_entry= used_entry; + + *active_entry= used_entry; + DBUG_RETURN(FALSE); +} + + +/* + External interface methods for the DDL log Module + --------------------------------------------------- +*/ + +/* + SYNOPSIS + write_ddl_log_entry() + ddl_log_entry Information about log entry + out:entry_written Entry information written into + + RETURN VALUES + TRUE Error + FALSE Success + + DESCRIPTION + A careful write of the ddl log is performed to ensure that we can + handle crashes occurring during CREATE and ALTER TABLE processing. +*/ + +bool write_ddl_log_entry(DDL_LOG_ENTRY *ddl_log_entry, + DDL_LOG_MEMORY_ENTRY **active_entry) +{ + bool error, write_header; + DBUG_ENTER("write_ddl_log_entry"); + + if (init_ddl_log()) + { + DBUG_RETURN(TRUE); + } + global_ddl_log.file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= DDL_LOG_ENTRY_CODE; + global_ddl_log.file_entry_buf[DDL_LOG_ACTION_TYPE_POS]= + ddl_log_entry->action_type; + global_ddl_log.file_entry_buf[DDL_LOG_PHASE_POS]= 0; + int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NEXT_ENTRY_POS], + ddl_log_entry->next_entry); + DBUG_ASSERT(strlen(ddl_log_entry->name) < FN_LEN); + strncpy(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS], + ddl_log_entry->name, FN_LEN); + if (ddl_log_entry->action_type == DDL_LOG_RENAME_ACTION || + ddl_log_entry->action_type == DDL_LOG_REPLACE_ACTION) + { + DBUG_ASSERT(strlen(ddl_log_entry->from_name) < FN_LEN); + strncpy(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_LEN], + ddl_log_entry->from_name, FN_LEN); + } + else + global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_LEN]= 0; + DBUG_ASSERT(strlen(ddl_log_entry->handler_name) < FN_LEN); + strncpy(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + (2*FN_LEN)], + ddl_log_entry->handler_name, FN_LEN); + if (get_free_ddl_log_entry(active_entry, &write_header)) + { + DBUG_RETURN(TRUE); + } + error= FALSE; + if (write_ddl_log_file_entry((*active_entry)->entry_pos)) + { + error= TRUE; + sql_print_error("Failed to write entry_no = %u", + (*active_entry)->entry_pos); + } + if (write_header && !error) + { + VOID(sync_ddl_log()); + if (write_ddl_log_header()) + error= TRUE; + VOID(sync_ddl_log()); + } + if (error) + release_ddl_log_memory_entry(*active_entry); + DBUG_RETURN(error); +} + + +/* + Write final entry in the ddl log + SYNOPSIS + write_execute_ddl_log_entry() + first_entry First entry in linked list of entries + to execute, if 0 = NULL it means that + the entry is removed and the entries + are put into the free list. + in:out:exec_entry Entry to execute, 0 = NULL if the entry + is written first time and needs to be + returned. In this case the entry written + is returned in this parameter + RETURN VALUES + TRUE Error + FALSE Success + + DESCRIPTION + This is the last write in the ddl log. The previous log entries have + already been written but not yet synched to disk. + We write a couple of log entries that describes action to perform. + This entries are set-up in a linked list, however only when a first + execute entry is put as the first entry these will be executed. + This routine writes this first +*/ + +bool write_execute_ddl_log_entry(uint first_entry, + bool complete, + DDL_LOG_MEMORY_ENTRY **active_entry) +{ + bool write_header= FALSE; + char *file_entry_buf= (char*)global_ddl_log.file_entry_buf; + DBUG_ENTER("write_execute_ddl_log_entry"); + + if (init_ddl_log()) + { + DBUG_RETURN(TRUE); + } + if (!complete) + { + /* + We haven't synched the log entries yet, we synch them now before + writing the execute entry. If complete is true we haven't written + any log entries before, we are only here to write the execute + entry to indicate it is done. + */ + VOID(sync_ddl_log()); + file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= DDL_LOG_EXECUTE_CODE; + } + else + file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= DDL_IGNORE_LOG_ENTRY_CODE; + file_entry_buf[DDL_LOG_ACTION_TYPE_POS]= 0; /* Ignored for execute entries */ + file_entry_buf[DDL_LOG_PHASE_POS]= 0; + int4store(&file_entry_buf[DDL_LOG_NEXT_ENTRY_POS], first_entry); + file_entry_buf[DDL_LOG_NAME_POS]= 0; + file_entry_buf[DDL_LOG_NAME_POS + FN_LEN]= 0; + file_entry_buf[DDL_LOG_NAME_POS + 2*FN_LEN]= 0; + if (!(*active_entry)) + { + if (get_free_ddl_log_entry(active_entry, &write_header)) + { + DBUG_RETURN(TRUE); + } + } + if (write_ddl_log_file_entry((*active_entry)->entry_pos)) + { + sql_print_error("Error writing execute entry in ddl log"); + release_ddl_log_memory_entry(*active_entry); + DBUG_RETURN(TRUE); + } + VOID(sync_ddl_log()); + if (write_header) + { + if (write_ddl_log_header()) + { + release_ddl_log_memory_entry(*active_entry); + DBUG_RETURN(TRUE); + } + VOID(sync_ddl_log()); + } + DBUG_RETURN(FALSE); +} + + +/* + For complex rename operations we need to deactivate individual entries. + SYNOPSIS + deactivate_ddl_log_entry() + entry_no Entry position of record to change + RETURN VALUES + TRUE Error + FALSE Success + DESCRIPTION + During replace operations where we start with an existing table called + t1 and a replacement table called t1#temp or something else and where + we want to delete t1 and rename t1#temp to t1 this is not possible to + do in a safe manner unless the ddl log is informed of the phases in + the change. + + Delete actions are 1-phase actions that can be ignored immediately after + being executed. + Rename actions from x to y is also a 1-phase action since there is no + interaction with any other handlers named x and y. + Replace action where drop y and x -> y happens needs to be a two-phase + action. Thus the first phase will drop y and the second phase will + rename x -> y. +*/ + +bool deactivate_ddl_log_entry(uint entry_no) +{ + char *file_entry_buf= (char*)global_ddl_log.file_entry_buf; + DBUG_ENTER("deactivate_ddl_log_entry"); + + if (!read_ddl_log_file_entry(entry_no)) + { + if (file_entry_buf[DDL_LOG_ENTRY_TYPE_POS] == DDL_LOG_ENTRY_CODE) + { + if (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_DELETE_ACTION || + file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_RENAME_ACTION || + (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_REPLACE_ACTION && + file_entry_buf[DDL_LOG_PHASE_POS] == 1)) + file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= DDL_IGNORE_LOG_ENTRY_CODE; + else if (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_REPLACE_ACTION) + { + DBUG_ASSERT(file_entry_buf[DDL_LOG_PHASE_POS] == 0); + file_entry_buf[DDL_LOG_PHASE_POS]= 1; + } + else + { + DBUG_ASSERT(0); + } + if (write_ddl_log_file_entry(entry_no)) + { + sql_print_error("Error in deactivating log entry. Position = %u", + entry_no); + DBUG_RETURN(TRUE); + } + } + } + else + { + sql_print_error("Failed in reading entry before deactivating it"); + DBUG_RETURN(TRUE); + } + DBUG_RETURN(FALSE); +} + + +/* + Sync ddl log file + SYNOPSIS + sync_ddl_log() + RETURN VALUES + TRUE Error + FALSE Success +*/ + +bool sync_ddl_log() +{ + bool error= FALSE; + DBUG_ENTER("sync_ddl_log"); + + if ((!global_ddl_log.recovery_phase) && + init_ddl_log()) + { + DBUG_RETURN(TRUE); + } + if (my_sync(global_ddl_log.file_id, MYF(0))) + { + /* Write to error log */ + sql_print_error("Failed to sync ddl log"); + error= TRUE; + } + DBUG_RETURN(error); +} + + +/* + Release a log memory entry + SYNOPSIS + release_ddl_log_memory_entry() + log_memory_entry Log memory entry to release + RETURN VALUES + NONE +*/ + +void release_ddl_log_memory_entry(DDL_LOG_MEMORY_ENTRY *log_entry) +{ + DDL_LOG_MEMORY_ENTRY *first_free= global_ddl_log.first_free; + DDL_LOG_MEMORY_ENTRY *next_log_entry= log_entry->next_log_entry; + DDL_LOG_MEMORY_ENTRY *prev_log_entry= log_entry->prev_log_entry; + DBUG_ENTER("release_ddl_log_memory_entry"); + + global_ddl_log.first_free= log_entry; + log_entry->next_log_entry= first_free; + + if (prev_log_entry) + prev_log_entry->next_log_entry= next_log_entry; + else + global_ddl_log.first_used= next_log_entry; + if (next_log_entry) + next_log_entry->prev_log_entry= prev_log_entry; + DBUG_VOID_RETURN; +} + + +/* + Execute one entry in the ddl log. Executing an entry means executing + a linked list of actions. + SYNOPSIS + execute_ddl_log_entry() + first_entry Reference to first action in entry + RETURN VALUES + TRUE Error + FALSE Success +*/ + +bool execute_ddl_log_entry(THD *thd, uint first_entry) +{ + DDL_LOG_ENTRY ddl_log_entry; + uint read_entry= first_entry; + DBUG_ENTER("execute_ddl_log_entry"); + + lock_global_ddl_log(); + do + { + if (read_ddl_log_entry(read_entry, &ddl_log_entry)) + { + DBUG_ASSERT(0); + /* Write to error log and continue with next log entry */ + sql_print_error("Failed to read entry = %u from ddl log", + read_entry); + break; + } + DBUG_ASSERT(ddl_log_entry.entry_type == DDL_LOG_ENTRY_CODE || + ddl_log_entry.entry_type == DDL_IGNORE_LOG_ENTRY_CODE); + + if (execute_ddl_log_action(thd, &ddl_log_entry)) + { + DBUG_ASSERT(0); + /* Write to error log and continue with next log entry */ + sql_print_error("Failed to execute action for entry = %u from ddl log", + read_entry); + break; + } + read_entry= ddl_log_entry.next_entry; + } while (read_entry); + unlock_global_ddl_log(); + DBUG_RETURN(FALSE); +} + + +/* + Execute the ddl log at recovery of MySQL Server + SYNOPSIS + execute_ddl_log_recovery() + RETURN VALUES + NONE +*/ + +void execute_ddl_log_recovery() +{ + uint num_entries, i; + DDL_LOG_ENTRY ddl_log_entry; + DBUG_ENTER("execute_ddl_log_recovery"); + + /* + To be able to run this from boot, we allocate a temporary THD + */ + if (!(thd=new THD)) + DBUG_VOID_RETURN; + thd->thread_stack= (char*) &thd; + thd->store_globals(); + + num_entries= read_ddl_log_header(); + for (i= 0; i < num_entries; i++) + { + if (read_ddl_log_entry(i, &ddl_log_entry)) + { + DBUG_ASSERT(0); + sql_print_error("Failed to read entry no = %u from ddl log", + i); + continue; + } + if (ddl_log_entry.entry_type == DDL_LOG_EXECUTE_CODE) + { + if (execute_ddl_log_entry(ddl_log_entry.next_entry)) + { + /* Real unpleasant scenario but we continue anyways. */ + DBUG_ASSERT(0); + continue; + } + } + } + create_ddl_log_file_name(file_name); + VOID(my_delete(file_name, MYF(0))); + global_ddl_log.recovery_phase= FALSE; + delete thd; + /* Remember that we don't have a THD */ + my_pthread_setspecific_ptr(THR_THD, 0); + DBUG_VOID_RETURN; +} + + +/* + Release all memory allocated to the ddl log + SYNOPSIS + release_ddl_log() + RETURN VALUES + NONE +*/ + +void release_ddl_log() +{ + DDL_LOG_MEMORY_ENTRY *free_list= global_ddl_log.first_free; + DDL_LOG_MEMORY_ENTRY *used_list= global_ddl_log.first_used; + DBUG_ENTER("release_ddl_log"); + + lock_global_ddl_log(); + while (used_list) + { + DDL_LOG_MEMORY_ENTRY *tmp= used_list->next_log_entry; + my_free((char*)used_list, MYF(0)); + used_list= tmp; + } + while (free_list) + { + DDL_LOG_MEMORY_ENTRY *tmp= free_list->next_log_entry; + my_free((char*)free_list, MYF(0)); + free_list= tmp; + } + VOID(my_close(global_ddl_log.file_id, MYF(0))); + unlock_global_ddl_log(); + VOID(pthread_mutex_destroy(&LOCK_gdl)); + DBUG_VOID_RETURN; +} + + +/* + Lock mutex for global ddl log + SYNOPSIS + lock_global_ddl_log() + RETURN VALUES + NONE +*/ + +void lock_global_ddl_log() +{ + DBUG_ENTER("lock_global_ddl_log"); + + VOID(pthread_mutex_lock(&LOCK_gdl)); + DBUG_VOID_RETURN; +} + + +/* + Unlock mutex for global ddl log + SYNOPSIS + unlock_global_ddl_log() + RETURN VALUES + NONE +*/ + +void unlock_global_ddl_log() +{ + DBUG_ENTER("unlock_global_ddl_log"); + + VOID(pthread_mutex_unlock(&LOCK_gdl)); + DBUG_VOID_RETURN; +} + + +/* +--------------------------------------------------------------------------- + + END MODULE DDL log + -------------------- + +--------------------------------------------------------------------------- +*/ + /* SYNOPSIS @@ -274,83 +1197,66 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) */ int error= 0; char path[FN_REFLEN+1]; + char shadow_path[FN_REFLEN+1]; + char shadow_frm_name[FN_REFLEN+1]; char frm_name[FN_REFLEN+1]; DBUG_ENTER("mysql_write_frm"); - if (flags & WFRM_INITIAL_WRITE) + /* + Build shadow frm file name + */ + build_table_filename(shadow_path, sizeof(shadow_path), lpt->db, + lpt->table_name, "#"); + strxmov(shadow_frm_name, shadow_path, reg_ext, NullS); + if (flags & WFRM_WRITE_SHADOW) { - error= mysql_copy_create_list(lpt->create_list, - &lpt->new_create_list); - error+= mysql_copy_key_list(lpt->key_list, - &lpt->new_key_list); - if (error) + if (mysql_copy_create_list(lpt->create_list, + &lpt->new_create_list) || + mysql_copy_key_list(lpt->key_list, + &lpt->new_key_list) || + mysql_prepare_table(lpt->thd, lpt->create_info, + &lpt->new_create_list, + &lpt->new_key_list, + /*tmp_table*/ 1, + &lpt->db_options, + lpt->table->file, + &lpt->key_info_buffer, + &lpt->key_count, + /*select_field_count*/ 0)) { DBUG_RETURN(TRUE); } - } - build_table_filename(path, sizeof(path), lpt->db, lpt->table_name, ""); - strxmov(frm_name, path, reg_ext, NullS); - if ((flags & WFRM_INITIAL_WRITE) && - (mysql_prepare_table(lpt->thd, lpt->create_info, &lpt->new_create_list, - &lpt->new_key_list,/*tmp_table*/ 1, &lpt->db_options, - lpt->table->file, &lpt->key_info_buffer, - &lpt->key_count, /*select_field_count*/ 0))) - { - DBUG_RETURN(TRUE); - } #ifdef WITH_PARTITION_STORAGE_ENGINE - { - partition_info *part_info= lpt->table->part_info; - char *part_syntax_buf; - uint syntax_len, i; - bool any_unnormal_state= FALSE; - - if (part_info) { - uint max_part_state_len= part_info->partitions.elements + - part_info->temp_partitions.elements; - if (!(part_info->part_state= (uchar*)sql_alloc(max_part_state_len))) - { - DBUG_RETURN(TRUE); - } - part_info->part_state_len= 0; - if (!(part_syntax_buf= generate_partition_syntax(part_info, - &syntax_len, - TRUE, FALSE))) - { - DBUG_RETURN(TRUE); - } - for (i= 0; i < part_info->part_state_len; i++) - { - enum partition_state part_state= - (enum partition_state)part_info->part_state[i]; - if (part_state != PART_NORMAL && part_state != PART_IS_ADDED) - any_unnormal_state= TRUE; - } - if (!any_unnormal_state) + partition_info *part_info= lpt->table->part_info; + char *part_syntax_buf; + uint syntax_len; + + if (part_info) { - part_info->part_state= NULL; - part_info->part_state_len= 0; + if (!(part_syntax_buf= generate_partition_syntax(part_info, + &syntax_len, + TRUE, FALSE))) + { + DBUG_RETURN(TRUE); + } + part_info->part_info_string= part_syntax_buf; + part_info->part_info_len= syntax_len; } - part_info->part_info_string= part_syntax_buf; - part_info->part_info_len= syntax_len; } - } #endif - /* - We write the frm file with the LOCK_open mutex since otherwise we could - overwrite the frm file as another is reading it in open_table. - */ - lpt->create_info->table_options= lpt->db_options; - VOID(pthread_mutex_lock(&LOCK_open)); - if ((mysql_create_frm(lpt->thd, frm_name, lpt->db, lpt->table_name, - lpt->create_info, lpt->new_create_list, lpt->key_count, - lpt->key_info_buffer, lpt->table->file)) || - ((flags & WFRM_CREATE_HANDLER_FILES) && - lpt->table->file->create_handler_files(path))) - { - error= 1; - goto end; + /* Write shadow frm file */ + lpt->create_info->table_options= lpt->db_options; + if ((mysql_create_frm(lpt->thd, shadow_frm_name, lpt->db, + lpt->table_name, lpt->create_info, + lpt->new_create_list, lpt->key_count, + lpt->key_info_buffer, lpt->table->file)) || + lpt->table->file->create_handler_files(shadow_path, NULL, FALSE)) + { + my_delete(shadow_frm_name, MYF(0)); + error= 1; + goto end; + } } if (flags & WFRM_PACK_FRM) { @@ -362,7 +1268,7 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) */ const void *data= 0; uint length= 0; - if (readfrm(path, &data, &length) || + if (readfrm(shadow_path, &data, &length) || packfrm(data, length, &lpt->pack_frm_data, &lpt->pack_frm_len)) { my_free((char*)data, MYF(MY_ALLOW_ZERO_PTR)); @@ -371,11 +1277,43 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) error= 1; goto end; } - error= my_delete(frm_name, MYF(MY_WME)); + error= my_delete(shadow_frm_name, MYF(MY_WME)); + } + if (flags & WFRM_INSTALL_SHADOW) + { +#ifdef WITH_PARTITION_STORAGE_ENGINE + partition_info *part_info= lpt->part_info; +#endif + /* + Build frm file name + */ + build_table_filename(path, sizeof(path), lpt->db, + lpt->table_name, ""); + strxmov(frm_name, path, reg_ext, NullS); + /* + When we are changing to use new frm file we need to ensure that we + don't collide with another thread in process to open the frm file. + */ + VOID(pthread_mutex_lock(&LOCK_open)); + if (my_delete(frm_name, MYF(MY_WME)) || +#ifdef WITH_PARTITION_STORAGE_ENGINE + deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos) || + (sync_ddl_log(), FALSE) || +#endif + my_rename(shadow_frm_name, frm_name, MYF(MY_WME)) || + lpt->table->file->create_handler_files(path, shadow_path, TRUE)) + { + error= 1; + } + VOID(pthread_mutex_unlock(&LOCK_open)); +#ifdef WITH_PARTITION_STORAGE_ENGINE + deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos); + part_info->frm_log_entry= NULL; + VOID(sync_ddl_log()); +#endif } - /* Frm file have been updated to reflect the change about to happen. */ + end: - VOID(pthread_mutex_unlock(&LOCK_open)); DBUG_RETURN(error); } @@ -4788,7 +5726,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, error= (mysql_create_frm(thd, reg_path, db, table_name, create_info, prepared_create_list, key_count, key_info_buffer, table->file) || - table->file->create_handler_files(reg_path)); + table->file->create_handler_files(reg_path, NULL, FALSE)); VOID(pthread_mutex_unlock(&LOCK_open)); if (error) goto err; @@ -4834,7 +5772,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, error= (mysql_create_frm(thd, reg_path, db, table_name, create_info, prepared_create_list, key_count, key_info_buffer, table->file) || - table->file->create_handler_files(reg_path)); + table->file->create_handler_files(reg_path, NULL, FALSE)); VOID(pthread_mutex_unlock(&LOCK_open)); if (error) goto err; @@ -5061,7 +5999,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, VOID(pthread_mutex_lock(&LOCK_open)); } /* Tell the handler that a new frm file is in place. */ - if (table->file->create_handler_files(reg_path)) + if (table->file->create_handler_files(reg_path, NULL, FALSE)) { VOID(pthread_mutex_unlock(&LOCK_open)); goto err; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index e9379ab2f18..11f6fa6e67b 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -3557,75 +3557,14 @@ part_definition: LEX *lex= Lex; partition_info *part_info= lex->part_info; partition_element *p_elem= new partition_element(); - uint part_id= part_info->partitions.elements + - part_info->temp_partitions.elements; - enum partition_state part_state; + uint part_id= part_info->partitions.elements; - if (part_info->part_state) - part_state= (enum partition_state)part_info->part_state[part_id]; - else - part_state= PART_NORMAL; - switch (part_state) + if (!p_elem || part_info->partitions.push_back(p_elem)) { - case PART_TO_BE_DROPPED: - /* - This part is currently removed so we keep it in a - temporary list for REPAIR TABLE to be able to handle - failures during drop partition process. - */ - case PART_TO_BE_ADDED: - /* - This part is currently being added so we keep it in a - temporary list for REPAIR TABLE to be able to handle - failures during add partition process. - */ - if (!p_elem || part_info->temp_partitions.push_back(p_elem)) - { - mem_alloc_error(sizeof(partition_element)); - YYABORT; - } - break; - case PART_IS_ADDED: - /* - Part has been added and is now a normal partition - */ - case PART_TO_BE_REORGED: - /* - This part is currently reorganised, it is still however - used so we keep it in the list of partitions. We do - however need the state to be able to handle REPAIR TABLE - after failures in the reorganisation process. - */ - case PART_REORGED_DROPPED: - /* - This part is currently reorganised as part of a - COALESCE PARTITION and it will be dropped without a new - replacement partition after completing the reorganisation. - */ - case PART_CHANGED: - /* - This part is currently split or merged as part of ADD - PARTITION for a hash partition or as part of COALESCE - PARTITION for a hash partitioned table. - */ - case PART_IS_CHANGED: - /* - This part has been split or merged as part of ADD - PARTITION for a hash partition or as part of COALESCE - PARTITION for a hash partitioned table. - */ - case PART_NORMAL: - if (!p_elem || part_info->partitions.push_back(p_elem)) - { - mem_alloc_error(sizeof(partition_element)); - YYABORT; - } - break; - default: - mem_alloc_error((part_id * 1000) + part_state); - YYABORT; + mem_alloc_error(sizeof(partition_element)); + YYABORT; } - p_elem->part_state= part_state; + p_elem->part_state= PART_NORMAL; part_info->curr_part_elem= p_elem; part_info->current_partition= p_elem; part_info->use_default_partitions= FALSE; diff --git a/sql/table.cc b/sql/table.cc index 4807d9d0cf0..d8d5b841e21 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -667,36 +667,16 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, #endif next_chunk+= 5 + partition_info_len; } - if (share->mysql_version > 50105 && next_chunk + 5 < buff_end) +#if 1 + if (share->mysql_version == 50106 || + share->mysql_version == 50107) { /* - Partition state was introduced to support partition management in version 5.1.5 - */ - uint32 part_state_len= uint4korr(next_chunk); -#ifdef WITH_PARTITION_STORAGE_ENGINE - if ((share->part_state_len= part_state_len)) - if (!(share->part_state= - (uchar*) memdup_root(&share->mem_root, next_chunk + 4, - part_state_len))) - { - my_free(buff, MYF(0)); - goto err; - } -#else - if (part_state_len) - { - DBUG_PRINT("info", ("WITH_PARTITION_STORAGE_ENGINE is not defined")); - my_free(buff, MYF(0)); - goto err; - } -#endif - next_chunk+= 4 + part_state_len; - } -#ifdef WITH_PARTITION_STORAGE_ENGINE - else - { - share->part_state_len= 0; - share->part_state= NULL; + Partition state array was here in version 5.1.6, this code makes + it possible to load a 5.1.6 table in later versions. Can most + likely be removed at some point in time. + */ + next_chunk+= 4; } #endif keyinfo= share->key_info; diff --git a/sql/unireg.cc b/sql/unireg.cc index 7cb79e866f9..fb3ec994eff 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -136,7 +136,6 @@ bool mysql_create_frm(THD *thd, const char *file_name, if (part_info) { create_info->extra_size+= part_info->part_info_len; - create_info->extra_size+= part_info->part_state_len; } #endif @@ -209,12 +208,6 @@ bool mysql_create_frm(THD *thd, const char *file_name, my_write(file, (const byte*)part_info->part_info_string, part_info->part_info_len + 1, MYF_RW)) goto err; - DBUG_PRINT("info", ("Part state len = %d", part_info->part_state_len)); - int4store(buff, part_info->part_state_len); - if (my_write(file, (const byte*)buff, 4, MYF_RW) || - my_write(file, (const byte*)part_info->part_state, - part_info->part_state_len, MYF_RW)) - goto err; } else #endif @@ -330,7 +323,7 @@ int rea_create_table(THD *thd, const char *path, // Make sure mysql_create_frm din't remove extension DBUG_ASSERT(*fn_rext(frm_name)); - if (file->create_handler_files(path)) + if (file->create_handler_files(path, NULL, FALSE)) goto err_handler; if (!create_info->frm_only && ha_create_table(thd, path, db, table_name, create_info,0)) |