diff options
Diffstat (limited to 'sql/sql_table.cc')
-rw-r--r-- | sql/sql_table.cc | 1200 |
1 files changed, 677 insertions, 523 deletions
diff --git a/sql/sql_table.cc b/sql/sql_table.cc index db64c5afbc2..79c6d4cbaf9 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1,6 +1,6 @@ /* - Copyright (c) 2000, 2012, Oracle and/or its affiliates. - Copyright (c) 2010, 2011, Monty Program Ab + Copyright (c) 2000, 2013, Oracle and/or its affiliates. + Copyright (c) 2010, 2013, Monty Program Ab. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,7 +22,6 @@ #include "unireg.h" #include "debug_sync.h" #include "sql_table.h" -#include "sql_rename.h" // do_rename #include "sql_parse.h" // test_if_data_home_dir #include "sql_cache.h" // query_cache_* #include "sql_base.h" // open_table_uncached, lock_table_names @@ -55,7 +54,7 @@ #include "sql_parse.h" #include "sql_show.h" #include "transaction.h" -#include "datadict.h" // dd_frm_type() +#include "sql_audit.h" #ifdef __WIN__ #include <io.h> @@ -74,13 +73,8 @@ static int copy_data_between_tables(THD *thd, TABLE *from,TABLE *to, static bool prepare_blob_field(THD *thd, Create_field *sql_field); static bool check_engine(THD *, const char *, const char *, HA_CREATE_INFO *); -static int -mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, - Alter_info *alter_info, - bool tmp_table, - uint *db_options, - handler *file, KEY **key_info_buffer, - uint *key_count, int create_table_mode); +static int mysql_prepare_create_table(THD *, HA_CREATE_INFO *, Alter_info *, + uint *, handler *, KEY **, uint *, int); /** @brief Helper function for explain_filename @@ -206,7 +200,6 @@ uint explain_filename(THD* thd, uint to_length, enum_explain_filename_mode explain_mode) { - uint res= 0; char *to_p= to; char *end_p= to_p + to_length; const char *db_name= NULL; @@ -217,7 +210,8 @@ uint explain_filename(THD* thd, int part_name_len= 0; const char *subpart_name= NULL; int subpart_name_len= 0; - uint name_variant= NORMAL_PART_NAME; + uint part_type= NORMAL_PART_NAME; + const char *tmp_p; DBUG_ENTER("explain_filename"); DBUG_PRINT("enter", ("from '%s'", from)); @@ -236,17 +230,18 @@ uint explain_filename(THD* thd, table_name= tmp_p; } tmp_p= table_name; - while (!res && (tmp_p= strchr(tmp_p, '#'))) + /* Look if there are partition tokens in the table name. */ + while ((tmp_p= strchr(tmp_p, '#'))) { tmp_p++; switch (tmp_p[0]) { case 'P': case 'p': if (tmp_p[1] == '#') + { part_name= tmp_p + 2; - else - res= 1; - tmp_p+= 2; + tmp_p+= 2; + } break; case 'S': case 's': @@ -256,48 +251,32 @@ uint explain_filename(THD* thd, subpart_name= tmp_p + 3; tmp_p+= 3; } - else if ((tmp_p[1] == 'Q' || tmp_p[1] == 'q') && - (tmp_p[2] == 'L' || tmp_p[2] == 'l') && - tmp_p[3] == '-') - { - tmp_p+= 4; /* sql- prefix found */ - } - else - res= 2; break; case 'T': case 't': if ((tmp_p[1] == 'M' || tmp_p[1] == 'm') && (tmp_p[2] == 'P' || tmp_p[2] == 'p') && tmp_p[3] == '#' && !tmp_p[4]) - name_variant= TEMP_PART_NAME; - else - res= 3; - tmp_p+= 4; + { + part_type= TEMP_PART_NAME; + tmp_p+= 4; + } break; case 'R': case 'r': if ((tmp_p[1] == 'E' || tmp_p[1] == 'e') && (tmp_p[2] == 'N' || tmp_p[2] == 'n') && tmp_p[3] == '#' && !tmp_p[4]) - name_variant= RENAMED_PART_NAME; - else - res= 4; - tmp_p+= 4; + { + part_type= RENAMED_PART_NAME; + tmp_p+= 4; + } break; default: - res= 5; + /* Not partition name part. */ + ; } } - if (res) - { - /* Better to give something back if we fail parsing, than nothing at all */ - DBUG_PRINT("info", ("Error in explain_filename: %u", res)); - sql_print_warning("Invalid (old?) table or database name '%s'", from); - DBUG_RETURN(my_snprintf(to, to_length, - "<result %u when explaining filename '%s'>", - res, from)); - } if (part_name) { table_name_len= part_name - table_name - 3; @@ -305,7 +284,7 @@ uint explain_filename(THD* thd, subpart_name_len= strlen(subpart_name); else part_name_len= strlen(part_name); - if (name_variant != NORMAL_PART_NAME) + if (part_type != NORMAL_PART_NAME) { if (subpart_name) subpart_name_len-= 5; @@ -347,9 +326,9 @@ uint explain_filename(THD* thd, to_p= strnmov(to_p, " ", end_p - to_p); else to_p= strnmov(to_p, ", ", end_p - to_p); - if (name_variant != NORMAL_PART_NAME) + if (part_type != NORMAL_PART_NAME) { - if (name_variant == TEMP_PART_NAME) + if (part_type == TEMP_PART_NAME) to_p= strnmov(to_p, ER_THD_OR_DEFAULT(thd, ER_TEMPORARY_NAME), end_p - to_p); else @@ -401,31 +380,14 @@ uint filename_to_tablename(const char *from, char *to, uint to_length DBUG_ENTER("filename_to_tablename"); DBUG_PRINT("enter", ("from '%s'", from)); - if (!strncmp(from, tmp_file_prefix, tmp_file_prefix_length)) - { - /* Temporary table name. */ - res= (strnmov(to, from, to_length) - to); - } - else + res= strconvert(&my_charset_filename, from, FN_REFLEN, + system_charset_info, to, to_length, &errors); + if (errors) // Old 5.0 name { - res= strconvert(&my_charset_filename, from, FN_REFLEN, - system_charset_info, to, to_length, &errors); - if (errors) // Old 5.0 name - { - res= (strxnmov(to, to_length, MYSQL50_TABLE_NAME_PREFIX, from, NullS) - - to); -#ifndef DBUG_OFF - if (!stay_quiet) { -#endif /* DBUG_OFF */ - sql_print_error("Invalid (old?) table or database name '%s'", from); -#ifndef DBUG_OFF - } -#endif /* DBUG_OFF */ - /* - TODO: add a stored procedure for fix table and database names, - and mention its name in error log. - */ - } + res= (strxnmov(to, to_length, MYSQL50_TABLE_NAME_PREFIX, from, NullS) - + to); + if (IF_DBUG(!stay_quiet,0)) + sql_print_error("Invalid (old?) table or database name '%s'", from); } DBUG_PRINT("exit", ("to '%s'", to)); @@ -1844,11 +1806,10 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) if (flags & WFRM_WRITE_SHADOW) { if (mysql_prepare_create_table(lpt->thd, lpt->create_info, lpt->alter_info, - /*tmp_table*/ 1, &lpt->db_options, lpt->table->file, &lpt->key_info_buffer, &lpt->key_count, C_ALTER_TABLE)) - { + { DBUG_RETURN(TRUE); } #ifdef WITH_PARTITION_STORAGE_ENGINE @@ -1871,13 +1832,23 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) #endif /* 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->alter_info->create_list, lpt->key_count, - lpt->key_info_buffer, lpt->table->file)) || - lpt->table->file->ha_create_handler_files(shadow_path, NULL, - CHF_CREATE_FLAG, - lpt->create_info)) + LEX_CUSTRING frm= build_frm_image(lpt->thd, lpt->table_name, + lpt->create_info, + lpt->alter_info->create_list, + lpt->key_count, lpt->key_info_buffer, + lpt->table->file); + if (!frm.str) + { + error= 1; + goto end; + } + + int error= writefrm(shadow_path, lpt->db, lpt->table_name, + lpt->create_info->tmp_table(), frm.str, frm.length); + my_free(const_cast<uchar*>(frm.str)); + + if (error || lpt->table->file->ha_create_partitioning_metadata(shadow_path, + NULL, CHF_CREATE_FLAG)) { mysql_file_delete(key_file_frm, shadow_frm_name, MYF(0)); error= 1; @@ -1892,12 +1863,12 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) handlers that have the main version of the frm file stored in the handler. */ - uchar *data; + const uchar *data; size_t length; if (readfrm(shadow_path, &data, &length) || packfrm(data, length, &lpt->pack_frm_data, &lpt->pack_frm_len)) { - my_free(data); + my_free(const_cast<uchar*>(data)); my_free(lpt->pack_frm_data); mem_alloc_error(length); error= 1; @@ -1915,7 +1886,7 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) */ build_table_filename(path, sizeof(path) - 1, lpt->db, lpt->table_name, "", 0); - strxmov(frm_name, path, reg_ext, NullS); + strxnmov(frm_name, sizeof(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. @@ -1928,14 +1899,14 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) */ if (mysql_file_delete(key_file_frm, frm_name, MYF(MY_WME)) || #ifdef WITH_PARTITION_STORAGE_ENGINE - lpt->table->file->ha_create_handler_files(path, shadow_path, - CHF_DELETE_FLAG, NULL) || + lpt->table->file->ha_create_partitioning_metadata(path, shadow_path, + CHF_DELETE_FLAG) || deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos) || (sync_ddl_log(), FALSE) || mysql_file_rename(key_file_frm, shadow_frm_name, frm_name, MYF(MY_WME)) || - lpt->table->file->ha_create_handler_files(path, shadow_path, - CHF_RENAME_FLAG, NULL)) + lpt->table->file->ha_create_partitioning_metadata(path, shadow_path, + CHF_RENAME_FLAG)) #else mysql_file_rename(key_file_frm, shadow_frm_name, frm_name, MYF(MY_WME))) @@ -2093,13 +2064,6 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, if (lock_table_names(thd, tables, NULL, thd->variables.lock_wait_timeout, 0)) DBUG_RETURN(true); - for (table= tables; table; table= table->next_local) - { - if (is_temporary_table(table)) - continue; - tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name, - false); - } } else { @@ -2315,8 +2279,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, bool is_trans; char *db=table->db; size_t db_length= table->db_length; - handlerton *table_type; - enum legacy_db_type frm_db_type= DB_TYPE_UNKNOWN; + handlerton *table_type= 0; DBUG_PRINT("table", ("table_l: '%s'.'%s' table: 0x%lx s: 0x%lx", table->db, table->table_name, (long) table->table, @@ -2396,29 +2359,14 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, { non_temp_tables_count++; - if (thd->locked_tables_mode) - { - if (wait_while_table_is_used(thd, table->table, HA_EXTRA_NOT_USED)) - { - error= -1; - goto err; - } - close_all_tables_for_name(thd, table->table->s, - HA_EXTRA_PREPARE_FOR_DROP, NULL); - table->table= 0; - } - - /* Check that we have an exclusive lock on the table to be dropped. */ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db, table->table_name, - MDL_EXCLUSIVE)); + MDL_SHARED)); alias= (lower_case_table_names == 2) ? table->alias : table->table_name; /* remove .frm file and engine files */ path_length= build_table_filename(path, sizeof(path) - 1, db, alias, - reg_ext, - table->internal_tmp_table ? - FN_IS_TMP : 0); + reg_ext, 0); /* This handles the case where a "DROP" was executed and a regular @@ -2451,14 +2399,9 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, } } DEBUG_SYNC(thd, "rm_table_no_locks_before_delete_table"); - DBUG_EXECUTE_IF("sleep_before_no_locks_delete_table", - my_sleep(100000);); error= 0; - if (drop_temporary || - ((access(path, F_OK) && - ha_create_table_from_engine(thd, db, alias)) || - (!drop_view && - dd_frm_type(thd, path, &frm_db_type) != FRMTYPE_TABLE))) + if ((drop_temporary || !ha_table_exists(thd, db, alias, &table_type) || + (!drop_view && table_type == view_pseudo_hton))) { /* One of the following cases happened: @@ -2489,29 +2432,46 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, { char *end; /* - Cannot use the db_type from the table, since that might have changed - while waiting for the exclusive name lock. + It could happen that table's share in the table_def_cache + is the only thing that keeps the engine plugin loaded + (if it is uninstalled and waits for the ref counter to drop to 0). + + In this case, the tdc_remove_table() below will release and unload + the plugin. And ha_delete_table() will get a dangling pointer. + + Let's lock the plugin till the end of the statement. */ - if (frm_db_type == DB_TYPE_UNKNOWN) + if (table_type && table_type != view_pseudo_hton) + ha_lock_engine(thd, table_type); + + if (thd->locked_tables_mode) { - dd_frm_type(thd, path, &frm_db_type); - DBUG_PRINT("info", ("frm_db_type %d from %s", frm_db_type, path)); + if (wait_while_table_is_used(thd, table->table, HA_EXTRA_NOT_USED, + TDC_RT_REMOVE_NOT_OWN_AND_MARK_NOT_USABLE)) + { + error= -1; + goto err; + } + /* the following internally does TDC_RT_REMOVE_ALL */ + close_all_tables_for_name(thd, table->table->s, + HA_EXTRA_PREPARE_FOR_DROP, NULL); + table->table= 0; } - table_type= ha_resolve_by_legacy_type(thd, frm_db_type); + else + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name, + false); + + /* Check that we have an exclusive lock on the table to be dropped. */ + DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db, + table->table_name, + MDL_EXCLUSIVE)); + // Remove extension for delete *(end= path + path_length - reg_ext_length)= '\0'; - DBUG_PRINT("info", ("deleting table of type %d", - (table_type ? table_type->db_type : 0))); + error= ha_delete_table(thd, table_type, path, db, table->table_name, !dont_log_query); - /* No error if non existent table and 'IF EXIST' clause or view */ - if ((error == ENOENT || error == HA_ERR_NO_SUCH_TABLE) && - (if_exists || table_type == NULL)) - { - error= 0; - thd->clear_error(); - } if (error == HA_ERR_ROW_IS_REFERENCED) { /* the table is referenced by a foreign key constraint */ @@ -2519,18 +2479,29 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, } if (!error || error == ENOENT || error == HA_ERR_NO_SUCH_TABLE) { - int new_error; + int frm_delete_error, trigger_drop_error= 0; /* Delete the table definition file */ strmov(end,reg_ext); - if (!(new_error= mysql_file_delete(key_file_frm, path, MYF(MY_WME)))) + frm_delete_error= mysql_file_delete(key_file_frm, path, MYF(MY_WME)); + if (frm_delete_error) + frm_delete_error= my_errno; + else { non_tmp_table_deleted= TRUE; - new_error= Table_triggers_list::drop_all_triggers(thd, db, - table->table_name); + trigger_drop_error= + Table_triggers_list::drop_all_triggers(thd, db, table->table_name); + } + + if (trigger_drop_error || + (frm_delete_error && frm_delete_error != ENOENT)) + error= 1; + else if (!frm_delete_error || !error || if_exists) + { + error= 0; + thd->clear_error(); } - error|= new_error; } - non_tmp_error= error ? TRUE : non_tmp_error; + non_tmp_error= error ? TRUE : non_tmp_error; } if (error) { @@ -2540,6 +2511,13 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, wrong_tables.append('.'); wrong_tables.append(table->table_name); } + else + { + PSI_CALL_drop_table_share(false, table->db, table->db_length, + table->table_name, table->table_name_length); + mysql_audit_drop_table(thd, table); + } + DBUG_PRINT("table", ("table: 0x%lx s: 0x%lx", (long) table->table, table->table ? (long) table->table->s : (long) -1)); @@ -2677,11 +2655,18 @@ bool quick_rm_table(THD *thd, handlerton *base, const char *db, handler *file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root, base); if (!file) DBUG_RETURN(true); - (void) file->ha_create_handler_files(path, NULL, CHF_DELETE_FLAG, NULL); + (void) file->ha_create_partitioning_metadata(path, NULL, CHF_DELETE_FLAG); delete file; } if (!(flags & (FRM_ONLY|NO_HA_TABLE))) error|= ha_delete_table(current_thd, base, path, db, table_name, 0); + + if (likely(error == 0)) + { + PSI_CALL_drop_table_share(flags & FN_IS_TMP, db, strlen(db), + table_name, strlen(table_name)); + } + DBUG_RETURN(error); } @@ -2947,6 +2932,8 @@ int prepare_create_field(Create_field *sql_field, case MYSQL_TYPE_NEWDATE: case MYSQL_TYPE_TIME: case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIME2: + case MYSQL_TYPE_DATETIME2: case MYSQL_TYPE_NULL: sql_field->pack_flag=f_settype((uint) sql_field->sql_type); break; @@ -2965,6 +2952,7 @@ int prepare_create_field(Create_field *sql_field, (sql_field->decimals << FIELDFLAG_DEC_SHIFT)); break; case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_TIMESTAMP2: /* fall-through */ default: sql_field->pack_flag=(FIELDFLAG_NUMBER | @@ -3035,7 +3023,7 @@ void promote_first_timestamp_column(List<Create_field> *column_definitions) while ((column_definition= it++) != NULL) { - if (column_definition->sql_type == MYSQL_TYPE_TIMESTAMP || // TIMESTAMP + if (is_timestamp_type(column_definition->sql_type) || // TIMESTAMP column_definition->unireg_check == Field::TIMESTAMP_OLD_FIELD) // Legacy { if ((column_definition->flags & NOT_NULL_FLAG) != 0 && // NOT NULL, @@ -3063,12 +3051,12 @@ void promote_first_timestamp_column(List<Create_field> *column_definitions) thd Thread object. create_info Create information (like MAX_ROWS). alter_info List of columns and indexes to create - tmp_table If a temporary table is to be created. db_options INOUT Table options (like HA_OPTION_PACK_RECORD). file The handler for the new table. key_info_buffer OUT An array of KEY structs for the indexes. key_count OUT The number of elements in the array. - select_field_count The number of fields coming from a select table. + create_table_mode C_ORDINARY_CREATE, C_ALTER_TABLE, + C_CREATE_SELECT, C_ASSISTED_DISCOVERY DESCRIPTION Prepares the table and key structures for table creation. @@ -3083,9 +3071,7 @@ void promote_first_timestamp_column(List<Create_field> *column_definitions) static int mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, - Alter_info *alter_info, - bool tmp_table, - uint *db_options, + Alter_info *alter_info, uint *db_options, handler *file, KEY **key_info_buffer, uint *key_count, int create_table_mode) { @@ -3101,6 +3087,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, List_iterator<Create_field> it2(alter_info->create_list); uint total_uneven_bit_length= 0; int select_field_count= C_CREATE_SELECT(create_table_mode); + bool tmp_table= create_table_mode == C_ALTER_TABLE; DBUG_ENTER("mysql_prepare_create_table"); select_field_pos= alter_info->create_list.elements - select_field_count; @@ -3387,8 +3374,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, sql_field->offset= record_offset; if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER) auto_increment++; - if (parse_option_list(thd, &sql_field->option_struct, - sql_field->option_list, + if (parse_option_list(thd, create_info->db_type, &sql_field->option_struct, + &sql_field->option_list, create_info->db_type->field_options, FALSE, thd->mem_root)) DBUG_RETURN(TRUE); @@ -3418,15 +3405,13 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (auto_increment && (file->ha_table_flags() & HA_NO_AUTO_INCREMENT)) { - my_message(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT, - ER(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT), MYF(0)); + my_error(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT, MYF(0), file->table_type()); DBUG_RETURN(TRUE); } if (blob_columns && (file->ha_table_flags() & HA_NO_BLOBS)) { - my_message(ER_TABLE_CANT_HANDLE_BLOB, ER(ER_TABLE_CANT_HANDLE_BLOB), - MYF(0)); + my_error(ER_TABLE_CANT_HANDLE_BLOB, MYF(0), file->table_type()); DBUG_RETURN(TRUE); } @@ -3592,8 +3577,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, key_info->usable_key_parts= key_number; key_info->algorithm= key->key_create_info.algorithm; key_info->option_list= key->option_list; - if (parse_option_list(thd, &key_info->option_struct, - key_info->option_list, + if (parse_option_list(thd, create_info->db_type, &key_info->option_struct, + &key_info->option_list, create_info->db_type->index_options, FALSE, thd->mem_root)) DBUG_RETURN(TRUE); @@ -3602,8 +3587,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, { if (!(file->ha_table_flags() & HA_CAN_FULLTEXT)) { - my_message(ER_TABLE_CANT_HANDLE_FT, ER(ER_TABLE_CANT_HANDLE_FT), - MYF(0)); + my_error(ER_TABLE_CANT_HANDLE_FT, MYF(0), file->table_type()); DBUG_RETURN(TRUE); } } @@ -3620,8 +3604,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, { if (!(file->ha_table_flags() & HA_CAN_RTREEKEYS)) { - my_message(ER_TABLE_CANT_HANDLE_SPKEYS, ER(ER_TABLE_CANT_HANDLE_SPKEYS), - MYF(0)); + my_error(ER_TABLE_CANT_HANDLE_SPKEYS, MYF(0), file->table_type()); DBUG_RETURN(TRUE); } if (key_info->user_defined_key_parts != 1) @@ -3736,7 +3719,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, { if (!(file->ha_table_flags() & HA_CAN_INDEX_BLOBS)) { - my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name.str); + my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name.str, + file->table_type()); DBUG_RETURN(TRUE); } if (f_is_geom(sql_field->pack_flag) && sql_field->geom_type == @@ -3858,7 +3842,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, } else if (length == 0 && (sql_field->flags & NOT_NULL_FLAG)) { - my_error(ER_WRONG_KEY_COLUMN, MYF(0), column->field_name.str); + my_error(ER_WRONG_KEY_COLUMN, MYF(0), file->table_type(), + column->field_name.str); DBUG_RETURN(TRUE); } if (length > file->max_key_part_length() && key->type != Key::FULLTEXT) @@ -4000,7 +3985,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (thd->variables.sql_mode & MODE_NO_ZERO_DATE && !sql_field->def && - sql_field->sql_type == MYSQL_TYPE_TIMESTAMP && + is_timestamp_type(sql_field->sql_type) && (sql_field->flags & NOT_NULL_FLAG) && (type == Field::NONE || type == Field::TIMESTAMP_UN_FIELD)) { @@ -4023,6 +4008,9 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, } } + if (create_info->tmp_table()) + create_info->options|=HA_CREATE_DELAY_KEY_WRITE; + /* Give warnings for not supported table options */ #if defined(WITH_ARIA_STORAGE_ENGINE) extern handlerton *maria_hton; @@ -4035,8 +4023,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, file->engine_name()->str, "TRANSACTIONAL=1"); - if (parse_option_list(thd, &create_info->option_struct, - create_info->option_list, + if (parse_option_list(thd, file->partition_ht(), &create_info->option_struct, + &create_info->option_list, file->partition_ht()->table_options, FALSE, thd->mem_root)) DBUG_RETURN(TRUE); @@ -4183,146 +4171,40 @@ void sp_prepare_create_field(THD *thd, Create_field *sql_field) } -/** - Check that there is no frm file for given table - - @param old_path path to the old frm file - @param path path to the frm file in new encoding - @param db database name - @param table_name table name - @param alias table name for error message (for new encoding) - @param issue_error should we issue error messages - - @retval FALSE there is no frm file - @retval TRUE there is frm file -*/ - -bool check_table_file_presence(char *old_path, - char *path, - const char *db, - const char *table_name, - const char *alias, - bool issue_error) -{ - if (!access(path,F_OK)) - { - if (issue_error) - my_error(ER_TABLE_EXISTS_ERROR,MYF(0),alias); - return TRUE; - } - { - /* - Check if file of the table in 5.0 file name encoding exists. - - Except case when it is the same table. - */ - char tbl50[FN_REFLEN]; -#ifdef _WIN32 - if (check_if_legal_tablename(table_name) != 0) - { - /* - Check for reserved device names for which access() returns 0 - (CON, AUX etc). - */ - return FALSE; - } -#endif - strxmov(tbl50, mysql_data_home, "/", db, "/", table_name, NullS); - fn_format(tbl50, tbl50, "", reg_ext, MY_UNPACK_FILENAME); - if (!access(tbl50, F_OK) && - (old_path == NULL || - strcmp(old_path, tbl50) != 0)) - { - if (issue_error) - { - strxmov(tbl50, MYSQL50_TABLE_NAME_PREFIX, table_name, NullS); - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), tbl50); - } - return TRUE; - } - } - return FALSE; -} - - -/** - Create a table - - @param thd Thread object - @param db Database - @param table_name Table name - @param path Path to table (i.e. to its .FRM file without - the extension). - @param create_info Create information (like MAX_ROWS) - @param alter_info Description of fields and keys for new table - @param internal_tmp_table Set to true if this is an internal temporary table - (From ALTER TABLE) - @param select_field_count Number of fields coming from SELECT part of - CREATE TABLE ... SELECT statement. Must be zero - for standard create of table. - @param no_ha_table Indicates that only .FRM file (and PAR file if table - is partitioned) needs to be created and not a table - in the storage engine. - @param[out] is_trans Identifies the type of engine where the table - was created: either trans or non-trans. - @param[out] key_info Array of KEY objects describing keys in table - which was created. - @param[out] key_count Number of keys in table which was created. - - If one creates a temporary table, this is automatically opened - - Note that this function assumes that caller already have taken - exclusive metadata lock on table being created or used some other - way to ensure that concurrent operations won't intervene. - mysql_create_table() is a wrapper that can be used for this. - - @retval false OK - @retval true error -*/ - -static -bool create_table_impl(THD *thd, - const char *db, const char *table_name, - const char *path, - HA_CREATE_INFO *create_info, - Alter_info *alter_info, - bool internal_tmp_table, - bool no_ha_table, - bool *is_trans, - KEY **key_info, - uint *key_count, - int create_table_mode) +handler *mysql_create_frm_image(THD *thd, + const char *db, const char *table_name, + HA_CREATE_INFO *create_info, + Alter_info *alter_info, int create_table_mode, + KEY **key_info, + uint *key_count, + LEX_CUSTRING *frm) { - const char *alias; uint db_options; - handler *file; - bool error= TRUE; - DBUG_ENTER("create_table_impl"); - DBUG_PRINT("enter", ("db: '%s' table: '%s' tmp: %d", - db, table_name, internal_tmp_table)); + handler *file; + DBUG_ENTER("mysql_create_frm_image"); - - /* Check for duplicate fields and check type of table to create */ if (!alter_info->create_list.elements) { - my_message(ER_TABLE_MUST_HAVE_COLUMNS, ER(ER_TABLE_MUST_HAVE_COLUMNS), - MYF(0)); - DBUG_RETURN(TRUE); + my_error(ER_TABLE_MUST_HAVE_COLUMNS, MYF(0)); + DBUG_RETURN(NULL); } + if (check_engine(thd, db, table_name, create_info)) - DBUG_RETURN(TRUE); + DBUG_RETURN(NULL); set_table_default_charset(thd, create_info, (char*) db); db_options= create_info->table_options; - if (create_info->row_type == ROW_TYPE_DYNAMIC) - db_options|=HA_OPTION_PACK_RECORD; - alias= table_case_name(create_info, table_name); + if (create_table_mode != C_ALTER_TABLE_FRM_ONLY && + create_info->row_type != ROW_TYPE_FIXED && + create_info->row_type != ROW_TYPE_DEFAULT) + db_options|= HA_OPTION_PACK_RECORD; + if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root, create_info->db_type))) { mem_alloc_error(sizeof(handler)); - DBUG_RETURN(TRUE); + DBUG_RETURN(NULL); } #ifdef WITH_PARTITION_STORAGE_ENGINE partition_info *part_info= thd->work_part_info; @@ -4339,7 +4221,7 @@ bool create_table_impl(THD *thd, if (!part_info) { mem_alloc_error(sizeof(partition_info)); - DBUG_RETURN(TRUE); + goto err; } file->set_auto_partitions(part_info); part_info->default_engine_type= create_info->db_type; @@ -4355,12 +4237,11 @@ bool create_table_impl(THD *thd, this information in the default_db_type variable, it is either DB_TYPE_DEFAULT or the engine set in the ALTER TABLE command. */ - Key *key; handlerton *part_engine_type= create_info->db_type; char *part_syntax_buf; uint syntax_len; handlerton *engine_type; - if (create_info->options & HA_LEX_CREATE_TMP_TABLE) + if (create_info->tmp_table()) { my_error(ER_PARTITION_NO_TEMPORARY, MYF(0)); goto err; @@ -4423,9 +4304,8 @@ bool create_table_impl(THD *thd, delete file; create_info->db_type= partition_hton; if (!(file= get_ha_partition(part_info))) - { - DBUG_RETURN(TRUE); - } + DBUG_RETURN(NULL); + /* If we have default number of partitions or subpartitions we might require to set-up the part_info object such that it @@ -4467,203 +4347,212 @@ bool create_table_impl(THD *thd, engine_type))) { mem_alloc_error(sizeof(handler)); - DBUG_RETURN(TRUE); + DBUG_RETURN(NULL); } } - /* - Unless table's storage engine supports partitioning natively - don't allow foreign keys on partitioned tables (they won't - work work even with InnoDB beneath of partitioning engine). - If storage engine handles partitioning natively (like NDB) - foreign keys support is possible, so we let the engine decide. - */ - if (create_info->db_type == partition_hton) + } + /* + Unless table's storage engine supports partitioning natively + don't allow foreign keys on partitioned tables (they won't + work work even with InnoDB beneath of partitioning engine). + If storage engine handles partitioning natively (like NDB) + foreign keys support is possible, so we let the engine decide. + */ + if (create_info->db_type == partition_hton) + { + List_iterator_fast<Key> key_iterator(alter_info->key_list); + Key *key; + while ((key= key_iterator++)) { - List_iterator_fast<Key> key_iterator(alter_info->key_list); - while ((key= key_iterator++)) + if (key->type == Key::FOREIGN_KEY) { - if (key->type == Key::FOREIGN_KEY) - { - my_error(ER_FOREIGN_KEY_ON_PARTITIONED, MYF(0)); - goto err; - } + my_error(ER_FOREIGN_KEY_ON_PARTITIONED, MYF(0)); + goto err; } } } #endif - if (mysql_prepare_create_table(thd, create_info, alter_info, - internal_tmp_table, - &db_options, file, - key_info, key_count, + if (mysql_prepare_create_table(thd, create_info, alter_info, &db_options, + file, key_info, key_count, create_table_mode)) goto err; + create_info->table_options=db_options; + + *frm= build_frm_image(thd, table_name, create_info, + alter_info->create_list, *key_count, + *key_info, file); + + if (frm->str) + DBUG_RETURN(file); + +err: + delete file; + DBUG_RETURN(NULL); +} + + +/** + Create a table + + @param thd Thread object + @param db Database + @param table_name Table name + @param path Path to table (i.e. to its .FRM file without + the extension). + @param create_info Create information (like MAX_ROWS) + @param alter_info Description of fields and keys for new table + @param create_table_mode C_ORDINARY_CREATE, C_ALTER_TABLE, C_ASSISTED_DISCOVERY + or any positive number (for C_CREATE_SELECT). + @param[out] is_trans Identifies the type of engine where the table + was created: either trans or non-trans. + @param[out] key_info Array of KEY objects describing keys in table + which was created. + @param[out] key_count Number of keys in table which was created. + + If one creates a temporary table, this is automatically opened - if (create_info->options & HA_LEX_CREATE_TMP_TABLE) - create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE; + Note that this function assumes that caller already have taken + exclusive metadata lock on table being created or used some other + way to ensure that concurrent operations won't intervene. + mysql_create_table() is a wrapper that can be used for this. - /* Check if table already exists */ - if ((create_info->options & HA_LEX_CREATE_TMP_TABLE) && - find_temporary_table(thd, db, table_name)) + @retval false OK + @retval true error +*/ + +static +bool create_table_impl(THD *thd, + const char *db, const char *table_name, + const char *path, + HA_CREATE_INFO *create_info, + Alter_info *alter_info, + int create_table_mode, + bool *is_trans, + KEY **key_info, + uint *key_count, + LEX_CUSTRING *frm) +{ + const char *alias; + handler *file= 0; + bool error= TRUE; + bool frm_only= create_table_mode == C_ALTER_TABLE_FRM_ONLY; + bool internal_tmp_table= create_table_mode == C_ALTER_TABLE || frm_only; + DBUG_ENTER("mysql_create_table_no_lock"); + DBUG_PRINT("enter", ("db: '%s' table: '%s' tmp: %d", + db, table_name, internal_tmp_table)); + + if (!my_use_symdir || (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)) { - if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) - { - push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, - ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR), - alias); - error= 0; - goto err; - } - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias); - goto err; + if (create_info->data_file_name) + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED), + "DATA DIRECTORY"); + if (create_info->index_file_name) + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED), + "INDEX DIRECTORY"); + create_info->data_file_name= create_info->index_file_name= 0; } + else + if (error_if_data_home_dir(create_info->data_file_name, "DATA DIRECTORY") || + error_if_data_home_dir(create_info->index_file_name, "INDEX DIRECTORY")|| + check_partition_dirs(thd->lex->part_info)) + goto err; - if (!internal_tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE)) - { - char frm_name[FN_REFLEN+1]; - strxnmov(frm_name, sizeof(frm_name) - 1, path, reg_ext, NullS); + alias= table_case_name(create_info, table_name); - if (!access(frm_name, F_OK)) + /* Check if table exists */ + if (create_info->tmp_table()) + { + if (find_temporary_table(thd, db, table_name)) { if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) goto warn; - my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name); + my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias); goto err; } - /* - We don't assert here, but check the result, because the table could be - in the table definition cache and in the same time the .frm could be - missing from the disk, in case of manual intervention which deletes - the .frm file. The user has to use FLUSH TABLES; to clear the cache. - Then she could create the table. This case is pretty obscure and - therefore we don't introduce a new error message only for it. - */ - mysql_mutex_lock(&LOCK_open); - if (get_cached_table_share(db, table_name)) + } + else + { + if (!internal_tmp_table && ha_table_exists(thd, db, table_name)) { - mysql_mutex_unlock(&LOCK_open); + if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) + goto warn; my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name); goto err; } - mysql_mutex_unlock(&LOCK_open); } - /* - Check that table with given name does not already - exist in any storage engine. In such a case it should - be discovered and the error ER_TABLE_EXISTS_ERROR be returned - unless user specified CREATE TABLE IF EXISTS - An exclusive metadata lock ensures that no - one else is attempting to discover the table. Since - it's not on disk as a frm file, no one could be using it! - */ - if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE)) + THD_STAGE_INFO(thd, stage_creating_table); + + if (create_table_mode == C_ASSISTED_DISCOVERY) { - bool create_if_not_exists = - create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS; - int retcode = ha_table_exists_in_engine(thd, db, table_name); - DBUG_PRINT("info", ("exists_in_engine: %u",retcode)); - switch (retcode) - { - case HA_ERR_NO_SUCH_TABLE: - /* Normal case, no table exists. we can go and create it */ - break; - case HA_ERR_TABLE_EXIST: - DBUG_PRINT("info", ("Table existed in handler")); + /* check that it's used correctly */ + DBUG_ASSERT(alter_info->create_list.elements == 0); + DBUG_ASSERT(alter_info->key_list.elements == 0); - if (create_if_not_exists) - goto warn; - my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name); - goto err; - break; - default: - DBUG_PRINT("info", ("error: %u from storage engine", retcode)); - my_error(retcode, MYF(0),table_name); - goto err; + TABLE_SHARE share; + handlerton *hton= create_info->db_type; + int ha_err; + Field *no_fields= 0; + + if (!hton->discover_table_structure) + { + my_error(ER_ILLEGAL_HA, MYF(0), hton_name(hton)->str, db, table_name); + goto err; } - } - THD_STAGE_INFO(thd, stage_creating_table); + init_tmp_table_share(thd, &share, db, 0, table_name, path); - { - size_t dirlen; - char dirpath[FN_REFLEN]; + /* prepare everything for discovery */ + share.field= &no_fields; + share.db_plugin= ha_lock_engine(thd, hton); + share.option_list= create_info->option_list; + share.connect_string= create_info->connect_string; + + if (parse_engine_table_options(thd, hton, &share)) + goto err; + + ha_err= hton->discover_table_structure(hton, thd, &share, create_info); /* - data_file_name and index_file_name include the table name without - extension. Mostly this does not refer to an existing file. When - comparing data_file_name or index_file_name against the data - directory, we try to resolve all symbolic links. On some systems, - we use realpath(3) for the resolution. This returns ENOENT if the - resolved path does not refer to an existing file. my_realpath() - does then copy the requested path verbatim, without symlink - resolution. Thereafter the comparison can fail even if the - requested path is within the data directory. E.g. if symlinks to - another file system are used. To make realpath(3) return the - resolved path, we strip the table name and compare the directory - path only. If the directory doesn't exist either, table creation - will fail anyway. + if discovery failed, the plugin will be auto-unlocked, as it + was locked on the THD, see above. + if discovery succeeded, the plugin was replaced by a globally + locked plugin, that will be unlocked by free_table_share() */ - if (create_info->data_file_name) - { - dirname_part(dirpath, create_info->data_file_name, &dirlen); - if (test_if_data_home_dir(dirpath)) - { - my_error(ER_WRONG_ARGUMENTS, MYF(0), "DATA DIRECTORY"); - goto err; - } - } - if (create_info->index_file_name) + if (ha_err) + share.db_plugin= 0; // will be auto-freed, locked above on the THD + + free_table_share(&share); + + if (ha_err) { - dirname_part(dirpath, create_info->index_file_name, &dirlen); - if (test_if_data_home_dir(dirpath)) - { - my_error(ER_WRONG_ARGUMENTS, MYF(0), "INDEX DIRECTORY"); - goto err; - } + my_error(ER_GET_ERRNO, MYF(0), ha_err, hton_name(hton)->str); + goto err; } } - -#ifdef WITH_PARTITION_STORAGE_ENGINE - if (check_partition_dirs(thd->lex->part_info)) - { - goto err; - } -#endif /* WITH_PARTITION_STORAGE_ENGINE */ - -#ifdef HAVE_READLINK - if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE) -#endif + else { - if (create_info->data_file_name) - push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, - WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED), - "DATA DIRECTORY"); - if (create_info->index_file_name) - push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, - WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED), - "INDEX DIRECTORY"); - create_info->data_file_name= create_info->index_file_name= 0; + file= mysql_create_frm_image(thd, db, table_name, create_info, alter_info, + create_table_mode, key_info, key_count, frm); + if (!file) + goto err; + if (rea_create_table(thd, frm, path, db, table_name, create_info, + file, frm_only)) + goto err; } - create_info->table_options=db_options; - - /* - Create .FRM (and .PAR file for partitioned table). - If "no_ha_table" is false also create table in storage engine. - */ - if (rea_create_table(thd, path, db, table_name, - create_info, alter_info->create_list, - *key_count, *key_info, file, no_ha_table)) - goto err; - if (!no_ha_table && create_info->options & HA_LEX_CREATE_TMP_TABLE) + if (!frm_only && create_info->tmp_table()) { /* Open a table (skipping table cache) and add it into THD::temporary_tables list. */ - TABLE *table= open_table_uncached(thd, path, db, table_name, true, true); + TABLE *table= open_table_uncached(thd, create_info->db_type, path, + db, table_name, true, true); if (!table) { @@ -4677,7 +4566,7 @@ bool create_table_impl(THD *thd, thd->thread_specific_used= TRUE; } #ifdef WITH_PARTITION_STORAGE_ENGINE - else if (part_info && no_ha_table) + else if (thd->work_part_info && frm_only) { /* For partitioned tables we can't find some problems with table @@ -4694,7 +4583,7 @@ bool create_table_impl(THD *thd, init_tmp_table_share(thd, &share, db, 0, table_name, path); - bool result= (open_table_def(thd, &share, 0) || + bool result= (open_table_def(thd, &share, GTS_TABLE) || open_table_from_share(thd, &share, "", 0, (uint) READ_ALL, 0, &table, true)); if (!result) @@ -4704,11 +4593,10 @@ bool create_table_impl(THD *thd, if (result) { - char frm_name[FN_REFLEN + 1]; - strxnmov(frm_name, sizeof(frm_name) - 1, path, reg_ext, NullS); + char frm_name[FN_REFLEN]; + strxnmov(frm_name, sizeof(frm_name), path, reg_ext, NullS); (void) mysql_file_delete(key_file_frm, frm_name, MYF(0)); - (void) file->ha_create_handler_files(path, NULL, CHF_DELETE_FLAG, - create_info); + (void) file->ha_create_partitioning_metadata(path, NULL, CHF_DELETE_FLAG); goto err; } } @@ -4728,7 +4616,6 @@ warn: goto err; } - /** Simple wrapper around create_table_impl() to be used in various version of CREATE TABLE statement. @@ -4742,8 +4629,9 @@ bool mysql_create_table_no_lock(THD *thd, KEY *not_used_1; uint not_used_2; char path[FN_REFLEN + 1]; + LEX_CUSTRING frm= {0,0}; - if (create_info->options & HA_LEX_CREATE_TMP_TABLE) + if (create_info->tmp_table()) build_tmptable_filename(thd, path, sizeof(path)); else { @@ -4759,12 +4647,13 @@ bool mysql_create_table_no_lock(THD *thd, } } - return create_table_impl(thd, db, table_name, path, create_info, alter_info, - false, false, is_trans, - ¬_used_1, ¬_used_2, create_table_mode); + bool res= create_table_impl(thd, db, table_name, path, create_info, + alter_info, create_table_mode, is_trans, + ¬_used_1, ¬_used_2, &frm); + my_free(const_cast<uchar*>(frm.str)); + return res; } - /** Implementation of SQLCOM_CREATE_TABLE. @@ -4779,19 +4668,17 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, HA_CREATE_INFO *create_info, Alter_info *alter_info) { - bool result; + const char *db= create_table->db; + const char *table_name= create_table->table_name; bool is_trans= FALSE; int create_table_mode; DBUG_ENTER("mysql_create_table"); - /* - Open or obtain an exclusive metadata lock on table being created. - */ + /* Open or obtain an exclusive metadata lock on table being created */ if (open_and_lock_tables(thd, thd->lex->query_tables, FALSE, 0)) { /* is_error() may be 0 if table existed and we generated a warning */ - result= thd->is_error(); - goto end; + DBUG_RETURN(thd->is_error()); } /* Got lock. */ @@ -4803,20 +4690,16 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, create_table_mode= C_ASSISTED_DISCOVERY; promote_first_timestamp_column(&alter_info->create_list); - result= mysql_create_table_no_lock(thd, create_table->db, - create_table->table_name, create_info, - alter_info, &is_trans, create_table_mode); - if (result) - DBUG_RETURN(result); + if (mysql_create_table_no_lock(thd, db, table_name, create_info, alter_info, + &is_trans, create_table_mode)) + DBUG_RETURN(1); /* In RBR we don't need to log CREATE TEMPORARY TABLE */ if (thd->is_current_stmt_binlog_format_row() && create_info->tmp_table()) DBUG_RETURN(0); + bool result; result= write_bin_log(thd, TRUE, thd->query(), thd->query_length(), is_trans); - thd->abort_on_warning= false; - -end: DBUG_RETURN(result); } @@ -4940,16 +4823,20 @@ mysql_rename_table(handlerton *base, const char *old_db, { if (rename_file_ext(from,to,reg_ext)) error= my_errno; - (void) file->ha_create_handler_files(to, from, CHF_RENAME_FLAG, NULL); + (void) file->ha_create_partitioning_metadata(to, from, CHF_RENAME_FLAG); } else if (!file || !(error=file->ha_rename_table(from_base, to_base))) { if (!(flags & NO_FRM_RENAME) && rename_file_ext(from,to,reg_ext)) { error=my_errno; - /* Restore old file name */ if (file) - file->ha_rename_table(to_base, from_base); + { + if (error == ENOENT) + error= 0; // this is ok if file->ha_rename_table() succeeded + else + file->ha_rename_table(to_base, from_base); // Restore old file name + } } } delete file; @@ -4957,20 +4844,19 @@ mysql_rename_table(handlerton *base, const char *old_db, my_error(ER_NOT_SUPPORTED_YET, MYF(0), "ALTER TABLE"); else if (error) my_error(ER_ERROR_ON_RENAME, MYF(0), from, to, error); + else if (!(flags & FN_IS_TMP)) + mysql_audit_rename_table(thd, old_db, old_name, new_db, new_name); - -#ifdef HAVE_PSI_TABLE_INTERFACE /* Remove the old table share from the pfs table share array. The new table share will be created when the renamed table is first accessed. */ if (likely(error == 0)) { - my_bool temp_table= (my_bool)is_prefix(old_name, tmp_file_prefix); - PSI_TABLE_CALL(drop_table_share) - (temp_table, old_db, strlen(old_db), old_name, strlen(old_name)); + PSI_CALL_drop_table_share(flags & FN_FROM_IS_TMP, + old_db, strlen(old_db), + old_name, strlen(old_name)); } -#endif DBUG_RETURN(error != 0); } @@ -5049,7 +4935,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, local_create_info.options|= create_info->options&HA_LEX_CREATE_IF_NOT_EXISTS; /* Replace type of source table with one specified in the statement. */ local_create_info.options&= ~HA_LEX_CREATE_TMP_TABLE; - local_create_info.options|= create_info->options & HA_LEX_CREATE_TMP_TABLE; + local_create_info.options|= create_info->tmp_table(); /* Reset auto-increment counter for the new table. */ local_create_info.auto_increment_value= 0; /* @@ -5067,7 +4953,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, Ensure that we have an exclusive lock on target table if we are creating non-temporary table. */ - DBUG_ASSERT((create_info->options & HA_LEX_CREATE_TMP_TABLE) || + DBUG_ASSERT((create_info->tmp_table()) || thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db, table->table_name, MDL_EXCLUSIVE)); @@ -5094,7 +4980,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, 4 temporary temporary Nothing ==== ========= ========= ============================== */ - if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE)) + if (!(create_info->tmp_table())) { if (src_table->table->s->tmp_table) // Case 2 { @@ -5172,6 +5058,8 @@ int mysql_discard_or_import_tablespace(THD *thd, int error; DBUG_ENTER("mysql_discard_or_import_tablespace"); + mysql_audit_alter_table(thd, table_list); + /* Note that DISCARD/IMPORT TABLESPACE always is the only operation in an ALTER TABLE @@ -5255,11 +5143,248 @@ static bool is_candidate_key(KEY *key) if (key_part->key_part_flag & HA_PART_KEY_SEG) return false; } - return true; } +/* + Preparation for table creation + + SYNOPSIS + handle_if_exists_option() + thd Thread object. + table The altered table. + alter_info List of columns and indexes to create + + DESCRIPTION + Looks for the IF [NOT] EXISTS options, checks the states and remove items + from the list if existing found. + + RETURN VALUES + NONE +*/ + +static void +handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info) +{ + Field **f_ptr; + DBUG_ENTER("handle_if_exists_option"); + + /* Handle ADD COLUMN IF NOT EXISTS. */ + { + List_iterator<Create_field> it(alter_info->create_list); + Create_field *sql_field; + + while ((sql_field=it++)) + { + if (!sql_field->create_if_not_exists || sql_field->change) + continue; + /* + If there is a field with the same name in the table already, + remove the sql_field from the list. + */ + for (f_ptr=table->field; *f_ptr; f_ptr++) + { + if (my_strcasecmp(system_charset_info, + sql_field->field_name, (*f_ptr)->field_name) == 0) + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_DUP_FIELDNAME, ER(ER_DUP_FIELDNAME), + sql_field->field_name); + it.remove(); + if (alter_info->create_list.is_empty()) + { + alter_info->flags&= ~Alter_info::ALTER_ADD_COLUMN; + if (alter_info->key_list.is_empty()) + alter_info->flags&= ~Alter_info::ALTER_ADD_INDEX; + } + break; + } + } + } + } + + /* Handle MODIFY COLUMN IF EXISTS. */ + { + List_iterator<Create_field> it(alter_info->create_list); + Create_field *sql_field; + + while ((sql_field=it++)) + { + if (!sql_field->create_if_not_exists || !sql_field->change) + continue; + /* + If there is NO field with the same name in the table already, + remove the sql_field from the list. + */ + for (f_ptr=table->field; *f_ptr; f_ptr++) + { + if (my_strcasecmp(system_charset_info, + sql_field->field_name, (*f_ptr)->field_name) == 0) + { + break; + } + } + if (*f_ptr == NULL) + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_BAD_FIELD_ERROR, ER(ER_BAD_FIELD_ERROR), + sql_field->change, table->s->table_name.str); + it.remove(); + if (alter_info->create_list.is_empty()) + { + alter_info->flags&= ~(Alter_info::ALTER_ADD_COLUMN | + Alter_info::ALTER_CHANGE_COLUMN); + if (alter_info->key_list.is_empty()) + alter_info->flags&= ~Alter_info::ALTER_ADD_INDEX; + } + } + } + } + + /* Handle DROP COLUMN/KEY IF EXISTS. */ + { + List_iterator<Alter_drop> drop_it(alter_info->drop_list); + Alter_drop *drop; + bool remove_drop; + while ((drop= drop_it++)) + { + if (!drop->drop_if_exists) + continue; + remove_drop= TRUE; + if (drop->type == Alter_drop::COLUMN) + { + /* + If there is NO field with that name in the table, + remove the 'drop' from the list. + */ + for (f_ptr=table->field; *f_ptr; f_ptr++) + { + if (my_strcasecmp(system_charset_info, + drop->name, (*f_ptr)->field_name) == 0) + { + remove_drop= FALSE; + break; + } + } + } + else /* Alter_drop::KEY */ + { + uint n_key; + for (n_key=0; n_key < table->s->keys; n_key++) + { + if (my_strcasecmp(system_charset_info, + drop->name, table->key_info[n_key].name) == 0) + { + remove_drop= FALSE; + break; + } + } + } + if (remove_drop) + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_CANT_DROP_FIELD_OR_KEY, ER(ER_CANT_DROP_FIELD_OR_KEY), + drop->name); + drop_it.remove(); + if (alter_info->drop_list.is_empty()) + alter_info->flags&= ~(Alter_info::ALTER_DROP_COLUMN | + Alter_info::ALTER_DROP_INDEX); + } + } + } + + /* ALTER TABLE ADD KEY IF NOT EXISTS */ + /* ALTER TABLE ADD FOREIGN KEY IF NOT EXISTS */ + { + Key *key; + List_iterator<Key> key_it(alter_info->key_list); + uint n_key; + while ((key=key_it++)) + { + if (!key->create_if_not_exists) + continue; + for (n_key=0; n_key < table->s->keys; n_key++) + { + if (my_strcasecmp(system_charset_info, + key->name.str, table->key_info[n_key].name) == 0) + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_DUP_KEYNAME, ER(ER_DUP_KEYNAME), key->name.str); + key_it.remove(); + if (key->type == Key::FOREIGN_KEY) + { + /* ADD FOREIGN KEY appends two items. */ + key_it.remove(); + } + if (alter_info->key_list.is_empty()) + alter_info->flags&= ~Alter_info::ALTER_ADD_INDEX; + break; + } + } + } + } + +#ifdef WITH_PARTITION_STORAGE_ENGINE + partition_info *tab_part_info= table->part_info; + if (tab_part_info && thd->lex->check_exists) + { + /* ALTER TABLE ADD PARTITION IF NOT EXISTS */ + if (alter_info->flags & Alter_info::ALTER_ADD_PARTITION) + { + partition_info *alt_part_info= thd->lex->part_info; + if (alt_part_info) + { + List_iterator<partition_element> new_part_it(alt_part_info->partitions); + partition_element *pe; + while ((pe= new_part_it++)) + { + if (!tab_part_info->has_unique_name(pe)) + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_SAME_NAME_PARTITION, ER(ER_SAME_NAME_PARTITION), + pe->partition_name); + alter_info->flags&= ~Alter_info::ALTER_ADD_PARTITION; + thd->lex->part_info= NULL; + break; + } + } + } + } + /* ALTER TABLE DROP PARTITION IF EXISTS */ + if (alter_info->flags & Alter_info::ALTER_DROP_PARTITION) + { + List_iterator<char> names_it(alter_info->partition_names); + char *name; + + while ((name= names_it++)) + { + List_iterator<partition_element> part_it(tab_part_info->partitions); + partition_element *part_elem; + while ((part_elem= part_it++)) + { + if (my_strcasecmp(system_charset_info, + part_elem->partition_name, name) == 0) + break; + } + if (!part_elem) + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_DROP_PARTITION_NON_EXISTENT, + ER(ER_DROP_PARTITION_NON_EXISTENT), "DROP"); + names_it.remove(); + } + } + if (alter_info->partition_names.elements == 0) + alter_info->flags&= ~Alter_info::ALTER_DROP_PARTITION; + } + } +#endif /*WITH_PARTITION_STORAGE_ENGINE*/ + + DBUG_VOID_RETURN; +} + + /** Get Create_field object for newly created table by field index. @@ -5868,10 +5993,9 @@ bool mysql_compare_tables(TABLE *table, int create_table_mode= table->s->tmp_table == NO_TMP_TABLE ? C_ORDINARY_CREATE : C_ALTER_TABLE; if (mysql_prepare_create_table(thd, create_info, &tmp_alter_info, - (table->s->tmp_table != NO_TMP_TABLE), &db_options, table->file, &key_info_buffer, &key_count, create_table_mode)) - DBUG_RETURN(true); + DBUG_RETURN(1); /* Some very basic checks. */ if (table->s->fields != alter_info->create_list.elements || @@ -6012,6 +6136,7 @@ bool alter_table_manage_keys(TABLE *table, int indexes_were_disabled, switch (keys_onoff) { case Alter_info::ENABLE: + DEBUG_SYNC(table->in_use, "alter_table_enable_indexes"); error= table->file->ha_enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); break; case Alter_info::LEAVE_AS_IS: @@ -6028,7 +6153,6 @@ bool alter_table_manage_keys(TABLE *table, int indexes_were_disabled, ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA), table->file->table_type(), table->s->db.str, table->s->table_name.str); - error= 0; } else if (error) table->file->print_error(error, MYF(0)); @@ -6684,7 +6808,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, */ if ((def->sql_type == MYSQL_TYPE_DATE || def->sql_type == MYSQL_TYPE_NEWDATE || - def->sql_type == MYSQL_TYPE_DATETIME) && + def->sql_type == MYSQL_TYPE_DATETIME || + def->sql_type == MYSQL_TYPE_DATETIME2) && !alter_ctx->datetime_field && !(~def->flags & (NO_DEFAULT_VALUE_FLAG | NOT_NULL_FLAG)) && thd->variables.sql_mode & MODE_NO_ZERO_DATE) @@ -6902,7 +7027,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, key= new Key(key_type, key_name, strlen(key_name), &key_create_info, test(key_info->flags & HA_GENERATED_KEY), - key_parts, key_info->option_list); + key_parts, key_info->option_list, FALSE); new_key_list.push_back(key); } } @@ -7683,6 +7808,9 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, DBUG_RETURN(true); } + if (table->s->tmp_table == NO_TMP_TABLE) + mysql_audit_alter_table(thd, table_list); + THD_STAGE_INFO(thd, stage_setup); if (!(alter_info->flags & ~(Alter_info::ALTER_RENAME | Alter_info::ALTER_KEYS_ONOFF)) && @@ -7698,9 +7826,23 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, "LOCK=NONE/SHARED", "LOCK=EXCLUSIVE"); DBUG_RETURN(true); } - DBUG_RETURN(simple_rename_or_index_change(thd, table_list, - alter_info->keys_onoff, - &alter_ctx)); + bool res= simple_rename_or_index_change(thd, table_list, + alter_info->keys_onoff, + &alter_ctx); + DBUG_RETURN(res); + } + + handle_if_exists_options(thd, table, alter_info); + + /* Look if we have to do anything at all. */ + /* Normally ALTER can become NOOP only after handling */ + /* the IF (NOT) EXISTS options. */ + if (alter_info->flags == 0) + { + my_snprintf(alter_ctx.tmp_name, sizeof(alter_ctx.tmp_name), + ER(ER_INSERT_INFO), 0L, 0L, 0L); + my_ok(thd, 0L, 0L, alter_ctx.tmp_name); + DBUG_RETURN(false); } /* We have to do full alter table. */ @@ -7871,8 +8013,6 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, } DEBUG_SYNC(thd, "alter_table_before_create_table_no_lock"); - DBUG_EXECUTE_IF("sleep_before_create_table_no_lock", - my_sleep(100000);); /* We can abort alter table for any table type */ thd->abort_on_warning= !ignore && thd->is_strict_mode(); @@ -7892,17 +8032,22 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, mysql_prepare_create_table(). */ bool varchar= create_info->varchar; + LEX_CUSTRING frm= {0,0}; tmp_disable_binlog(thd); + create_info->options|=HA_CREATE_TMP_ALTER; error= create_table_impl(thd, alter_ctx.new_db, alter_ctx.tmp_name, alter_ctx.get_tmp_path(), create_info, alter_info, - true, true, NULL, - &key_info, &key_count, FALSE); + C_ALTER_TABLE_FRM_ONLY, NULL, + &key_info, &key_count, &frm); reenable_binlog(thd); thd->abort_on_warning= false; if (error) + { + my_free(const_cast<uchar*>(frm.str)); DBUG_RETURN(true); + } /* Remember that we have not created table in storage engine yet. */ bool no_ha_table= true; @@ -7924,10 +8069,32 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, if (fill_alter_inplace_info(thd, table, varchar, &ha_alter_info)) goto err_new_table_cleanup; + if (ha_alter_info.handler_flags == 0) + { + /* + No-op ALTER, no need to call handler API functions. + + If this code path is entered for an ALTER statement that + should not be a real no-op, new handler flags should be added + and fill_alter_inplace_info() adjusted. + + Note that we can end up here if an ALTER statement has clauses + that cancel each other out (e.g. ADD/DROP identically index). + + Also note that we ignore the LOCK clause here. + + TODO don't create the frm in the first place + */ + deletefrm(alter_ctx.get_tmp_path()); + my_free(const_cast<uchar*>(frm.str)); + goto end_inplace; + } + // We assume that the table is non-temporary. DBUG_ASSERT(!table->s->tmp_table); - if (!(altered_table= open_table_uncached(thd, alter_ctx.get_tmp_path(), + if (!(altered_table= open_table_uncached(thd, new_db_type, + alter_ctx.get_tmp_path(), alter_ctx.new_db, alter_ctx.tmp_name, true, false))) @@ -7944,24 +8111,6 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, altered_table->column_bitmaps_set_no_signal(&altered_table->s->all_set, &altered_table->s->all_set); - if (ha_alter_info.handler_flags == 0) - { - /* - No-op ALTER, no need to call handler API functions. - - If this code path is entered for an ALTER statement that - should not be a real no-op, new handler flags should be added - and fill_alter_inplace_info() adjusted. - - Note that we can end up here if an ALTER statement has clauses - that cancel each other out (e.g. ADD/DROP identically index). - - Also note that we ignore the LOCK clause here. - */ - close_temporary_table(thd, altered_table, true, false); - goto end_inplace; - } - // Ask storage engine whether to use copy or in-place enum_alter_inplace_result inplace_supported= table->file->check_if_supported_inplace_alter(altered_table, @@ -8032,6 +8181,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, if (use_inplace) { + my_free(const_cast<uchar*>(frm.str)); if (mysql_inplace_alter_table(thd, table_list, table, altered_table, &ha_alter_info, @@ -8091,15 +8241,16 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, { if (ha_create_table(thd, alter_ctx.get_tmp_path(), alter_ctx.new_db, alter_ctx.tmp_name, - create_info, false)) + create_info, &frm)) goto err_new_table_cleanup; /* Mark that we have created table in storage engine. */ no_ha_table= false; - if (create_info->options & HA_LEX_CREATE_TMP_TABLE) + if (create_info->tmp_table()) { - if (!open_table_uncached(thd, alter_ctx.get_tmp_path(), + if (!open_table_uncached(thd, new_db_type, + alter_ctx.get_tmp_path(), alter_ctx.new_db, alter_ctx.tmp_name, true, true)) goto err_new_table_cleanup; @@ -8122,7 +8273,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, { /* table is a normal table: Create temporary table in same directory */ /* Open our intermediate table. */ - new_table= open_table_uncached(thd, alter_ctx.get_tmp_path(), + new_table= open_table_uncached(thd, new_db_type, alter_ctx.get_tmp_path(), alter_ctx.new_db, alter_ctx.tmp_name, true, true); } @@ -8202,6 +8353,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, if (!thd->is_current_stmt_binlog_format_row() && write_bin_log(thd, true, thd->query(), thd->query_length())) DBUG_RETURN(true); + my_free(const_cast<uchar*>(frm.str)); goto end_temporary; } @@ -8243,6 +8395,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, HA_EXTRA_NOT_USED, NULL); table_list->table= table= NULL; /* Safety */ + my_free(const_cast<uchar*>(frm.str)); /* Rename the old table to temporary name to have a backup in case @@ -8316,7 +8469,6 @@ end_inplace: THD_STAGE_INFO(thd, stage_end); - DBUG_EXECUTE_IF("sleep_alter_before_main_binlog", my_sleep(6000000);); DEBUG_SYNC(thd, "alter_table_before_main_binlog"); ha_binlog_log_query(thd, create_info->db_type, LOGCOM_ALTER_TABLE, @@ -8325,7 +8477,7 @@ end_inplace: DBUG_ASSERT(!(mysql_bin_log.is_open() && thd->is_current_stmt_binlog_format_row() && - (create_info->options & HA_LEX_CREATE_TMP_TABLE))); + (create_info->tmp_table()))); if (write_bin_log(thd, true, thd->query(), thd->query_length())) DBUG_RETURN(true); @@ -8337,7 +8489,7 @@ end_inplace: shutdown. But we do not need to attach MERGE children. */ TABLE *t_table; - t_table= open_table_uncached(thd, alter_ctx.get_new_path(), + t_table= open_table_uncached(thd, new_db_type, alter_ctx.get_new_path(), alter_ctx.new_db, alter_ctx.new_name, false, true); if (t_table) @@ -8368,6 +8520,7 @@ end_temporary: DBUG_RETURN(false); err_new_table_cleanup: + my_free(const_cast<uchar*>(frm.str)); if (new_table) { /* close_temporary_table() frees the new_table pointer. */ @@ -8397,6 +8550,7 @@ err_new_table_cleanup: t_type= MYSQL_TIMESTAMP_DATE; break; case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_DATETIME2: f_val= "0000-00-00 00:00:00"; t_type= MYSQL_TIMESTAMP_DATETIME; break; @@ -8508,10 +8662,10 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, if (!(copy= new Copy_field[to->s->fields])) DBUG_RETURN(-1); /* purecov: inspected */ + /* We need external lock before we can disable/enable keys */ if (to->file->ha_external_lock(thd, F_WRLCK)) DBUG_RETURN(-1); - /* We need external lock before we can disable/enable keys */ alter_table_manage_keys(to, from->file->indexes_are_disabled(), keys_onoff); /* We can abort alter table for any table type */ @@ -8996,7 +9150,7 @@ static bool check_engine(THD *thd, const char *db_name, ha_resolve_storage_engine_name(*new_engine), table_name); } - if (create_info->options & HA_LEX_CREATE_TMP_TABLE && + if (create_info->tmp_table() && ha_check_storage_engine_flag(*new_engine, HTON_TEMPORARY_NOT_SUPPORTED)) { if (create_info->used_fields & HA_CREATE_USED_ENGINE) |