The world's most popular open source database
00001 /* Copyright (C) 2000-2004 MySQL AB 00002 00003 This program is free software; you can redistribute it and/or modify 00004 it under the terms of the GNU General Public License as published by 00005 the Free Software Foundation; either version 2 of the License, or 00006 (at your option) any later version. 00007 00008 This program is distributed in the hope that it will be useful, 00009 but WITHOUT ANY WARRANTY; without even the implied warranty of 00010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00011 GNU General Public License for more details. 00012 00013 You should have received a copy of the GNU General Public License 00014 along with this program; if not, write to the Free Software 00015 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 00016 00017 /* drop and alter of tables */ 00018 00019 #include "mysql_priv.h" 00020 #include <hash.h> 00021 #include <myisam.h> 00022 #include <my_dir.h> 00023 #include "sp_head.h" 00024 #include "sql_trigger.h" 00025 #include "sql_show.h" 00026 00027 #ifdef __WIN__ 00028 #include <io.h> 00029 #endif 00030 00031 int creating_table= 0; // How many mysql_create_table are running 00032 00033 const char *primary_key_name="PRIMARY"; 00034 00035 static bool check_if_keyname_exists(const char *name,KEY *start, KEY *end); 00036 static char *make_unique_key_name(const char *field_name,KEY *start,KEY *end); 00037 static int copy_data_between_tables(TABLE *from,TABLE *to, 00038 List<create_field> &create, bool ignore, 00039 uint order_num, ORDER *order, 00040 ha_rows *copied,ha_rows *deleted); 00041 static bool prepare_blob_field(THD *thd, create_field *sql_field); 00042 static bool check_engine(THD *thd, const char *table_name, 00043 HA_CREATE_INFO *create_info); 00044 static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, 00045 List<create_field> *fields, 00046 List<Key> *keys, bool tmp_table, 00047 uint *db_options, 00048 handler *file, KEY **key_info_buffer, 00049 uint *key_count, int select_field_count); 00050 00051 #define MYSQL50_TABLE_NAME_PREFIX "#mysql50#" 00052 #define MYSQL50_TABLE_NAME_PREFIX_LENGTH 9 00053 00054 00055 /* 00056 Translate a file name to a table name (WL #1324). 00057 00058 SYNOPSIS 00059 filename_to_tablename() 00060 from The file name in my_charset_filename. 00061 to OUT The table name in system_charset_info. 00062 to_length The size of the table name buffer. 00063 00064 RETURN 00065 Table name length. 00066 */ 00067 00068 uint filename_to_tablename(const char *from, char *to, uint to_length) 00069 { 00070 uint errors; 00071 uint res; 00072 DBUG_ENTER("filename_to_tablename"); 00073 DBUG_PRINT("enter", ("from '%s'", from)); 00074 00075 if (!memcmp(from, tmp_file_prefix, tmp_file_prefix_length)) 00076 { 00077 /* Temporary table name. */ 00078 res= (strnmov(to, from, to_length) - to); 00079 } 00080 else 00081 { 00082 res= strconvert(&my_charset_filename, from, 00083 system_charset_info, to, to_length, &errors); 00084 if (errors) // Old 5.0 name 00085 { 00086 res= (strxnmov(to, to_length, MYSQL50_TABLE_NAME_PREFIX, from, NullS) - 00087 to); 00088 sql_print_error("Invalid (old?) table or database name '%s'", from); 00089 /* 00090 TODO: add a stored procedure for fix table and database names, 00091 and mention its name in error log. 00092 */ 00093 } 00094 } 00095 00096 DBUG_PRINT("exit", ("to '%s'", to)); 00097 DBUG_RETURN(res); 00098 } 00099 00100 00101 /* 00102 Translate a table name to a file name (WL #1324). 00103 00104 SYNOPSIS 00105 tablename_to_filename() 00106 from The table name in system_charset_info. 00107 to OUT The file name in my_charset_filename. 00108 to_length The size of the file name buffer. 00109 00110 RETURN 00111 File name length. 00112 */ 00113 00114 uint tablename_to_filename(const char *from, char *to, uint to_length) 00115 { 00116 uint errors, length; 00117 DBUG_ENTER("tablename_to_filename"); 00118 DBUG_PRINT("enter", ("from '%s'", from)); 00119 00120 if (from[0] == '#' && !strncmp(from, MYSQL50_TABLE_NAME_PREFIX, 00121 MYSQL50_TABLE_NAME_PREFIX_LENGTH)) 00122 DBUG_RETURN((uint) (strmake(to, from+MYSQL50_TABLE_NAME_PREFIX_LENGTH, 00123 to_length-1) - 00124 (from + MYSQL50_TABLE_NAME_PREFIX_LENGTH))); 00125 length= strconvert(system_charset_info, from, 00126 &my_charset_filename, to, to_length, &errors); 00127 if (check_if_legal_tablename(to) && 00128 length + 4 < to_length) 00129 { 00130 memcpy(to + length, "@@@", 4); 00131 length+= 3; 00132 } 00133 DBUG_PRINT("exit", ("to '%s'", to)); 00134 DBUG_RETURN(length); 00135 } 00136 00137 00138 /* 00139 Creates path to a file: mysql_data_dir/db/table.ext 00140 00141 SYNOPSIS 00142 build_table_filename() 00143 buff Where to write result in my_charset_filename. 00144 bufflen buff size 00145 db Database name in system_charset_info. 00146 table_name Table name in system_charset_info. 00147 ext File extension. 00148 flags FN_FROM_IS_TMP or FN_TO_IS_TMP or FN_IS_TMP 00149 table_name is temporary, do not change. 00150 00151 NOTES 00152 00153 Uses database and table name, and extension to create 00154 a file name in mysql_data_dir. Database and table 00155 names are converted from system_charset_info into "fscs". 00156 Unless flags indicate a temporary table name. 00157 'db' is always converted. 00158 'ext' is not converted. 00159 00160 The conversion suppression is required for ALTER TABLE. This 00161 statement creates intermediate tables. These are regular 00162 (non-temporary) tables with a temporary name. Their path names must 00163 be derivable from the table name. So we cannot use 00164 build_tmptable_filename() for them. 00165 00166 RETURN 00167 path length 00168 */ 00169 00170 uint build_table_filename(char *buff, size_t bufflen, const char *db, 00171 const char *table_name, const char *ext, uint flags) 00172 { 00173 uint length; 00174 char dbbuff[FN_REFLEN]; 00175 char tbbuff[FN_REFLEN]; 00176 DBUG_ENTER("build_table_filename"); 00177 00178 if (flags & FN_IS_TMP) // FN_FROM_IS_TMP | FN_TO_IS_TMP 00179 strnmov(tbbuff, table_name, sizeof(tbbuff)); 00180 else 00181 VOID(tablename_to_filename(table_name, tbbuff, sizeof(tbbuff))); 00182 00183 VOID(tablename_to_filename(db, dbbuff, sizeof(dbbuff))); 00184 length= strxnmov(buff, bufflen, mysql_data_home, "/", dbbuff, 00185 "/", tbbuff, ext, NullS) - buff; 00186 DBUG_PRINT("exit", ("buff: '%s'", buff)); 00187 DBUG_RETURN(length); 00188 } 00189 00190 00191 /* 00192 Creates path to a file: mysql_tmpdir/#sql1234_12_1.ext 00193 00194 SYNOPSIS 00195 build_tmptable_filename() 00196 thd The thread handle. 00197 buff Where to write result in my_charset_filename. 00198 bufflen buff size 00199 00200 NOTES 00201 00202 Uses current_pid, thread_id, and tmp_table counter to create 00203 a file name in mysql_tmpdir. 00204 00205 RETURN 00206 path length 00207 */ 00208 00209 uint build_tmptable_filename(THD* thd, char *buff, size_t bufflen) 00210 { 00211 uint length; 00212 char tmp_table_name[tmp_file_prefix_length+22+22+22+3]; 00213 DBUG_ENTER("build_tmptable_filename"); 00214 00215 my_snprintf(tmp_table_name, sizeof(tmp_table_name), 00216 "%s%lx_%lx_%x", 00217 tmp_file_prefix, current_pid, 00218 thd->thread_id, thd->tmp_table++); 00219 00220 strxnmov(buff, bufflen, mysql_tmpdir, "/", tmp_table_name, reg_ext, NullS); 00221 length= unpack_filename(buff, buff); 00222 DBUG_PRINT("exit", ("buff: '%s'", buff)); 00223 DBUG_RETURN(length); 00224 } 00225 00226 /* 00227 Return values for compare_tables(). 00228 If you make compare_tables() non-static, move them to a header file. 00229 */ 00230 #define ALTER_TABLE_DATA_CHANGED 1 00231 #define ALTER_TABLE_INDEX_CHANGED 2 00232 00233 00234 /* 00235 SYNOPSIS 00236 mysql_copy_create_list() 00237 orig_create_list Original list of created fields 00238 inout::new_create_list Copy of original list 00239 00240 RETURN VALUES 00241 FALSE Success 00242 TRUE Memory allocation error 00243 00244 DESCRIPTION 00245 mysql_prepare_table destroys the create_list and in some cases we need 00246 this lists for more purposes. Thus we copy it specifically for use 00247 by mysql_prepare_table 00248 */ 00249 00250 static int mysql_copy_create_list(List<create_field> *orig_create_list, 00251 List<create_field> *new_create_list) 00252 { 00253 List_iterator<create_field> prep_field_it(*orig_create_list); 00254 create_field *prep_field; 00255 DBUG_ENTER("mysql_copy_create_list"); 00256 00257 while ((prep_field= prep_field_it++)) 00258 { 00259 create_field *field= new create_field(*prep_field); 00260 if (!field || new_create_list->push_back(field)) 00261 { 00262 mem_alloc_error(2); 00263 DBUG_RETURN(TRUE); 00264 } 00265 } 00266 DBUG_RETURN(FALSE); 00267 } 00268 00269 00270 /* 00271 SYNOPSIS 00272 mysql_copy_key_list() 00273 orig_key Original list of keys 00274 inout::new_key Copy of original list 00275 00276 RETURN VALUES 00277 FALSE Success 00278 TRUE Memory allocation error 00279 00280 DESCRIPTION 00281 mysql_prepare_table destroys the key list and in some cases we need 00282 this lists for more purposes. Thus we copy it specifically for use 00283 by mysql_prepare_table 00284 */ 00285 00286 static int mysql_copy_key_list(List<Key> *orig_key, 00287 List<Key> *new_key) 00288 { 00289 List_iterator<Key> prep_key_it(*orig_key); 00290 Key *prep_key; 00291 DBUG_ENTER("mysql_copy_key_list"); 00292 00293 while ((prep_key= prep_key_it++)) 00294 { 00295 List<key_part_spec> prep_columns; 00296 List_iterator<key_part_spec> prep_col_it(prep_key->columns); 00297 key_part_spec *prep_col; 00298 Key *temp_key; 00299 00300 while ((prep_col= prep_col_it++)) 00301 { 00302 key_part_spec *prep_key_part; 00303 00304 if (!(prep_key_part= new key_part_spec(*prep_col))) 00305 { 00306 mem_alloc_error(sizeof(key_part_spec)); 00307 DBUG_RETURN(TRUE); 00308 } 00309 if (prep_columns.push_back(prep_key_part)) 00310 { 00311 mem_alloc_error(2); 00312 DBUG_RETURN(TRUE); 00313 } 00314 } 00315 if (!(temp_key= new Key(prep_key->type, prep_key->name, 00316 &prep_key->key_create_info, 00317 prep_key->generated, 00318 prep_columns))) 00319 { 00320 mem_alloc_error(sizeof(Key)); 00321 DBUG_RETURN(TRUE); 00322 } 00323 if (new_key->push_back(temp_key)) 00324 { 00325 mem_alloc_error(2); 00326 DBUG_RETURN(TRUE); 00327 } 00328 } 00329 DBUG_RETURN(FALSE); 00330 } 00331 00332 /* 00333 -------------------------------------------------------------------------- 00334 00335 MODULE: DDL log 00336 ----------------- 00337 00338 This module is used to ensure that we can recover from crashes that occur 00339 in the middle of a meta-data operation in MySQL. E.g. DROP TABLE t1, t2; 00340 We need to ensure that both t1 and t2 are dropped and not only t1 and 00341 also that each table drop is entirely done and not "half-baked". 00342 00343 To support this we create log entries for each meta-data statement in the 00344 ddl log while we are executing. These entries are dropped when the 00345 operation is completed. 00346 00347 At recovery those entries that were not completed will be executed. 00348 00349 There is only one ddl log in the system and it is protected by a mutex 00350 and there is a global struct that contains information about its current 00351 state. 00352 00353 History: 00354 First version written in 2006 by Mikael Ronstrom 00355 -------------------------------------------------------------------------- 00356 */ 00357 00358 00359 typedef struct st_global_ddl_log 00360 { 00361 /* 00362 We need to adjust buffer size to be able to handle downgrades/upgrades 00363 where IO_SIZE has changed. We'll set the buffer size such that we can 00364 handle that the buffer size was upto 4 times bigger in the version 00365 that wrote the DDL log. 00366 */ 00367 char file_entry_buf[4*IO_SIZE]; 00368 char file_name_str[FN_REFLEN]; 00369 char *file_name; 00370 DDL_LOG_MEMORY_ENTRY *first_free; 00371 DDL_LOG_MEMORY_ENTRY *first_used; 00372 uint num_entries; 00373 File file_id; 00374 uint name_len; 00375 uint io_size; 00376 bool inited; 00377 bool recovery_phase; 00378 } GLOBAL_DDL_LOG; 00379 00380 GLOBAL_DDL_LOG global_ddl_log; 00381 00382 pthread_mutex_t LOCK_gdl; 00383 00384 #define DDL_LOG_ENTRY_TYPE_POS 0 00385 #define DDL_LOG_ACTION_TYPE_POS 1 00386 #define DDL_LOG_PHASE_POS 2 00387 #define DDL_LOG_NEXT_ENTRY_POS 4 00388 #define DDL_LOG_NAME_POS 8 00389 00390 #define DDL_LOG_NUM_ENTRY_POS 0 00391 #define DDL_LOG_NAME_LEN_POS 4 00392 #define DDL_LOG_IO_SIZE_POS 8 00393 00394 /* 00395 Read one entry from ddl log file 00396 SYNOPSIS 00397 read_ddl_log_file_entry() 00398 entry_no Entry number to read 00399 RETURN VALUES 00400 TRUE Error 00401 FALSE Success 00402 */ 00403 00404 static bool read_ddl_log_file_entry(uint entry_no) 00405 { 00406 bool error= FALSE; 00407 File file_id= global_ddl_log.file_id; 00408 char *file_entry_buf= (char*)global_ddl_log.file_entry_buf; 00409 uint io_size= global_ddl_log.io_size; 00410 DBUG_ENTER("read_ddl_log_file_entry"); 00411 00412 if (my_pread(file_id, (byte*)file_entry_buf, io_size, io_size * entry_no, 00413 MYF(MY_WME)) != io_size) 00414 error= TRUE; 00415 DBUG_RETURN(error); 00416 } 00417 00418 00419 /* 00420 Write one entry from ddl log file 00421 SYNOPSIS 00422 write_ddl_log_file_entry() 00423 entry_no Entry number to read 00424 RETURN VALUES 00425 TRUE Error 00426 FALSE Success 00427 */ 00428 00429 static bool write_ddl_log_file_entry(uint entry_no) 00430 { 00431 bool error= FALSE; 00432 File file_id= global_ddl_log.file_id; 00433 char *file_entry_buf= (char*)global_ddl_log.file_entry_buf; 00434 DBUG_ENTER("write_ddl_log_file_entry"); 00435 00436 if (my_pwrite(file_id, (byte*)file_entry_buf, 00437 IO_SIZE, IO_SIZE * entry_no, MYF(MY_WME)) != IO_SIZE) 00438 error= TRUE; 00439 DBUG_RETURN(error); 00440 } 00441 00442 00443 /* 00444 Write ddl log header 00445 SYNOPSIS 00446 write_ddl_log_header() 00447 RETURN VALUES 00448 TRUE Error 00449 FALSE Success 00450 */ 00451 00452 static bool write_ddl_log_header() 00453 { 00454 uint16 const_var; 00455 bool error= FALSE; 00456 DBUG_ENTER("write_ddl_log_header"); 00457 00458 int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NUM_ENTRY_POS], 00459 global_ddl_log.num_entries); 00460 const_var= FN_LEN; 00461 int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_LEN_POS], 00462 const_var); 00463 const_var= IO_SIZE; 00464 int4store(&global_ddl_log.file_entry_buf[DDL_LOG_IO_SIZE_POS], 00465 const_var); 00466 if (write_ddl_log_file_entry(0UL)) 00467 { 00468 sql_print_error("Error writing ddl log header"); 00469 DBUG_RETURN(TRUE); 00470 } 00471 VOID(sync_ddl_log()); 00472 DBUG_RETURN(error); 00473 } 00474 00475 00476 /* 00477 Create ddl log file name 00478 SYNOPSIS 00479 create_ddl_log_file_name() 00480 file_name Filename setup 00481 RETURN VALUES 00482 NONE 00483 */ 00484 00485 static inline void create_ddl_log_file_name(char *file_name) 00486 { 00487 strxmov(file_name, mysql_data_home, "/", "ddl_log.log", NullS); 00488 } 00489 00490 00491 /* 00492 Read header of ddl log file 00493 SYNOPSIS 00494 read_ddl_log_header() 00495 RETURN VALUES 00496 > 0 Last entry in ddl log 00497 0 No entries in ddl log 00498 DESCRIPTION 00499 When we read the ddl log header we get information about maximum sizes 00500 of names in the ddl log and we also get information about the number 00501 of entries in the ddl log. 00502 */ 00503 00504 static uint read_ddl_log_header() 00505 { 00506 char *file_entry_buf= (char*)global_ddl_log.file_entry_buf; 00507 char file_name[FN_REFLEN]; 00508 uint entry_no; 00509 bool successful_open= FALSE; 00510 DBUG_ENTER("read_ddl_log_header"); 00511 00512 create_ddl_log_file_name(file_name); 00513 if ((global_ddl_log.file_id= my_open(file_name, 00514 O_RDWR | O_BINARY, MYF(MY_WME))) >= 0) 00515 { 00516 if (read_ddl_log_file_entry(0UL)) 00517 { 00518 /* Write message into error log */ 00519 sql_print_error("Failed to read ddl log file in recovery"); 00520 } 00521 else 00522 successful_open= TRUE; 00523 } 00524 entry_no= uint4korr(&file_entry_buf[DDL_LOG_NUM_ENTRY_POS]); 00525 global_ddl_log.name_len= uint4korr(&file_entry_buf[DDL_LOG_NAME_LEN_POS]); 00526 if (successful_open) 00527 { 00528 global_ddl_log.io_size= uint4korr(&file_entry_buf[DDL_LOG_IO_SIZE_POS]); 00529 DBUG_ASSERT(global_ddl_log.io_size <= 00530 sizeof(global_ddl_log.file_entry_buf)); 00531 } 00532 else 00533 { 00534 entry_no= 0; 00535 } 00536 global_ddl_log.first_free= NULL; 00537 global_ddl_log.first_used= NULL; 00538 global_ddl_log.num_entries= 0; 00539 VOID(pthread_mutex_init(&LOCK_gdl, MY_MUTEX_INIT_FAST)); 00540 DBUG_RETURN(entry_no); 00541 } 00542 00543 00544 /* 00545 Read a ddl log entry 00546 SYNOPSIS 00547 read_ddl_log_entry() 00548 read_entry Number of entry to read 00549 out:entry_info Information from entry 00550 RETURN VALUES 00551 TRUE Error 00552 FALSE Success 00553 DESCRIPTION 00554 Read a specified entry in the ddl log 00555 */ 00556 00557 bool read_ddl_log_entry(uint read_entry, DDL_LOG_ENTRY *ddl_log_entry) 00558 { 00559 char *file_entry_buf= (char*)&global_ddl_log.file_entry_buf; 00560 uint inx; 00561 uchar single_char; 00562 DBUG_ENTER("read_ddl_log_entry"); 00563 00564 if (read_ddl_log_file_entry(read_entry)) 00565 { 00566 DBUG_RETURN(TRUE); 00567 } 00568 ddl_log_entry->entry_pos= read_entry; 00569 single_char= file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]; 00570 ddl_log_entry->entry_type= (enum ddl_log_entry_code)single_char; 00571 single_char= file_entry_buf[DDL_LOG_ACTION_TYPE_POS]; 00572 ddl_log_entry->action_type= (enum ddl_log_action_code)single_char; 00573 ddl_log_entry->phase= file_entry_buf[DDL_LOG_PHASE_POS]; 00574 ddl_log_entry->next_entry= uint4korr(&file_entry_buf[DDL_LOG_NEXT_ENTRY_POS]); 00575 ddl_log_entry->name= &file_entry_buf[DDL_LOG_NAME_POS]; 00576 inx= DDL_LOG_NAME_POS + global_ddl_log.name_len; 00577 ddl_log_entry->from_name= &file_entry_buf[inx]; 00578 inx+= global_ddl_log.name_len; 00579 ddl_log_entry->handler_name= &file_entry_buf[inx]; 00580 DBUG_RETURN(FALSE); 00581 } 00582 00583 00584 /* 00585 Initialise ddl log 00586 SYNOPSIS 00587 init_ddl_log() 00588 00589 DESCRIPTION 00590 Write the header of the ddl log file and length of names. Also set 00591 number of entries to zero. 00592 00593 RETURN VALUES 00594 TRUE Error 00595 FALSE Success 00596 */ 00597 00598 static bool init_ddl_log() 00599 { 00600 bool error= FALSE; 00601 char file_name[FN_REFLEN]; 00602 DBUG_ENTER("init_ddl_log"); 00603 00604 if (global_ddl_log.inited) 00605 goto end; 00606 00607 global_ddl_log.io_size= IO_SIZE; 00608 create_ddl_log_file_name(file_name); 00609 if ((global_ddl_log.file_id= my_create(file_name, 00610 CREATE_MODE, 00611 O_RDWR | O_TRUNC | O_BINARY, 00612 MYF(MY_WME))) < 0) 00613 { 00614 /* Couldn't create ddl log file, this is serious error */ 00615 sql_print_error("Failed to open ddl log file"); 00616 DBUG_RETURN(TRUE); 00617 } 00618 global_ddl_log.inited= TRUE; 00619 if (write_ddl_log_header()) 00620 { 00621 VOID(my_close(global_ddl_log.file_id, MYF(MY_WME))); 00622 global_ddl_log.inited= FALSE; 00623 DBUG_RETURN(TRUE); 00624 } 00625 00626 end: 00627 DBUG_RETURN(FALSE); 00628 } 00629 00630 00631 /* 00632 Execute one action in a ddl log entry 00633 SYNOPSIS 00634 execute_ddl_log_action() 00635 ddl_log_entry Information in action entry to execute 00636 RETURN VALUES 00637 TRUE Error 00638 FALSE Success 00639 */ 00640 00641 static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry) 00642 { 00643 bool frm_action= FALSE; 00644 LEX_STRING handler_name; 00645 handler *file= NULL; 00646 MEM_ROOT mem_root; 00647 int error= TRUE; 00648 char to_path[FN_REFLEN]; 00649 char from_path[FN_REFLEN]; 00650 char *par_ext= (char*)".par"; 00651 handlerton *hton; 00652 DBUG_ENTER("execute_ddl_log_action"); 00653 00654 if (ddl_log_entry->entry_type == DDL_IGNORE_LOG_ENTRY_CODE) 00655 { 00656 DBUG_RETURN(FALSE); 00657 } 00658 handler_name.str= (char*)ddl_log_entry->handler_name; 00659 handler_name.length= strlen(ddl_log_entry->handler_name); 00660 init_sql_alloc(&mem_root, TABLE_ALLOC_BLOCK_SIZE, 0); 00661 if (!strcmp(ddl_log_entry->handler_name, reg_ext)) 00662 frm_action= TRUE; 00663 else 00664 { 00665 TABLE_SHARE dummy; 00666 00667 hton= ha_resolve_by_name(thd, &handler_name); 00668 if (!hton) 00669 { 00670 my_error(ER_ILLEGAL_HA, MYF(0), ddl_log_entry->handler_name); 00671 goto error; 00672 } 00673 bzero(&dummy, sizeof(TABLE_SHARE)); 00674 file= get_new_handler(&dummy, &mem_root, hton); 00675 if (!file) 00676 { 00677 mem_alloc_error(sizeof(handler)); 00678 goto error; 00679 } 00680 } 00681 switch (ddl_log_entry->action_type) 00682 { 00683 case DDL_LOG_REPLACE_ACTION: 00684 case DDL_LOG_DELETE_ACTION: 00685 { 00686 if (ddl_log_entry->phase == 0) 00687 { 00688 if (frm_action) 00689 { 00690 strxmov(to_path, ddl_log_entry->name, reg_ext, NullS); 00691 if ((error= my_delete(to_path, MYF(MY_WME)))) 00692 { 00693 if (my_errno != ENOENT) 00694 break; 00695 } 00696 #ifdef WITH_PARTITION_STORAGE_ENGINE 00697 strxmov(to_path, ddl_log_entry->name, par_ext, NullS); 00698 VOID(my_delete(to_path, MYF(MY_WME))); 00699 #endif 00700 } 00701 else 00702 { 00703 if ((error= file->delete_table(ddl_log_entry->name))) 00704 { 00705 if (error != ENOENT && error != HA_ERR_NO_SUCH_TABLE) 00706 break; 00707 } 00708 } 00709 if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos))) 00710 break; 00711 VOID(sync_ddl_log()); 00712 error= FALSE; 00713 if (ddl_log_entry->action_type == DDL_LOG_DELETE_ACTION) 00714 break; 00715 } 00716 DBUG_ASSERT(ddl_log_entry->action_type == DDL_LOG_REPLACE_ACTION); 00717 /* 00718 Fall through and perform the rename action of the replace 00719 action. We have already indicated the success of the delete 00720 action in the log entry by stepping up the phase. 00721 */ 00722 } 00723 case DDL_LOG_RENAME_ACTION: 00724 { 00725 error= TRUE; 00726 if (frm_action) 00727 { 00728 strxmov(to_path, ddl_log_entry->name, reg_ext, NullS); 00729 strxmov(from_path, ddl_log_entry->from_name, reg_ext, NullS); 00730 if (my_rename(from_path, to_path, MYF(MY_WME))) 00731 break; 00732 #ifdef WITH_PARTITION_STORAGE_ENGINE 00733 strxmov(to_path, ddl_log_entry->name, par_ext, NullS); 00734 strxmov(from_path, ddl_log_entry->from_name, par_ext, NullS); 00735 VOID(my_rename(from_path, to_path, MYF(MY_WME))); 00736 #endif 00737 } 00738 else 00739 { 00740 if (file->rename_table(ddl_log_entry->from_name, 00741 ddl_log_entry->name)) 00742 break; 00743 } 00744 if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos))) 00745 break; 00746 VOID(sync_ddl_log()); 00747 error= FALSE; 00748 break; 00749 } 00750 default: 00751 DBUG_ASSERT(0); 00752 break; 00753 } 00754 delete file; 00755 error: 00756 free_root(&mem_root, MYF(0)); 00757 DBUG_RETURN(error); 00758 } 00759 00760 00761 /* 00762 Get a free entry in the ddl log 00763 SYNOPSIS 00764 get_free_ddl_log_entry() 00765 out:active_entry A ddl log memory entry returned 00766 RETURN VALUES 00767 TRUE Error 00768 FALSE Success 00769 */ 00770 00771 static bool get_free_ddl_log_entry(DDL_LOG_MEMORY_ENTRY **active_entry, 00772 bool *write_header) 00773 { 00774 DDL_LOG_MEMORY_ENTRY *used_entry; 00775 DDL_LOG_MEMORY_ENTRY *first_used= global_ddl_log.first_used; 00776 DBUG_ENTER("get_free_ddl_log_entry"); 00777 00778 if (global_ddl_log.first_free == NULL) 00779 { 00780 if (!(used_entry= (DDL_LOG_MEMORY_ENTRY*)my_malloc( 00781 sizeof(DDL_LOG_MEMORY_ENTRY), MYF(MY_WME)))) 00782 { 00783 sql_print_error("Failed to allocate memory for ddl log free list"); 00784 DBUG_RETURN(TRUE); 00785 } 00786 global_ddl_log.num_entries++; 00787 used_entry->entry_pos= global_ddl_log.num_entries; 00788 *write_header= TRUE; 00789 } 00790 else 00791 { 00792 used_entry= global_ddl_log.first_free; 00793 global_ddl_log.first_free= used_entry->next_log_entry; 00794 *write_header= FALSE; 00795 } 00796 /* 00797 Move from free list to used list 00798 */ 00799 used_entry->next_log_entry= first_used; 00800 used_entry->prev_log_entry= NULL; 00801 global_ddl_log.first_used= used_entry; 00802 if (first_used) 00803 first_used->prev_log_entry= used_entry; 00804 00805 *active_entry= used_entry; 00806 DBUG_RETURN(FALSE); 00807 } 00808 00809 00810 /* 00811 External interface methods for the DDL log Module 00812 --------------------------------------------------- 00813 */ 00814 00815 /* 00816 SYNOPSIS 00817 write_ddl_log_entry() 00818 ddl_log_entry Information about log entry 00819 out:entry_written Entry information written into 00820 00821 RETURN VALUES 00822 TRUE Error 00823 FALSE Success 00824 00825 DESCRIPTION 00826 A careful write of the ddl log is performed to ensure that we can 00827 handle crashes occurring during CREATE and ALTER TABLE processing. 00828 */ 00829 00830 bool write_ddl_log_entry(DDL_LOG_ENTRY *ddl_log_entry, 00831 DDL_LOG_MEMORY_ENTRY **active_entry) 00832 { 00833 bool error, write_header; 00834 DBUG_ENTER("write_ddl_log_entry"); 00835 00836 if (init_ddl_log()) 00837 { 00838 DBUG_RETURN(TRUE); 00839 } 00840 global_ddl_log.file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= 00841 (char)DDL_LOG_ENTRY_CODE; 00842 global_ddl_log.file_entry_buf[DDL_LOG_ACTION_TYPE_POS]= 00843 (char)ddl_log_entry->action_type; 00844 global_ddl_log.file_entry_buf[DDL_LOG_PHASE_POS]= 0; 00845 int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NEXT_ENTRY_POS], 00846 ddl_log_entry->next_entry); 00847 DBUG_ASSERT(strlen(ddl_log_entry->name) < FN_LEN); 00848 strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS], 00849 ddl_log_entry->name, FN_LEN - 1); 00850 if (ddl_log_entry->action_type == DDL_LOG_RENAME_ACTION || 00851 ddl_log_entry->action_type == DDL_LOG_REPLACE_ACTION) 00852 { 00853 DBUG_ASSERT(strlen(ddl_log_entry->from_name) < FN_LEN); 00854 strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_LEN], 00855 ddl_log_entry->from_name, FN_LEN - 1); 00856 } 00857 else 00858 global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_LEN]= 0; 00859 DBUG_ASSERT(strlen(ddl_log_entry->handler_name) < FN_LEN); 00860 strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + (2*FN_LEN)], 00861 ddl_log_entry->handler_name, FN_LEN - 1); 00862 if (get_free_ddl_log_entry(active_entry, &write_header)) 00863 { 00864 DBUG_RETURN(TRUE); 00865 } 00866 error= FALSE; 00867 if (write_ddl_log_file_entry((*active_entry)->entry_pos)) 00868 { 00869 error= TRUE; 00870 sql_print_error("Failed to write entry_no = %u", 00871 (*active_entry)->entry_pos); 00872 } 00873 if (write_header && !error) 00874 { 00875 VOID(sync_ddl_log()); 00876 if (write_ddl_log_header()) 00877 error= TRUE; 00878 } 00879 if (error) 00880 release_ddl_log_memory_entry(*active_entry); 00881 DBUG_RETURN(error); 00882 } 00883 00884 00885 /* 00886 Write final entry in the ddl log 00887 SYNOPSIS 00888 write_execute_ddl_log_entry() 00889 first_entry First entry in linked list of entries 00890 to execute, if 0 = NULL it means that 00891 the entry is removed and the entries 00892 are put into the free list. 00893 complete Flag indicating we are simply writing 00894 info about that entry has been completed 00895 in:out:active_entry Entry to execute, 0 = NULL if the entry 00896 is written first time and needs to be 00897 returned. In this case the entry written 00898 is returned in this parameter 00899 RETURN VALUES 00900 TRUE Error 00901 FALSE Success 00902 00903 DESCRIPTION 00904 This is the last write in the ddl log. The previous log entries have 00905 already been written but not yet synched to disk. 00906 We write a couple of log entries that describes action to perform. 00907 This entries are set-up in a linked list, however only when a first 00908 execute entry is put as the first entry these will be executed. 00909 This routine writes this first 00910 */ 00911 00912 bool write_execute_ddl_log_entry(uint first_entry, 00913 bool complete, 00914 DDL_LOG_MEMORY_ENTRY **active_entry) 00915 { 00916 bool write_header= FALSE; 00917 char *file_entry_buf= (char*)global_ddl_log.file_entry_buf; 00918 DBUG_ENTER("write_execute_ddl_log_entry"); 00919 00920 if (init_ddl_log()) 00921 { 00922 DBUG_RETURN(TRUE); 00923 } 00924 if (!complete) 00925 { 00926 /* 00927 We haven't synched the log entries yet, we synch them now before 00928 writing the execute entry. If complete is true we haven't written 00929 any log entries before, we are only here to write the execute 00930 entry to indicate it is done. 00931 */ 00932 VOID(sync_ddl_log()); 00933 file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (char)DDL_LOG_EXECUTE_CODE; 00934 } 00935 else 00936 file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (char)DDL_IGNORE_LOG_ENTRY_CODE; 00937 file_entry_buf[DDL_LOG_ACTION_TYPE_POS]= 0; /* Ignored for execute entries */ 00938 file_entry_buf[DDL_LOG_PHASE_POS]= 0; 00939 int4store(&file_entry_buf[DDL_LOG_NEXT_ENTRY_POS], first_entry); 00940 file_entry_buf[DDL_LOG_NAME_POS]= 0; 00941 file_entry_buf[DDL_LOG_NAME_POS + FN_LEN]= 0; 00942 file_entry_buf[DDL_LOG_NAME_POS + 2*FN_LEN]= 0; 00943 if (!(*active_entry)) 00944 { 00945 if (get_free_ddl_log_entry(active_entry, &write_header)) 00946 { 00947 DBUG_RETURN(TRUE); 00948 } 00949 } 00950 if (write_ddl_log_file_entry((*active_entry)->entry_pos)) 00951 { 00952 sql_print_error("Error writing execute entry in ddl log"); 00953 release_ddl_log_memory_entry(*active_entry); 00954 DBUG_RETURN(TRUE); 00955 } 00956 VOID(sync_ddl_log()); 00957 if (write_header) 00958 { 00959 if (write_ddl_log_header()) 00960 { 00961 release_ddl_log_memory_entry(*active_entry); 00962 DBUG_RETURN(TRUE); 00963 } 00964 } 00965 DBUG_RETURN(FALSE); 00966 } 00967 00968 00969 /* 00970 For complex rename operations we need to deactivate individual entries. 00971 SYNOPSIS 00972 deactivate_ddl_log_entry() 00973 entry_no Entry position of record to change 00974 RETURN VALUES 00975 TRUE Error 00976 FALSE Success 00977 DESCRIPTION 00978 During replace operations where we start with an existing table called 00979 t1 and a replacement table called t1#temp or something else and where 00980 we want to delete t1 and rename t1#temp to t1 this is not possib

