diff options
Diffstat (limited to 'sql/sql_table.cc')
-rw-r--r-- | sql/sql_table.cc | 1289 |
1 files changed, 757 insertions, 532 deletions
diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 54e382a6c24..18b34e4a212 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -33,7 +33,6 @@ // partition_info // NOT_A_PARTITION_ID #include "sql_db.h" // load_db_opt_by_name -#include "sql_time.h" // make_truncated_value_warning #include "records.h" // init_read_record, end_read_record #include "filesort.h" // filesort_free_buffers #include "sql_select.h" // setup_order @@ -55,7 +54,7 @@ #include "sql_audit.h" #include "sql_sequence.h" #include "tztime.h" - +#include <algorithm> #ifdef __WIN__ #include <io.h> @@ -64,27 +63,22 @@ const char *primary_key_name="PRIMARY"; static int check_if_keyname_exists(const char *name,KEY *start, KEY *end); -static char *make_unique_key_name(THD *thd, const char *field_name, KEY *start, - KEY *end); -static bool make_unique_constraint_name(THD *thd, LEX_CSTRING *name, - List<Virtual_column_info> *vcol, - uint *nr); -static const -char * make_unique_invisible_field_name(THD *thd, const char *field_name, - List<Create_field> *fields); - -static int copy_data_between_tables(THD *thd, TABLE *from,TABLE *to, - List<Create_field> &create, bool ignore, - uint order_num, ORDER *order, - ha_rows *copied,ha_rows *deleted, - Alter_info::enum_enable_or_disable keys_onoff, - Alter_table_ctx *alter_ctx); - +static char *make_unique_key_name(THD *, const char *, KEY *, KEY *); +static bool make_unique_constraint_name(THD *, LEX_CSTRING *, const char *, + List<Virtual_column_info> *, uint *); +static const char *make_unique_invisible_field_name(THD *, const char *, + List<Create_field> *); +static int copy_data_between_tables(THD *, TABLE *,TABLE *, + List<Create_field> &, bool, uint, ORDER *, + ha_rows *, ha_rows *, + Alter_info::enum_enable_or_disable, + Alter_table_ctx *); static int mysql_prepare_create_table(THD *, HA_CREATE_INFO *, Alter_info *, uint *, handler *, KEY **, uint *, int); static uint blob_length_by_type(enum_field_types type); static bool fix_constraints_names(THD *thd, List<Virtual_column_info> - *check_constraint_list); + *check_constraint_list, + const HA_CREATE_INFO *create_info); /** @brief Helper function for explain_filename @@ -1851,7 +1845,7 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) #endif /* Write shadow frm file */ lpt->create_info->table_options= lpt->db_options; - LEX_CUSTRING frm= build_frm_image(lpt->thd, &lpt->table_name, + 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, @@ -2043,18 +2037,6 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, bool if_exists, if (!drop_temporary) { - if (!in_bootstrap) - { - for (table= tables; table; table= table->next_local) - { - LEX_CSTRING db_name= table->db; - LEX_CSTRING table_name= table->table_name; - if (table->open_type == OT_BASE_ONLY || - !thd->find_temporary_table(table)) - (void) delete_statistics_for_table(thd, &db_name, &table_name); - } - } - if (!thd->locked_tables_mode) { if (drop_sequence) @@ -2118,6 +2100,15 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, bool if_exists, } } } + /* We remove statistics for table last, after we have the DDL lock */ + for (table= tables; table; table= table->next_local) + { + LEX_CSTRING db_name= table->db; + LEX_CSTRING table_name= table->table_name; + if (table->open_type == OT_BASE_ONLY || + !thd->find_temporary_table(table)) + (void) delete_statistics_for_table(thd, &db_name, &table_name); + } } DBUG_EXECUTE_IF("ib_purge_virtual_mdev_16222_1", @@ -2135,7 +2126,6 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, bool if_exists, DBUG_RETURN(TRUE); my_ok(thd); DBUG_RETURN(FALSE); - } @@ -2653,9 +2643,6 @@ err: /* Chop of the last comma */ built_non_trans_tmp_query.chop(); built_non_trans_tmp_query.append(" /* generated by server */"); -#ifdef WITH_WSREP - thd->wsrep_skip_wsrep_GTID = true; -#endif /* WITH_WSREP */ error |= (thd->binlog_query(THD::STMT_QUERY_TYPE, built_non_trans_tmp_query.ptr(), built_non_trans_tmp_query.length(), @@ -2668,9 +2655,6 @@ err: /* Chop of the last comma */ built_trans_tmp_query.chop(); built_trans_tmp_query.append(" /* generated by server */"); -#ifdef WITH_WSREP - thd->wsrep_skip_wsrep_GTID = true; -#endif /* WITH_WSREP */ error |= (thd->binlog_query(THD::STMT_QUERY_TYPE, built_trans_tmp_query.ptr(), built_trans_tmp_query.length(), @@ -2685,9 +2669,6 @@ err: built_query.append(" /* generated by server */"); int error_code = non_tmp_error ? thd->get_stmt_da()->sql_errno() : 0; -#ifdef WITH_WSREP - thd->wsrep_skip_wsrep_GTID = false; -#endif /* WITH_WSREP */ error |= (thd->binlog_query(THD::STMT_QUERY_TYPE, built_query.ptr(), built_query.length(), @@ -2736,9 +2717,6 @@ err: } end: -#ifdef WITH_WSREP - thd->wsrep_skip_wsrep_GTID = false; -#endif /* WITH_WSREP */ DBUG_RETURN(error); } @@ -2836,6 +2814,7 @@ bool quick_rm_table(THD *thd, handlerton *base, const LEX_CSTRING *db, - UNIQUE keys where all column are NOT NULL - UNIQUE keys that don't contain partial segments - Other UNIQUE keys + - LONG UNIQUE keys - Normal keys - Fulltext keys @@ -2847,10 +2826,26 @@ static int sort_keys(KEY *a, KEY *b) { ulong a_flags= a->flags, b_flags= b->flags; + /* + Do not reorder LONG_HASH indexes, because they must match the order + of their LONG_UNIQUE_HASH_FIELD's. + */ + if (a->algorithm == HA_KEY_ALG_LONG_HASH && + b->algorithm == HA_KEY_ALG_LONG_HASH) + return a->usable_key_parts - b->usable_key_parts; + if (a_flags & HA_NOSAME) { if (!(b_flags & HA_NOSAME)) return -1; + /* + Long Unique keys should always be last unique key. + Before this patch they used to change order wrt to partial keys (MDEV-19049) + */ + if (a->algorithm == HA_KEY_ALG_LONG_HASH) + return 1; + if (b->algorithm == HA_KEY_ALG_LONG_HASH) + return -1; if ((a_flags ^ b_flags) & HA_NULL_PART_KEY) { /* Sort NOT NULL keys before other keys */ @@ -2875,9 +2870,7 @@ static int sort_keys(KEY *a, KEY *b) Prefer original key order. usable_key_parts contains here the original key position. */ - return ((a->usable_key_parts < b->usable_key_parts) ? -1 : - (a->usable_key_parts > b->usable_key_parts) ? 1 : - 0); + return a->usable_key_parts - b->usable_key_parts; } /* @@ -3052,7 +3045,8 @@ CHARSET_INFO* get_sql_field_charset(Column_definition *sql_field, by adding the features DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP. If the first TIMESTAMP column appears to be nullable, or to have an - explicit default, or to be a virtual column, then no promition is done. + explicit default, or to be a virtual column, or to be part of table period, + then no promotion is done. @param column_definitions The list of column definitions, in the physical order in which they appear in the table. @@ -3060,33 +3054,36 @@ CHARSET_INFO* get_sql_field_charset(Column_definition *sql_field, void promote_first_timestamp_column(List<Create_field> *column_definitions) { - List_iterator_fast<Create_field> it(*column_definitions); - Create_field *column_definition; - - while ((column_definition= it++) != NULL) + for (Create_field &column_definition : *column_definitions) { - if (column_definition->is_timestamp_type() || // TIMESTAMP - column_definition->unireg_check == Field::TIMESTAMP_OLD_FIELD) // Legacy + if (column_definition.is_timestamp_type() || // TIMESTAMP + column_definition.unireg_check == Field::TIMESTAMP_OLD_FIELD) // Legacy { - DBUG_PRINT("info", ("field-ptr:%p", column_definition->field)); - if ((column_definition->flags & NOT_NULL_FLAG) != 0 && // NOT NULL, - column_definition->default_value == NULL && // no constant default, - column_definition->unireg_check == Field::NONE && // no function default - column_definition->vcol_info == NULL && - !(column_definition->flags & VERS_SYSTEM_FIELD)) // column isn't generated + DBUG_PRINT("info", ("field-ptr:%p", column_definition.field)); + if ((column_definition.flags & NOT_NULL_FLAG) != 0 && // NOT NULL, + column_definition.default_value == NULL && // no constant default, + column_definition.unireg_check == Field::NONE && // no function default + column_definition.vcol_info == NULL && + column_definition.period == NULL && + !(column_definition.flags & VERS_SYSTEM_FIELD)) // column isn't generated { DBUG_PRINT("info", ("First TIMESTAMP column '%s' was promoted to " "DEFAULT CURRENT_TIMESTAMP ON UPDATE " "CURRENT_TIMESTAMP", - column_definition->field_name.str + column_definition.field_name.str )); - column_definition->unireg_check= Field::TIMESTAMP_DNUN_FIELD; + column_definition.unireg_check= Field::TIMESTAMP_DNUN_FIELD; } return; } } } +static bool key_cmp(const Key_part_spec &a, const Key_part_spec &b) +{ + return a.length == b.length && + !lex_string_cmp(system_charset_info, &a.field_name, &b.field_name); +} /** Check if there is a duplicate key. Report a warning for every duplicate key. @@ -3096,8 +3093,8 @@ void promote_first_timestamp_column(List<Create_field> *column_definitions) @param key_info Key meta-data info. @param key_list List of existing keys. */ -static void check_duplicate_key(THD *thd, Key *key, KEY *key_info, - List<Key> *key_list) +static void check_duplicate_key(THD *thd, const Key *key, const KEY *key_info, + const List<Key> *key_list) { /* We only check for duplicate indexes if it is requested and the @@ -3109,56 +3106,28 @@ static void check_duplicate_key(THD *thd, Key *key, KEY *key_info, if (!key->key_create_info.check_for_duplicate_indexes || key->generated) return; - List_iterator_fast<Key> key_list_iterator(*key_list); - List_iterator_fast<Key_part_spec> key_column_iterator(key->columns); - Key *k; - - while ((k= key_list_iterator++)) + for (const Key &k : *key_list) { // Looking for a similar key... - if (k == key) + if (&k == key) break; - if (k->generated || - (key->type != k->type) || - (key->key_create_info.algorithm != k->key_create_info.algorithm) || - (key->columns.elements != k->columns.elements)) + if (k.generated || + (key->type != k.type) || + (key->key_create_info.algorithm != k.key_create_info.algorithm) || + (key->columns.elements != k.columns.elements)) { // Keys are different. continue; } - /* - Keys 'key' and 'k' might be identical. - Check that the keys have identical columns in the same order. - */ - - List_iterator_fast<Key_part_spec> k_column_iterator(k->columns); - uint i; - key_column_iterator.rewind(); - - for (i= 0; i < key->columns.elements; ++i) - { - Key_part_spec *c1= key_column_iterator++; - Key_part_spec *c2= k_column_iterator++; - - DBUG_ASSERT(c1 && c2); - - if (lex_string_cmp(system_charset_info, - &c1->field_name, &c2->field_name) || - (c1->length != c2->length)) - break; - } - - // Report a warning if we have two identical keys. - - if (i == key->columns.elements) + if (std::equal(key->columns.begin(), key->columns.end(), k.columns.begin(), + key_cmp)) { - push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, - ER_DUP_INDEX, ER_THD(thd, ER_DUP_INDEX), - key_info->name.str); - break; + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_DUP_INDEX, + ER_THD(thd, ER_DUP_INDEX), key_info->name.str); + return; } } } @@ -3353,6 +3322,55 @@ int mysql_add_invisible_field(THD *thd, List<Create_field> * field_list, return 0; } +#define LONG_HASH_FIELD_NAME_LENGTH 30 +static inline void make_long_hash_field_name(LEX_CSTRING *buf, uint num) +{ + buf->length= my_snprintf((char *)buf->str, + LONG_HASH_FIELD_NAME_LENGTH, "DB_ROW_HASH_%u", num); +} + +/** + Add fully invisible hash field to table in case of long + unique column + @param thd Thread Context. + @param create_list List of table fields. + @param key_info current long unique key info +*/ +static Create_field * add_hash_field(THD * thd, List<Create_field> *create_list, + KEY *key_info) +{ + List_iterator<Create_field> it(*create_list); + Create_field *dup_field, *cf= new (thd->mem_root) Create_field(); + cf->flags|= UNSIGNED_FLAG | LONG_UNIQUE_HASH_FIELD; + cf->decimals= 0; + cf->length= cf->char_length= cf->pack_length= HA_HASH_FIELD_LENGTH; + cf->invisible= INVISIBLE_FULL; + cf->pack_flag|= FIELDFLAG_MAYBE_NULL; + cf->vcol_info= new (thd->mem_root) Virtual_column_info(); + cf->vcol_info->stored_in_db= false; + uint num= 1; + LEX_CSTRING field_name; + field_name.str= (char *)thd->alloc(LONG_HASH_FIELD_NAME_LENGTH); + make_long_hash_field_name(&field_name, num); + /* + Check for collisions + */ + while ((dup_field= it++)) + { + if (!my_strcasecmp(system_charset_info, field_name.str, dup_field->field_name.str)) + { + num++; + make_long_hash_field_name(&field_name, num); + it.rewind(); + } + } + cf->field_name= field_name; + cf->set_handler(&type_handler_longlong); + key_info->algorithm= HA_KEY_ALG_LONG_HASH; + create_list->push_back(cf,thd->mem_root); + return cf; +} + Key * mysql_add_invisible_index(THD *thd, List<Key> *key_list, LEX_CSTRING* field_name, enum Key::Keytype type) @@ -3410,6 +3428,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, 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; + bool is_hash_field_needed= false; DBUG_ENTER("mysql_prepare_create_table"); DBUG_EXECUTE_IF("test_pseudo_invisible",{ @@ -3738,6 +3757,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, uint key_length=0; Key_part_spec *column; + is_hash_field_needed= false; if (key->name.str == ignore_key) { /* ignore redundant keys */ @@ -3948,22 +3968,29 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (f_is_blob(sql_field->pack_flag) || (f_is_geom(sql_field->pack_flag) && key->type != Key::SPATIAL)) - { - if (!(file->ha_table_flags() & HA_CAN_INDEX_BLOBS)) - { - my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name.str, + { + if (!(file->ha_table_flags() & HA_CAN_INDEX_BLOBS)) + { + my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name.str, file->table_type()); - DBUG_RETURN(TRUE); - } + DBUG_RETURN(TRUE); + } if (f_is_geom(sql_field->pack_flag) && sql_field->geom_type == Field::GEOM_POINT) column->length= MAX_LEN_GEOM_POINT_FIELD; - if (!column->length) - { - my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name.str); - DBUG_RETURN(TRUE); - } - } + if (!column->length) + { + if (key->type == Key::UNIQUE) + is_hash_field_needed= true; + else if (key->type == Key::MULTIPLE) + column->length= file->max_key_length() + 1; + else + { + my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name.str); + DBUG_RETURN(TRUE); + } + } + } #ifdef HAVE_SPATIAL if (key->type == Key::SPATIAL) { @@ -4032,33 +4059,30 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (column->length) { - if (f_is_blob(sql_field->pack_flag)) - { - key_part_length= MY_MIN(column->length, - blob_length_by_type(sql_field->real_field_type()) - * sql_field->charset->mbmaxlen); - if (key_part_length > max_key_length || - key_part_length > file->max_key_part_length()) - { - key_part_length= MY_MIN(max_key_length, file->max_key_part_length()); - if (key->type == Key::MULTIPLE) - { - /* not a critical problem */ - push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + if (f_is_blob(sql_field->pack_flag)) + { + key_part_length= MY_MIN(column->length, + blob_length_by_type(sql_field->real_field_type()) + * sql_field->charset->mbmaxlen); + if (key_part_length > max_key_length || + key_part_length > file->max_key_part_length()) + { + if (key->type == Key::MULTIPLE) + { + key_part_length= MY_MIN(max_key_length, file->max_key_part_length()); + /* not a critical problem */ + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_TOO_LONG_KEY, ER_THD(thd, ER_TOO_LONG_KEY), key_part_length); /* Align key length to multibyte char boundary */ key_part_length-= key_part_length % sql_field->charset->mbmaxlen; - } - else - { - my_error(ER_TOO_LONG_KEY, MYF(0), key_part_length); - DBUG_RETURN(TRUE); - } - } - } + } + else + is_hash_field_needed= true; + } + } // Catch invalid use of partial keys - else if (!f_is_geom(sql_field->pack_flag) && + else if (!f_is_geom(sql_field->pack_flag) && // is the key partial? column->length != key_part_length && // is prefix length bigger than field length? @@ -4072,13 +4096,14 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, // and is this a 'unique' key? (key_info->flags & HA_NOSAME)))) { - my_message(ER_WRONG_SUB_KEY, ER_THD(thd, ER_WRONG_SUB_KEY), MYF(0)); - DBUG_RETURN(TRUE); - } - else if (!(file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS)) - key_part_length= column->length; + my_message(ER_WRONG_SUB_KEY, ER_THD(thd, ER_WRONG_SUB_KEY), MYF(0)); + DBUG_RETURN(TRUE); + } + else if (!(file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS)) + key_part_length= column->length; } - else if (key_part_length == 0 && (sql_field->flags & NOT_NULL_FLAG)) + else if (key_part_length == 0 && (sql_field->flags & NOT_NULL_FLAG) && + !is_hash_field_needed) { my_error(ER_WRONG_KEY_COLUMN, MYF(0), file->table_type(), column->field_name.str); @@ -4087,30 +4112,45 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (key_part_length > file->max_key_part_length() && key->type != Key::FULLTEXT) { - key_part_length= file->max_key_part_length(); - if (key->type == Key::MULTIPLE) - { - /* not a critical problem */ - push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + if (key->type == Key::MULTIPLE) + { + key_part_length= file->max_key_part_length(); + /* not a critical problem */ + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_TOO_LONG_KEY, ER_THD(thd, ER_TOO_LONG_KEY), key_part_length); /* Align key length to multibyte char boundary */ key_part_length-= key_part_length % sql_field->charset->mbmaxlen; - } - else - { - my_error(ER_TOO_LONG_KEY, MYF(0), key_part_length); - DBUG_RETURN(TRUE); - } + } + else + { + if (key->type == Key::UNIQUE) + { + is_hash_field_needed= true; + } + else + { + key_part_length= MY_MIN(max_key_length, file->max_key_part_length()); + my_error(ER_TOO_LONG_KEY, MYF(0), key_part_length); + DBUG_RETURN(TRUE); + } + } + } + /* We can not store key_part_length more then 2^16 - 1 in frm */ + if (is_hash_field_needed && column->length > UINT_MAX16) + { + my_error(ER_TOO_LONG_KEYPART, MYF(0), UINT_MAX16); + DBUG_RETURN(TRUE); } - key_part_info->length= (uint16) key_part_length; + else + key_part_info->length= (uint16) key_part_length; /* Use packed keys for long strings on the first column */ if (!((*db_options) & HA_OPTION_NO_PACK_KEYS) && !((create_info->table_options & HA_OPTION_NO_PACK_KEYS)) && (key_part_length >= KEY_DEFAULT_PACK_LENGTH && (sql_field->real_field_type() == MYSQL_TYPE_STRING || sql_field->real_field_type() == MYSQL_TYPE_VARCHAR || - f_is_blob(sql_field->pack_flag)))) + f_is_blob(sql_field->pack_flag))) && !is_hash_field_needed) { if ((column_nr == 0 && f_is_blob(sql_field->pack_flag)) || sql_field->real_field_type() == MYSQL_TYPE_VARCHAR) @@ -4119,7 +4159,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, key_info->flags|= HA_PACK_KEY; } /* Check if the key segment is partial, set the key flag accordingly */ - if (key_part_length != sql_field->key_length) + if (key_part_length != sql_field->key_length && + key_part_length != sql_field->type_handler()->max_octet_length()) key_info->flags|= HA_KEY_HAS_PART_KEY_SEG; key_length+= key_part_length; @@ -4159,12 +4200,43 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (key->type == Key::UNIQUE && !(key_info->flags & HA_NULL_PART_KEY)) unique_key=1; key_info->key_length=(uint16) key_length; - if (key_length > max_key_length && key->type != Key::FULLTEXT) + if (key_info->key_length > max_key_length && key->type == Key::UNIQUE) + is_hash_field_needed= true; + if (key_length > max_key_length && key->type != Key::FULLTEXT && + !is_hash_field_needed) { - my_error(ER_TOO_LONG_KEY,MYF(0),max_key_length); + my_error(ER_TOO_LONG_KEY, MYF(0), max_key_length); DBUG_RETURN(TRUE); } + if (is_hash_field_needed && key_info->algorithm != HA_KEY_ALG_UNDEF && + key_info->algorithm != HA_KEY_ALG_HASH ) + { + my_error(ER_TOO_LONG_KEY, MYF(0), max_key_length); + DBUG_RETURN(TRUE); + } + if (is_hash_field_needed || + (key_info->algorithm == HA_KEY_ALG_HASH && + key->type != Key::PRIMARY && + key_info->flags & HA_NOSAME && + !(file->ha_table_flags() & HA_CAN_HASH_KEYS ) && + file->ha_table_flags() & HA_CAN_VIRTUAL_COLUMNS)) + { + Create_field *hash_fld= add_hash_field(thd, &alter_info->create_list, + key_info); + if (!hash_fld) + DBUG_RETURN(TRUE); + hash_fld->offset= record_offset; + hash_fld->charset= create_info->default_table_charset; + record_offset+= hash_fld->pack_length; + if (key_info->flags & HA_NULL_PART_KEY) + null_fields++; + else + { + hash_fld->flags|= NOT_NULL_FLAG; + hash_fld->pack_flag&= ~FIELDFLAG_MAYBE_NULL; + } + } if (validate_comment_length(thd, &key->key_create_info.comment, INDEX_COMMENT_MAXLEN, ER_TOO_LONG_INDEX_COMMENT, @@ -4180,14 +4252,11 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, // Check if a duplicate index is defined. check_duplicate_key(thd, key, key_info, &alter_info->key_list); - key_info++; } - if (!unique_key && !primary_key && - ((file->ha_table_flags() & HA_REQUIRE_PRIMARY_KEY) || - ((file->ha_table_flags() & HA_WANTS_PRIMARY_KEY) && - !create_info->sequence))) + if (!unique_key && !primary_key && !create_info->sequence && + (file->ha_table_flags() & HA_REQUIRE_PRIMARY_KEY)) { my_message(ER_REQUIRES_PRIMARY_KEY, ER_THD(thd, ER_REQUIRES_PRIMARY_KEY), MYF(0)); @@ -4277,6 +4346,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, const Virtual_column_info *dup_check; while ((dup_check= dup_it++) && dup_check != check) { + if (!dup_check->name.length || dup_check->automatic_name) + continue; if (!lex_string_cmp(system_charset_info, &check->name, &dup_check->name)) { @@ -4319,11 +4390,9 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, } /* Give warnings for not supported table options */ -#if defined(WITH_ARIA_STORAGE_ENGINE) extern handlerton *maria_hton; - if (file->partition_ht() != maria_hton) -#endif - if (create_info->transactional) + if (file->partition_ht() != maria_hton && create_info->transactional && + !file->has_transaction_manager()) push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_ILLEGAL_HA_CREATE_OPTION, ER_THD(thd, ER_ILLEGAL_HA_CREATE_OPTION), @@ -4414,9 +4483,8 @@ bool validate_comment_length(THD *thd, LEX_CSTRING *comment, size_t max_len, apply it to the table. */ -static void set_table_default_charset(THD *thd, - HA_CREATE_INFO *create_info, - const LEX_CSTRING *db) +static void set_table_default_charset(THD *thd, HA_CREATE_INFO *create_info, + const LEX_CSTRING &db) { /* If the table character set was not given explicitly, @@ -4427,7 +4495,7 @@ static void set_table_default_charset(THD *thd, { Schema_specification_st db_info; - load_db_opt_by_name(thd, db->str, &db_info); + load_db_opt_by_name(thd, db.str, &db_info); create_info->default_table_charset= db_info.default_table_charset; } @@ -4481,7 +4549,7 @@ bool Column_definition::prepare_blob_field(THD *thd) set_handler(Type_handler::blob_type_handler((uint) length)); pack_length= type_handler()->calc_pack_length(0); } - length= 0; + length= key_length= 0; } DBUG_RETURN(0); } @@ -4551,12 +4619,11 @@ static bool vers_prepare_keys(THD *thd, HA_CREATE_INFO *create_info, return false; } -handler *mysql_create_frm_image(THD *thd, - const LEX_CSTRING *db, const LEX_CSTRING *table_name, +handler *mysql_create_frm_image(THD *thd, const LEX_CSTRING &db, + const LEX_CSTRING &table_name, HA_CREATE_INFO *create_info, Alter_info *alter_info, int create_table_mode, - KEY **key_info, - uint *key_count, + KEY **key_info, uint *key_count, LEX_CUSTRING *frm) { uint db_options; @@ -4690,7 +4757,7 @@ handler *mysql_create_frm_image(THD *thd, if (part_info->vers_info && !create_info->versioned()) { - my_error(ER_VERS_NOT_VERSIONED, MYF(0), table_name->str); + my_error(ER_VERS_NOT_VERSIONED, MYF(0), table_name.str); goto err; } @@ -4794,8 +4861,7 @@ handler *mysql_create_frm_image(THD *thd, } if (mysql_prepare_create_table(thd, create_info, alter_info, &db_options, - file, key_info, key_count, - create_table_mode)) + file, key_info, key_count, create_table_mode)) goto err; create_info->table_options=db_options; @@ -4847,19 +4913,13 @@ err: */ static -int create_table_impl(THD *thd, - const LEX_CSTRING *orig_db, - const LEX_CSTRING *orig_table_name, - const LEX_CSTRING *db, const LEX_CSTRING *table_name, - const char *path, - const DDL_options_st options, - 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) +int create_table_impl(THD *thd, const LEX_CSTRING &orig_db, + const LEX_CSTRING &orig_table_name, + const LEX_CSTRING &db, const LEX_CSTRING &table_name, + const char *path, const DDL_options_st options, + 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) { LEX_CSTRING *alias; handler *file= 0; @@ -4868,9 +4928,10 @@ int create_table_impl(THD *thd, 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 path: %s", - db->str, table_name->str, internal_tmp_table, path)); + db.str, table_name.str, internal_tmp_table, path)); - if (fix_constraints_names(thd, &alter_info->check_constraint_list)) + if (fix_constraints_names(thd, &alter_info->check_constraint_list, + create_info)) DBUG_RETURN(1); if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE) @@ -4897,7 +4958,7 @@ int create_table_impl(THD *thd, goto err; } - alias= const_cast<LEX_CSTRING*>(table_case_name(create_info, table_name)); + alias= const_cast<LEX_CSTRING*>(table_case_name(create_info, &table_name)); /* Check if table exists */ if (create_info->tmp_table()) @@ -4906,7 +4967,7 @@ int create_table_impl(THD *thd, If a table exists, it must have been pre-opened. Try looking for one in-use in THD::all_temp_tables list of TABLE_SHAREs. */ - TABLE *tmp_table= thd->find_temporary_table(db->str, table_name->str); + TABLE *tmp_table= thd->find_temporary_table(db.str, table_name.str); if (tmp_table) { @@ -4941,14 +5002,14 @@ int create_table_impl(THD *thd, } else { - if (!internal_tmp_table && ha_table_exists(thd, db, table_name)) + if (!internal_tmp_table && ha_table_exists(thd, &db, &table_name)) { if (options.or_replace()) { - (void) delete_statistics_for_table(thd, db, table_name); + (void) delete_statistics_for_table(thd, &db, &table_name); TABLE_LIST table_list; - table_list.init_one_table(db, table_name, 0, TL_WRITE_ALLOW_WRITE); + table_list.init_one_table(&db, &table_name, 0, TL_WRITE_ALLOW_WRITE); table_list.table= create_info->table; if (check_if_log_table(&table_list, TRUE, "CREATE OR REPLACE")) @@ -4975,7 +5036,7 @@ int create_table_impl(THD *thd, /* Restart statement transactions for the case of CREATE ... SELECT. */ - if (thd->lex->select_lex.item_list.elements && + if (thd->lex->first_select_lex()->item_list.elements && restart_trans_for_tables(thd, thd->lex->query_tables)) goto err; } @@ -4983,7 +5044,7 @@ int create_table_impl(THD *thd, goto warn; else { - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name->str); + my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name.str); goto err; } } @@ -4991,7 +5052,7 @@ int create_table_impl(THD *thd, THD_STAGE_INFO(thd, stage_creating_table); - if (check_engine(thd, orig_db->str, orig_table_name->str, create_info)) + if (check_engine(thd, orig_db.str, orig_table_name.str, create_info)) goto err; if (create_table_mode == C_ASSISTED_DISCOVERY) @@ -5011,7 +5072,7 @@ int create_table_impl(THD *thd, goto err; } - init_tmp_table_share(thd, &share, db->str, 0, table_name->str, path); + init_tmp_table_share(thd, &share, db.str, 0, table_name.str, path); /* prepare everything for discovery */ share.field= &no_fields; @@ -5053,17 +5114,29 @@ int create_table_impl(THD *thd, */ if (!file || thd->is_error()) goto err; - if (rea_create_table(thd, frm, path, db->str, table_name->str, create_info, - file, frm_only)) + + if (thd->variables.keep_files_on_create) + create_info->options|= HA_CREATE_KEEP_FILES; + + if (file->ha_create_partitioning_metadata(path, NULL, CHF_CREATE_FLAG)) goto err; + + if (!frm_only) + { + if (ha_create_table(thd, path, db.str, table_name.str, create_info, frm)) + { + file->ha_create_partitioning_metadata(path, NULL, CHF_DELETE_FLAG); + deletefrm(path); + goto err; + } + } } create_info->table= 0; if (!frm_only && create_info->tmp_table()) { - TABLE *table= thd->create_and_open_tmp_table(create_info->db_type, frm, - path, db->str, - table_name->str, true, + TABLE *table= thd->create_and_open_tmp_table(frm, path, db.str, + table_name.str, false); if (!table) @@ -5078,43 +5151,7 @@ int create_table_impl(THD *thd, thd->thread_specific_used= TRUE; create_info->table= table; // Store pointer to table } -#ifdef WITH_PARTITION_STORAGE_ENGINE - else if (thd->work_part_info && frm_only) - { - /* - For partitioned tables we can't find some problems with table - until table is opened. Therefore in order to disallow creation - of corrupted tables we have to try to open table as the part - of its creation process. - In cases when both .FRM and SE part of table are created table - is implicitly open in ha_create_table() call. - In cases when we create .FRM without SE part we have to open - table explicitly. - */ - TABLE table; - TABLE_SHARE share; - - init_tmp_table_share(thd, &share, db->str, 0, table_name->str, path); - - bool result= (open_table_def(thd, &share, GTS_TABLE) || - open_table_from_share(thd, &share, &empty_clex_str, 0, - (uint) READ_ALL, 0, &table, true)); - if (!result) - (void) closefrm(&table); - - free_table_share(&share); - if (result) - { - 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_partitioning_metadata(path, NULL, CHF_DELETE_FLAG); - goto err; - } - } -#endif - error= 0; err: THD_STAGE_INFO(thd, stage_after_create); @@ -5142,13 +5179,11 @@ warn: -1 Table was used with IF NOT EXISTS and table existed (warning, not error) */ -int mysql_create_table_no_lock(THD *thd, - const LEX_CSTRING *db, +int mysql_create_table_no_lock(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *table_name, Table_specification_st *create_info, Alter_info *alter_info, bool *is_trans, - int create_table_mode, - TABLE_LIST *table_list) + int create_table_mode, TABLE_LIST *table_list) { KEY *not_used_1; uint not_used_2; @@ -5172,7 +5207,7 @@ int mysql_create_table_no_lock(THD *thd, } } - res= create_table_impl(thd, db, table_name, db, table_name, path, + res= create_table_impl(thd, *db, *table_name, *db, *table_name, path, *create_info, create_info, alter_info, create_table_mode, is_trans, ¬_used_1, ¬_used_2, &frm); @@ -5409,17 +5444,22 @@ make_unique_key_name(THD *thd, const char *field_name,KEY *start,KEY *end) */ static bool make_unique_constraint_name(THD *thd, LEX_CSTRING *name, + const char *own_name_base, List<Virtual_column_info> *vcol, uint *nr) { char buff[MAX_FIELD_NAME], *end; List_iterator_fast<Virtual_column_info> it(*vcol); - - end=strmov(buff, "CONSTRAINT_"); - for (;;) + end=strmov(buff, own_name_base ? own_name_base : "CONSTRAINT_"); + for (int round= 0;; round++) { Virtual_column_info *check; - char *real_end= int10_to_str((*nr)++, end, 10); + char *real_end= end; + if (round == 1 && own_name_base) + *end++= '_'; + // if own_base_name provided, try it first + if (round != 0 || !own_name_base) + real_end= int10_to_str((*nr)++, end, 10); it.rewind(); while ((check= it++)) { @@ -5643,7 +5683,9 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, #ifdef WITH_WSREP if (WSREP(thd) && !thd->wsrep_applier && wsrep_create_like_table(thd, table, src_table, create_info)) + { DBUG_RETURN(res); + } #endif /* @@ -5658,12 +5700,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, properly isolated from all concurrent operations which matter. */ - /* Copy temporarily the statement flags to thd for lock_table_names() */ - // QQ: is this really needed??? - uint save_thd_create_info_options= thd->lex->create_info.options; - thd->lex->create_info.options|= create_info->options; - res= open_tables(thd, &thd->lex->query_tables, ¬_used, 0); - thd->lex->create_info.options= save_thd_create_info_options; + res= open_tables(thd, *create_info, &thd->lex->query_tables, ¬_used, 0); if (res) { @@ -6055,6 +6092,7 @@ static bool is_candidate_key(KEY *key) thd Thread object. table The altered table. alter_info List of columns and indexes to create + period_info Application-time period info DESCRIPTION Looks for the IF [NOT] EXISTS options, checks the states and remove items @@ -6066,7 +6104,8 @@ static bool is_candidate_key(KEY *key) */ static bool -handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info) +handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info, + Table_period_info *period_info) { Field **f_ptr; DBUG_ENTER("handle_if_exists_option"); @@ -6252,6 +6291,11 @@ drop_create_field: } } } + else if (drop->type == Alter_drop::PERIOD) + { + if (table->s->period.name.streq(drop->name)) + remove_drop= FALSE; + } else /* Alter_drop::KEY and Alter_drop::FOREIGN_KEY */ { uint n_key; @@ -6533,12 +6577,33 @@ remove_key: } } - DBUG_RETURN(FALSE); + /* ADD PERIOD */ + + if (period_info->create_if_not_exists && table->s->period.name + && table->s->period.name.streq(period_info->name)) + { + DBUG_ASSERT(period_info->is_set()); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_DUP_FIELDNAME, ER_THD(thd, ER_DUP_FIELDNAME), + period_info->name.str, table->s->table_name.str); + + List_iterator<Virtual_column_info> vit(alter_info->check_constraint_list); + while (vit++ != period_info->constr) + { + // do nothing + } + vit.remove(); + + *period_info= {}; + } + + DBUG_RETURN(false); } static bool fix_constraints_names(THD *thd, List<Virtual_column_info> - *check_constraint_list) + *check_constraint_list, + const HA_CREATE_INFO *create_info) { List_iterator<Virtual_column_info> it((*check_constraint_list)); Virtual_column_info *check; @@ -6562,7 +6627,12 @@ static bool fix_constraints_names(THD *thd, List<Virtual_column_info> if (!check->name.length) { check->automatic_name= TRUE; + + const char *own_name_base= create_info->period_info.constr == check + ? create_info->period_info.name.str : NULL; + if (make_unique_constraint_name(thd, &check->name, + own_name_base, check_constraint_list, &nr)) DBUG_RETURN(TRUE); @@ -6571,31 +6641,101 @@ static bool fix_constraints_names(THD *thd, List<Virtual_column_info> DBUG_RETURN(FALSE); } -/** - Get Create_field object for newly created table by field index. - @param alter_info Alter_info describing newly created table. - @param idx Field index. -*/ - -static Create_field *get_field_by_index(Alter_info *alter_info, uint idx) +static int compare_uint(const uint *s, const uint *t) { - List_iterator_fast<Create_field> field_it(alter_info->create_list); - uint field_idx= 0; - Create_field *field; + return (*s < *t) ? -1 : ((*s > *t) ? 1 : 0); +} - while ((field= field_it++) && field_idx < idx) - { field_idx++; } +static Compare_keys merge(Compare_keys current, Compare_keys add) { + if (current == Compare_keys::Equal) + return add; - return field; -} + if (add == Compare_keys::Equal) + return current; + if (current == add) + return current; -static int compare_uint(const uint *s, const uint *t) -{ - return (*s < *t) ? -1 : ((*s > *t) ? 1 : 0); + if (current == Compare_keys::EqualButComment) { + return Compare_keys::NotEqual; + } + + if (current == Compare_keys::EqualButKeyPartLength) { + if (add == Compare_keys::EqualButComment) + return Compare_keys::NotEqual; + DBUG_ASSERT(add == Compare_keys::NotEqual); + return Compare_keys::NotEqual; + } + + DBUG_ASSERT(current == Compare_keys::NotEqual); + return current; } +Compare_keys compare_keys_but_name(const KEY *table_key, const KEY *new_key, + Alter_info *alter_info, const TABLE *table, + const KEY *const new_pk, + const KEY *const old_pk) +{ + if (table_key->algorithm != new_key->algorithm) + return Compare_keys::NotEqual; + + if ((table_key->flags & HA_KEYFLAG_MASK) != + (new_key->flags & HA_KEYFLAG_MASK)) + return Compare_keys::NotEqual; + + if (table_key->user_defined_key_parts != new_key->user_defined_key_parts) + return Compare_keys::NotEqual; + + if (table_key->block_size != new_key->block_size) + return Compare_keys::NotEqual; + + /* + Rebuild the index if following condition get satisfied: + + (i) Old table doesn't have primary key, new table has it and vice-versa + (ii) Primary key changed to another existing index + */ + if ((new_key == new_pk) != (table_key == old_pk)) + return Compare_keys::NotEqual; + + if (engine_options_differ(table_key->option_struct, new_key->option_struct, + table->file->ht->index_options)) + return Compare_keys::NotEqual; + + Compare_keys result= Compare_keys::Equal; + + for (const KEY_PART_INFO * + key_part= table_key->key_part, + *new_part= new_key->key_part, + *end= table_key->key_part + table_key->user_defined_key_parts; + key_part < end; key_part++, new_part++) + { + /* + For prefix keys KEY_PART_INFO::field points to cloned Field + object with adjusted length. So below we have to check field + indexes instead of simply comparing pointers to Field objects. + */ + const Create_field &new_field= + *alter_info->create_list.elem(new_part->fieldnr); + + if (!new_field.field || + new_field.field->field_index != key_part->fieldnr - 1) + { + return Compare_keys::NotEqual; + } + + auto compare= table->file->compare_key_parts( + *table->field[key_part->fieldnr - 1], new_field, *key_part, *new_part); + result= merge(result, compare); + } + + /* Check that key comment is not changed. */ + if (cmp(table_key->comment, new_key->comment) != 0) + result= merge(result, Compare_keys::EqualButComment); + + return result; +} /** Compare original and new versions of a table and fill Alter_inplace_info @@ -6642,21 +6782,21 @@ static int compare_uint(const uint *s, const uint *t) static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar, Alter_inplace_info *ha_alter_info) { - Field **f_ptr, *field, *old_field; + Field **f_ptr, *field; List_iterator_fast<Create_field> new_field_it; Create_field *new_field; - KEY_PART_INFO *key_part, *new_part; - KEY_PART_INFO *end; Alter_info *alter_info= ha_alter_info->alter_info; DBUG_ENTER("fill_alter_inplace_info"); DBUG_PRINT("info", ("alter_info->flags: %llu", alter_info->flags)); /* Allocate result buffers. */ + DBUG_ASSERT(ha_alter_info->rename_keys.mem_root() == thd->mem_root); if (! (ha_alter_info->index_drop_buffer= (KEY**) thd->alloc(sizeof(KEY*) * table->s->keys)) || ! (ha_alter_info->index_add_buffer= (uint*) thd->alloc(sizeof(uint) * - alter_info->key_list.elements))) + alter_info->key_list.elements)) || + ha_alter_info->rename_keys.reserve(ha_alter_info->index_add_count)) DBUG_RETURN(true); /* @@ -6731,61 +6871,50 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar, /* Field is not dropped. Evaluate changes bitmap for it. */ /* - Check if type of column has changed to some incompatible type. + Check if type of column has changed. */ - uint is_equal= field->is_equal(new_field); - switch (is_equal) + bool is_equal= field->is_equal(*new_field); + if (!is_equal) { - case IS_EQUAL_NO: - /* New column type is incompatible with old one. */ - if (field->stored_in_db()) - ha_alter_info->handler_flags|= ALTER_STORED_COLUMN_TYPE; + if (field->can_be_converted_by_engine(*new_field)) + { + /* + New column type differs from the old one, but storage engine can + change it by itself. + (for example, VARCHAR(300) is changed to VARCHAR(400)). + */ + ha_alter_info->handler_flags|= ALTER_COLUMN_TYPE_CHANGE_BY_ENGINE; + } else - ha_alter_info->handler_flags|= ALTER_VIRTUAL_COLUMN_TYPE; - if (table->s->tmp_table == NO_TMP_TABLE) { - delete_statistics_for_column(thd, table, field); - KEY *key_info= table->key_info; - for (uint i=0; i < table->s->keys; i++, key_info++) + /* New column type is incompatible with old one. */ + ha_alter_info->handler_flags|= field->stored_in_db() + ? ALTER_STORED_COLUMN_TYPE + : ALTER_VIRTUAL_COLUMN_TYPE; + + if (table->s->tmp_table == NO_TMP_TABLE) { - if (field->part_of_key.is_set(i)) + delete_statistics_for_column(thd, table, field); + KEY *key_info= table->key_info; + for (uint i= 0; i < table->s->keys; i++, key_info++) { + if (!field->part_of_key.is_set(i)) + continue; + uint key_parts= table->actual_n_key_parts(key_info); for (uint j= 0; j < key_parts; j++) { - if (key_info->key_part[j].fieldnr-1 == field->field_index) + if (key_info->key_part[j].fieldnr - 1 == field->field_index) { - delete_statistics_for_index(thd, table, key_info, - j >= key_info->user_defined_key_parts); + delete_statistics_for_index( + thd, table, key_info, + j >= key_info->user_defined_key_parts); break; } - } + } } - } + } } - break; - case IS_EQUAL_YES: - /* - New column is the same as the old one or the fully compatible with - it (for example, ENUM('a','b') was changed to ENUM('a','b','c')). - Such a change if any can ALWAYS be carried out by simply updating - data-dictionary without even informing storage engine. - No flag is set in this case. - */ - break; - case IS_EQUAL_PACK_LENGTH: - /* - New column type differs from the old one, but has compatible packed - data representation. Depending on storage engine, such a change can - be carried out by simply updating data dictionary without changing - actual data (for example, VARCHAR(300) is changed to VARCHAR(400)). - */ - ha_alter_info->handler_flags|= ALTER_COLUMN_EQUAL_PACK_LENGTH; - break; - default: - DBUG_ASSERT(0); - /* Safety. */ - ha_alter_info->handler_flags|= ALTER_STORED_COLUMN_TYPE; } if (field->vcol_info || new_field->vcol_info) @@ -6796,7 +6925,7 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar, ALTER_VIRTUAL_COLUMN_TYPE); if (field->vcol_info && new_field->vcol_info) { - bool value_changes= is_equal == IS_EQUAL_NO; + bool value_changes= !is_equal; alter_table_operations alter_expr; if (field->stored_in_db()) alter_expr= ALTER_STORED_GCOL_EXPR; @@ -6890,7 +7019,6 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar, ha_alter_info->create_info->fields_option_struct[f_ptr - table->field]= new_field->option_struct; } - } else { @@ -6940,7 +7068,6 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar, Go through keys and check if the original ones are compatible with new table. */ - uint old_field_len= 0; KEY *table_key; KEY *table_key_end= table->key_info + table->s->keys; KEY *new_key; @@ -6987,88 +7114,21 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar, continue; } - /* Check that the key types are compatible between old and new tables. */ - if ((table_key->algorithm != new_key->algorithm) || - ((table_key->flags & HA_KEYFLAG_MASK) != - (new_key->flags & HA_KEYFLAG_MASK)) || - (table_key->user_defined_key_parts != - new_key->user_defined_key_parts)) - goto index_changed; - - if (table_key->block_size != new_key->block_size) - goto index_changed; - - if (engine_options_differ(table_key->option_struct, new_key->option_struct, - table->file->ht->index_options)) - goto index_changed; - - /* - Check that the key parts remain compatible between the old and - new tables. - */ - end= table_key->key_part + table_key->user_defined_key_parts; - for (key_part= table_key->key_part, new_part= new_key->key_part; - key_part < end; - key_part++, new_part++) + switch (compare_keys_but_name(table_key, new_key, alter_info, table, new_pk, + old_pk)) { - new_field= get_field_by_index(alter_info, new_part->fieldnr); - old_field= table->field[key_part->fieldnr - 1]; - /* - If there is a change in index length due to column expansion - like varchar(X) changed to varchar(X + N) and has a compatible - packed data representation, we mark it for fast/INPLACE change - in index definition. InnoDB supports INPLACE for this cases - - Key definition has changed if we are using a different field or - if the user key part length is different. - */ - old_field_len= old_field->pack_length(); - - if (old_field->type() == MYSQL_TYPE_VARCHAR) - { - old_field_len= (old_field->pack_length() - - ((Field_varstring*) old_field)->length_bytes); - } - - if (key_part->length == old_field_len && - key_part->length < new_part->length && - (key_part->field->is_equal((Create_field*) new_field) - == IS_EQUAL_PACK_LENGTH)) - { - ha_alter_info->handler_flags |= ALTER_COLUMN_INDEX_LENGTH; - } - else if (key_part->length != new_part->length) - goto index_changed; - - /* - For prefix keys KEY_PART_INFO::field points to cloned Field - object with adjusted length. So below we have to check field - indexes instead of simply comparing pointers to Field objects. - */ - if (! new_field->field || - new_field->field->field_index != key_part->fieldnr - 1) - goto index_changed; + case Compare_keys::Equal: + continue; + case Compare_keys::EqualButKeyPartLength: + ha_alter_info->handler_flags|= ALTER_COLUMN_INDEX_LENGTH; + continue; + case Compare_keys::EqualButComment: + ha_alter_info->handler_flags|= ALTER_CHANGE_INDEX_COMMENT; + continue; + case Compare_keys::NotEqual: + break; } - /* - Rebuild the index if following condition get satisfied: - - (i) Old table doesn't have primary key, new table has it and vice-versa - (ii) Primary key changed to another existing index - */ - if ((new_key == new_pk) != (table_key == old_pk)) - goto index_changed; - - /* Check that key comment is not changed. */ - if (table_key->comment.length != new_key->comment.length || - (table_key->comment.length && - memcmp(table_key->comment.str, new_key->comment.str, - table_key->comment.length) != 0)) - goto index_changed; - - continue; - - index_changed: /* Key modified. Add the key / key offset to both buffers. */ ha_alter_info->index_drop_buffer [ha_alter_info->index_drop_count++]= @@ -7108,6 +7168,40 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar, new_key->option_struct; } + for (uint i= 0; i < ha_alter_info->index_add_count; i++) + { + uint *add_buffer= ha_alter_info->index_add_buffer; + const KEY *new_key= ha_alter_info->key_info_buffer + add_buffer[i]; + + for (uint j= 0; j < ha_alter_info->index_drop_count; j++) + { + KEY **drop_buffer= ha_alter_info->index_drop_buffer; + const KEY *old_key= drop_buffer[j]; + + if (compare_keys_but_name(old_key, new_key, alter_info, table, new_pk, + old_pk) != Compare_keys::Equal) + { + continue; + } + + DBUG_ASSERT( + lex_string_cmp(system_charset_info, &old_key->name, &new_key->name)); + + ha_alter_info->handler_flags|= ALTER_RENAME_INDEX; + ha_alter_info->rename_keys.push_back( + Alter_inplace_info::Rename_key_pair(old_key, new_key)); + + --ha_alter_info->index_add_count; + --ha_alter_info->index_drop_count; + memmove(add_buffer + i, add_buffer + i + 1, + sizeof(add_buffer[0]) * (ha_alter_info->index_add_count - i)); + memmove(drop_buffer + j, drop_buffer + j + 1, + sizeof(drop_buffer[0]) * (ha_alter_info->index_drop_count - j)); + --i; // this index once again + break; + } + } + /* Sort index_add_buffer according to how key_info_buffer is sorted. I.e. with primary keys first - see sort_keys(). @@ -7292,7 +7386,7 @@ bool mysql_compare_tables(TABLE *table, DBUG_RETURN(false); /* Evaluate changes bitmap and send to check_if_incompatible_data() */ - uint field_changes= field->is_equal(tmp_new_field); + uint field_changes= field->is_equal(*tmp_new_field); if (field_changes != IS_EQUAL_YES) DBUG_RETURN(false); @@ -7505,7 +7599,6 @@ static bool is_inplace_alter_impossible(TABLE *table, @param ha_alter_info Structure describing ALTER TABLE to be carried out and serving as a storage place for data used during different phases. - @param inplace_supported Enum describing the locking requirements. @param target_mdl_request Metadata request/lock on the target table name. @param alter_ctx ALTER TABLE runtime context. @@ -7530,20 +7623,23 @@ static bool mysql_inplace_alter_table(THD *thd, TABLE *table, TABLE *altered_table, Alter_inplace_info *ha_alter_info, - enum_alter_inplace_result inplace_supported, MDL_request *target_mdl_request, Alter_table_ctx *alter_ctx) { Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN | MYSQL_OPEN_IGNORE_KILLED); handlerton *db_type= table->s->db_type(); MDL_ticket *mdl_ticket= table->mdl_ticket; - HA_CREATE_INFO *create_info= ha_alter_info->create_info; Alter_info *alter_info= ha_alter_info->alter_info; bool reopen_tables= false; bool res; + const enum_alter_inplace_result inplace_supported= + ha_alter_info->inplace_supported; DBUG_ENTER("mysql_inplace_alter_table"); + /* Downgrade DDL lock while we are waiting for exclusive lock below */ + backup_set_alter_copy_lock(thd, table); + /* Upgrade to EXCLUSIVE lock if: - This is requested by the storage engine @@ -7616,9 +7712,7 @@ static bool mysql_inplace_alter_table(THD *thd, thd->mdl_context.upgrade_shared_lock(table->mdl_ticket, MDL_SHARED_NO_WRITE, thd->variables.lock_wait_timeout)) - { goto cleanup; - } // It's now safe to take the table level lock. if (lock_tables(thd, table_list, alter_ctx->tables_opened, 0)) @@ -7655,9 +7749,7 @@ static bool mysql_inplace_alter_table(THD *thd, if (table->file->ha_prepare_inplace_alter_table(altered_table, ha_alter_info)) - { goto rollback; - } /* Downgrade the lock if storage engine has told us that exclusive lock was @@ -7700,6 +7792,10 @@ static bool mysql_inplace_alter_table(THD *thd, if (wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_RENAME)) goto rollback; + /* Set MDL_BACKUP_DDL */ + if (backup_reset_alter_copy_lock(thd)) + goto rollback; + /* If we are killed after this point, we should ignore and continue. We have mostly completed the operation at this point, there should @@ -7743,8 +7839,6 @@ static bool mysql_inplace_alter_table(THD *thd, { goto rollback; } - - thd->drop_temporary_table(altered_table, NULL, false); } close_all_tables_for_name(thd, table->s, @@ -7759,7 +7853,7 @@ static bool mysql_inplace_alter_table(THD *thd, Rename to the new name (if needed) will be handled separately below. TODO: remove this check of thd->is_error() (now it intercept - errors in some val_*() methoids and bring some single place to + errors in some val_*() methods and bring some single place to such error interception). */ if (mysql_rename_table(db_type, &alter_ctx->new_db, &alter_ctx->tmp_name, @@ -7768,9 +7862,6 @@ static bool mysql_inplace_alter_table(THD *thd, thd->is_error()) { // Since changes were done in-place, we can't revert them. - (void) quick_rm_table(thd, db_type, - &alter_ctx->new_db, &alter_ctx->tmp_name, - FN_IS_TMP | NO_HA_TABLE); DBUG_RETURN(true); } @@ -7847,10 +7938,6 @@ static bool mysql_inplace_alter_table(THD *thd, thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0); /* QQ; do something about metadata locks ? */ } - thd->drop_temporary_table(altered_table, NULL, false); - // Delete temporary .frm/.par - (void) quick_rm_table(thd, create_info->db_type, &alter_ctx->new_db, - &alter_ctx->tmp_name, FN_IS_TMP | NO_HA_TABLE); DBUG_RETURN(true); } @@ -7965,6 +8052,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, Create_field *def; Field **f_ptr,*field; MY_BITMAP *dropped_fields= NULL; // if it's NULL - no dropped fields + bool drop_period= false; DBUG_ENTER("mysql_prepare_alter_table"); /* @@ -8339,9 +8427,9 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, Collect all keys which isn't in drop list. Add only those for which some fields exists. */ - for (uint i=0 ; i < table->s->keys ; i++,key_info++) { + bool long_hash_key= false; if (key_info->flags & HA_INVISIBLE_KEY) continue; const char *key_name= key_info->name.str; @@ -8374,6 +8462,11 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, continue; } + if (key_info->algorithm == HA_KEY_ALG_LONG_HASH) + { + setup_keyinfo_hash(key_info); + long_hash_key= true; + } const char *dropped_key_part= NULL; KEY_PART_INFO *key_part= key_info->key_part; key_parts.empty(); @@ -8467,6 +8560,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, enum Key::Keytype key_type; LEX_CSTRING tmp_name; bzero((char*) &key_create_info, sizeof(key_create_info)); + if (key_info->algorithm == HA_KEY_ALG_LONG_HASH) + key_info->algorithm= HA_KEY_ALG_UNDEF; key_create_info.algorithm= key_info->algorithm; /* We copy block size directly as some engines, like Area, sets this @@ -8496,6 +8591,11 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, if (dropped_key_part) { my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), dropped_key_part); + if (long_hash_key) + { + key_info->algorithm= HA_KEY_ALG_LONG_HASH; + re_setup_keyinfo_hash(key_info); + } goto err; } } @@ -8506,11 +8606,17 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, tmp_name.str= key_name; tmp_name.length= strlen(key_name); - key= new Key(key_type, &tmp_name, &key_create_info, + /* We dont need LONG_UNIQUE_HASH_FIELD flag because it will be autogenerated */ + key= new (thd->mem_root) Key(key_type, &tmp_name, &key_create_info, MY_TEST(key_info->flags & HA_GENERATED_KEY), &key_parts, key_info->option_list, DDL_options()); new_key_list.push_back(key, thd->mem_root); } + if (long_hash_key) + { + key_info->algorithm= HA_KEY_ALG_LONG_HASH; + re_setup_keyinfo_hash(key_info); + } } { Key *key; @@ -8529,6 +8635,35 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, } } + if (table->s->period.name) + { + drop_it.rewind(); + Alter_drop *drop; + for (bool found= false; !found && (drop= drop_it++); ) + { + found= drop->type == Alter_drop::PERIOD && + table->s->period.name.streq(drop->name); + } + + if (drop) + { + drop_period= true; + drop_it.remove(); + } + else if (create_info->period_info.is_set() && table->s->period.name) + { + my_error(ER_MORE_THAN_ONE_PERIOD, MYF(0)); + goto err; + } + else + { + Field *s= table->s->period.start_field(table->s); + Field *e= table->s->period.end_field(table->s); + create_info->period_info.set_period(s->field_name, e->field_name); + create_info->period_info.name= table->s->period.name; + } + } + /* Add all table level constraints which are not in the drop list */ if (table->s->table_check_constraints) { @@ -8539,6 +8674,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, { Virtual_column_info *check= table->check_constraints[i]; Alter_drop *drop; + bool keep= true; drop_it.rewind(); while ((drop=drop_it++)) { @@ -8546,17 +8682,50 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, !my_strcasecmp(system_charset_info, check->name.str, drop->name)) { drop_it.remove(); + keep= false; break; } } + + // NB: `check` is TABLE resident, we must keep it intact. + if (keep) + { + check= check->clone(thd); + if (!check) + { + my_error(ER_OUT_OF_RESOURCES, MYF(0)); + goto err; + } + } + + if (share->period.constr_name.streq(check->name.str)) + { + if (drop_period) + { + keep= false; + } + else if(!keep) + { + my_error(ER_PERIOD_CONSTRAINT_DROP, MYF(0), check->name.str, + share->period.name.str); + goto err; + } + else + { + DBUG_ASSERT(create_info->period_info.constr == NULL); + create_info->period_info.constr= check; + create_info->period_info.constr->automatic_name= true; + } + } + /* see if the constraint depends on *only* on dropped fields */ - if (!drop && dropped_fields) + if (keep && dropped_fields) { table->default_column_bitmaps(); bitmap_clear_all(table->read_set); check->expr->walk(&Item::register_field_in_read_map, 1, 0); if (bitmap_is_subset(table->read_set, dropped_fields)) - drop= (Alter_drop*)1; + keep= false; else if (bitmap_is_overlapping(dropped_fields, table->read_set)) { bitmap_intersect(table->read_set, dropped_fields); @@ -8566,7 +8735,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, goto err; } } - if (!drop) + if (keep) { if (alter_info->flags & ALTER_RENAME_COLUMN) { @@ -8620,8 +8789,9 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, case Alter_drop::KEY: case Alter_drop::COLUMN: case Alter_drop::CHECK_CONSTRAINT: - my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0), drop->type_name(), - alter_info->drop_list.head()->name); + case Alter_drop::PERIOD: + my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0), drop->type_name(), + alter_info->drop_list.head()->name); goto err; case Alter_drop::FOREIGN_KEY: // Leave the DROP FOREIGN KEY names in the alter_info->drop_list. @@ -8757,7 +8927,7 @@ fk_check_column_changes(THD *thd, Alter_info *alter_info, return FK_COLUMN_RENAMED; } - if ((old_field->is_equal(new_field) == IS_EQUAL_NO) || + if ((old_field->is_equal(*new_field) == IS_EQUAL_NO) || ((new_field->flags & NOT_NULL_FLAG) && !(old_field->flags & NOT_NULL_FLAG))) { @@ -9155,11 +9325,6 @@ simple_rename_or_index_change(THD *thd, TABLE_LIST *table_list, close_all_tables_for_name(thd, table->s, HA_EXTRA_PREPARE_FOR_RENAME, NULL); - (void) rename_table_in_stat_tables(thd, &alter_ctx->db, - &alter_ctx->table_name, - &alter_ctx->new_db, - &alter_ctx->new_alias); - if (mysql_rename_table(old_db_type, &alter_ctx->db, &alter_ctx->table_name, &alter_ctx->new_db, &alter_ctx->new_alias, 0)) error= -1; @@ -9176,6 +9341,12 @@ simple_rename_or_index_change(THD *thd, TABLE_LIST *table_list, NO_FK_CHECKS); error= -1; } + /* Update stat tables last. This is to be able to handle rename of a stat table */ + if (error == 0) + (void) rename_table_in_stat_tables(thd, &alter_ctx->db, + &alter_ctx->table_name, + &alter_ctx->new_db, + &alter_ctx->new_alias); } if (likely(!error)) @@ -9205,6 +9376,49 @@ simple_rename_or_index_change(THD *thd, TABLE_LIST *table_list, } +static void cleanup_table_after_inplace_alter_keep_files(TABLE *table) +{ + TABLE_SHARE *share= table->s; + closefrm(table); + free_table_share(share); +} + + +static void cleanup_table_after_inplace_alter(TABLE *table) +{ + table->file->ha_create_partitioning_metadata(table->s->normalized_path.str, 0, + CHF_DELETE_FLAG); + deletefrm(table->s->normalized_path.str); + cleanup_table_after_inplace_alter_keep_files(table); +} + + +static int create_table_for_inplace_alter(THD *thd, + const Alter_table_ctx &alter_ctx, + LEX_CUSTRING *frm, + TABLE_SHARE *share, + TABLE *table) +{ + init_tmp_table_share(thd, share, alter_ctx.new_db.str, 0, + alter_ctx.new_name.str, alter_ctx.get_tmp_path()); + if (share->init_from_binary_frm_image(thd, true, frm->str, frm->length) || + open_table_from_share(thd, share, &alter_ctx.new_name, 0, + EXTRA_RECORD, thd->open_options, + table, false)) + { + free_table_share(share); + deletefrm(alter_ctx.get_tmp_path()); + return 1; + } + if (table->internal_tables && open_and_lock_internal_tables(table, false)) + { + cleanup_table_after_inplace_alter(table); + return 1; + } + return 0; +} + + /** Alter table @@ -9308,6 +9522,7 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db, uint tables_opened; thd->open_options|= HA_OPEN_FOR_ALTER; + thd->mdl_backup_ticket= 0; bool error= open_tables(thd, &table_list, &tables_opened, 0, &alter_prelocking_strategy); thd->open_options&= ~HA_OPEN_FOR_ALTER; @@ -9415,12 +9630,12 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db, } /* - Global intention exclusive lock must have been already acquired when - table to be altered was open, so there is no need to do it here. + Protection against global read lock must have been acquired when table + to be altered was being opened. */ - DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::GLOBAL, + DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::BACKUP, "", "", - MDL_INTENTION_EXCLUSIVE)); + MDL_BACKUP_DDL)); if (thd->mdl_context.acquire_locks(&mdl_requests, thd->variables.lock_wait_timeout)) @@ -9587,11 +9802,12 @@ do_continue:; } } - if (handle_if_exists_options(thd, table, alter_info) || - fix_constraints_names(thd, &alter_info->check_constraint_list)) + if (handle_if_exists_options(thd, table, alter_info, + &create_info->period_info) || + fix_constraints_names(thd, &alter_info->check_constraint_list, + create_info)) DBUG_RETURN(true); - /* Look if we have to do anything at all. ALTER can become NOOP after handling @@ -9670,13 +9886,12 @@ do_continue:; DBUG_RETURN(true); } - if (create_info->vers_check_system_fields(thd, alter_info, - table->s->table_name, table->s->db)) - { - DBUG_RETURN(true); - } + set_table_default_charset(thd, create_info, alter_ctx.db); - set_table_default_charset(thd, create_info, &alter_ctx.db); + if (create_info->check_fields(thd, alter_info, + table_list->table_name, table_list->db) || + create_info->fix_period_fields(thd, alter_info)) + DBUG_RETURN(true); if (!opt_explicit_defaults_for_timestamp) promote_first_timestamp_column(&alter_info->create_list); @@ -9832,8 +10047,6 @@ do_continue:; } DEBUG_SYNC(thd, "alter_table_before_create_table_no_lock"); - /* We can abort alter table for any table type */ - thd->abort_on_warning= !ignore && thd->is_strict_mode(); /* Create .FRM for new version of table with a temporary name. @@ -9856,15 +10069,13 @@ do_continue:; tmp_disable_binlog(thd); create_info->options|=HA_CREATE_TMP_ALTER; create_info->alias= alter_ctx.table_name; - error= create_table_impl(thd, - &alter_ctx.db, &alter_ctx.table_name, - &alter_ctx.new_db, &alter_ctx.tmp_name, + error= create_table_impl(thd, alter_ctx.db, alter_ctx.table_name, + alter_ctx.new_db, alter_ctx.tmp_name, alter_ctx.get_tmp_path(), thd->lex->create_info, create_info, alter_info, C_ALTER_TABLE_FRM_ONLY, NULL, &key_info, &key_count, &frm); reenable_binlog(thd); - thd->abort_on_warning= false; if (unlikely(error)) { my_free(const_cast<uchar*>(frm.str)); @@ -9880,7 +10091,8 @@ do_continue:; key_info, key_count, IF_PARTITIONING(thd->work_part_info, NULL), ignore, alter_ctx.error_if_not_empty); - TABLE *altered_table= NULL; + TABLE_SHARE altered_share; + TABLE altered_table; bool use_inplace= true; /* Fill the Alter_inplace_info structure. */ @@ -9909,11 +10121,10 @@ do_continue:; Also note that we ignore the LOCK clause here. - TODO don't create the frm in the first place + TODO don't create partitioning metadata in the first place */ - const char *path= alter_ctx.get_tmp_path(); - table->file->ha_create_partitioning_metadata(path, NULL, CHF_DELETE_FLAG); - deletefrm(path); + table->file->ha_create_partitioning_metadata(alter_ctx.get_tmp_path(), + NULL, CHF_DELETE_FLAG); my_free(const_cast<uchar*>(frm.str)); goto end_inplace; } @@ -9921,49 +10132,48 @@ do_continue:; // We assume that the table is non-temporary. DBUG_ASSERT(!table->s->tmp_table); - if (!(altered_table= - thd->create_and_open_tmp_table(new_db_type, &frm, - alter_ctx.get_tmp_path(), - alter_ctx.new_db.str, - alter_ctx.new_name.str, - false, true))) + if (create_table_for_inplace_alter(thd, alter_ctx, &frm, &altered_share, + &altered_table)) goto err_new_table_cleanup; /* Set markers for fields in TABLE object for altered table. */ - update_altered_table(ha_alter_info, altered_table); + update_altered_table(ha_alter_info, &altered_table); /* Mark all columns in 'altered_table' as used to allow usage of its record[0] buffer and Field objects during in-place ALTER TABLE. */ - altered_table->column_bitmaps_set_no_signal(&altered_table->s->all_set, - &altered_table->s->all_set); - restore_record(altered_table, s->default_values); // Create empty record + altered_table.column_bitmaps_set_no_signal(&altered_table.s->all_set, + &altered_table.s->all_set); + restore_record(&altered_table, s->default_values); // Create empty record /* Check that we can call default functions with default field values */ thd->count_cuted_fields= CHECK_FIELD_EXPRESSION; - altered_table->reset_default_fields(); - if (altered_table->default_field && - altered_table->update_default_fields(true)) + altered_table.reset_default_fields(); + if (altered_table.default_field && + altered_table.update_default_fields(true)) + { + cleanup_table_after_inplace_alter(&altered_table); goto err_new_table_cleanup; + } thd->count_cuted_fields= CHECK_FIELD_IGNORE; if (alter_info->requested_lock == Alter_info::ALTER_TABLE_LOCK_NONE) ha_alter_info.online= true; // 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, + ha_alter_info.inplace_supported= + table->file->check_if_supported_inplace_alter(&altered_table, &ha_alter_info); - if (alter_info->supports_algorithm(thd, inplace_supported, &ha_alter_info) || - alter_info->supports_lock(thd, inplace_supported, &ha_alter_info)) + if (alter_info->supports_algorithm(thd, &ha_alter_info) || + alter_info->supports_lock(thd, &ha_alter_info)) { - thd->drop_temporary_table(altered_table, NULL, false); + cleanup_table_after_inplace_alter(&altered_table); goto err_new_table_cleanup; } // If SHARED lock and no particular algorithm was requested, use COPY. - if (inplace_supported == HA_ALTER_INPLACE_EXCLUSIVE_LOCK && + if (ha_alter_info.inplace_supported == HA_ALTER_INPLACE_EXCLUSIVE_LOCK && alter_info->requested_lock == Alter_info::ALTER_TABLE_LOCK_SHARED && alter_info->algorithm(thd) == Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT && @@ -9971,7 +10181,7 @@ do_continue:; Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT) use_inplace= false; - if (inplace_supported == HA_ALTER_INPLACE_NOT_SUPPORTED) + if (ha_alter_info.inplace_supported == HA_ALTER_INPLACE_NOT_SUPPORTED) use_inplace= false; if (use_inplace) @@ -9982,20 +10192,22 @@ do_continue:; for alter table. */ Check_level_instant_set check_level_save(thd, CHECK_FIELD_WARN); - int res= mysql_inplace_alter_table(thd, table_list, table, altered_table, - &ha_alter_info, inplace_supported, + int res= mysql_inplace_alter_table(thd, table_list, table, &altered_table, + &ha_alter_info, &target_mdl_request, &alter_ctx); my_free(const_cast<uchar*>(frm.str)); if (res) + { + cleanup_table_after_inplace_alter(&altered_table); DBUG_RETURN(true); + } + cleanup_table_after_inplace_alter_keep_files(&altered_table); goto end_inplace; } else - { - thd->drop_temporary_table(altered_table, NULL, false); - } + cleanup_table_after_inplace_alter_keep_files(&altered_table); } /* ALTER TABLE using copy algorithm. */ @@ -10049,15 +10261,15 @@ do_continue:; no_ha_table= false; DEBUG_SYNC(thd, "alter_table_intermediate_table_created"); - new_table= - thd->create_and_open_tmp_table(new_db_type, &frm, alter_ctx.get_tmp_path(), - alter_ctx.new_db.str, - alter_ctx.new_name.str, - true, true); + /* Open the table since we need to copy the data. */ + new_table= thd->create_and_open_tmp_table(&frm, + alter_ctx.get_tmp_path(), + alter_ctx.new_db.str, + alter_ctx.new_name.str, + true); if (!new_table) goto err_new_table_cleanup; - /* Open the table since we need to copy the data. */ if (table->s->tmp_table != NO_TMP_TABLE) { /* in case of alter temp table send the tracker in OK packet */ @@ -10177,7 +10389,7 @@ do_continue:; close_all_tables_for_name(thd, table->s, alter_ctx.is_table_renamed() ? - HA_EXTRA_PREPARE_FOR_RENAME: + HA_EXTRA_PREPARE_FOR_RENAME: HA_EXTRA_NOT_USED, NULL); table_list->table= table= NULL; /* Safety */ @@ -10302,19 +10514,17 @@ err_new_table_cleanup: if (unlikely(alter_ctx.error_if_not_empty && thd->get_stmt_da()->current_row_for_warning())) { - const char *f_val= 0; - enum enum_mysql_timestamp_type t_type= MYSQL_TIMESTAMP_DATE; + const char *f_val= "0000-00-00"; + const char *f_type= "date"; switch (alter_ctx.datetime_field->real_field_type()) { case MYSQL_TYPE_DATE: case MYSQL_TYPE_NEWDATE: - f_val= "0000-00-00"; - 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; + f_type= "datetime"; break; default: /* Shouldn't get here. */ @@ -10322,11 +10532,12 @@ err_new_table_cleanup: } bool save_abort_on_warning= thd->abort_on_warning; thd->abort_on_warning= true; - make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN, - f_val, strlength(f_val), t_type, - alter_ctx.new_db.str, - alter_ctx.new_name.str, - alter_ctx.datetime_field->field_name.str); + thd->push_warning_truncated_value_for_field(Sql_condition::WARN_LEVEL_WARN, + f_type, f_val, + alter_ctx.new_db.str, + alter_ctx.new_name.str, + alter_ctx.datetime_field-> + field_name.str); thd->abort_on_warning= save_abort_on_warning; } @@ -10374,7 +10585,7 @@ bool mysql_trans_prepare_alter_copy_data(THD *thd) /* Turn off recovery logging since rollback of an alter table is to delete the new table so there is no need to log the changes to it. - + This needs to be done before external_lock. */ DBUG_RETURN(ha_enable_transaction(thd, FALSE) != 0); @@ -10441,6 +10652,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, bool make_versioned= !from->versioned() && to->versioned(); bool make_unversioned= from->versioned() && !to->versioned(); bool keep_versioned= from->versioned() && to->versioned(); + bool bulk_insert_started= 0; Field *to_row_start= NULL, *to_row_end= NULL, *from_row_end= NULL; MYSQL_TIME query_start; DBUG_ENTER("copy_data_between_tables"); @@ -10466,12 +10678,11 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, DBUG_RETURN(-1); } + backup_set_alter_copy_lock(thd, from); + alter_table_manage_keys(to, from->file->indexes_are_disabled(), keys_onoff); - /* Set read map for all fields in from table */ from->default_column_bitmaps(); - bitmap_set_all(from->read_set); - from->file->column_bitmaps_signal(); /* We can abort alter table for any table type */ thd->abort_on_warning= !ignore && thd->is_strict_mode(); @@ -10480,6 +10691,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, to->file->extra(HA_EXTRA_PREPARE_FOR_ALTER_TABLE); to->file->ha_start_bulk_insert(from->file->stats.records, ignore ? 0 : HA_CREATE_UNIQUE_INDEX_BY_SORT); + bulk_insert_started= 1; List_iterator<Create_field> it(create); Create_field *def; copy_end=copy; @@ -10501,7 +10713,11 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, if (def->field == from->found_next_number_field) thd->variables.sql_mode|= MODE_NO_AUTO_VALUE_ON_ZERO; } - (copy_end++)->set(*ptr,def->field,0); + if (!(*ptr)->vcol_info) + { + bitmap_set_bit(from->read_set, def->field->field_index); + (copy_end++)->set(*ptr,def->field,0); + } } else { @@ -10529,7 +10745,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, char warn_buff[MYSQL_ERRMSG_SIZE]; bool save_abort_on_warning= thd->abort_on_warning; thd->abort_on_warning= false; - my_snprintf(warn_buff, sizeof(warn_buff), + my_snprintf(warn_buff, sizeof(warn_buff), "ORDER BY ignored as there is a user-defined clustered index" " in the table '%-.192s'", from->s->table_name.str); push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, @@ -10547,8 +10763,8 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, Filesort_tracker dummy_tracker(false); Filesort fsort(order, HA_POS_ERROR, true, NULL); - if (thd->lex->select_lex.setup_ref_array(thd, order_num) || - setup_order(thd, thd->lex->select_lex.ref_pointer_array, + if (thd->lex->first_select_lex()->setup_ref_array(thd, order_num) || + setup_order(thd, thd->lex->first_select_lex()->ref_pointer_array, &tables, fields, all_fields, order)) goto err; @@ -10569,6 +10785,11 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, from_row_end= from->vers_end_field(); } + if (from_row_end) + bitmap_set_bit(from->read_set, from_row_end->field_index); + + from->file->column_bitmaps_signal(); + THD_STAGE_INFO(thd, stage_copy_to_tmp_table); /* Tell handler that we have values for all columns in the to table */ to->use_all_columns(); @@ -10733,17 +10954,24 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, to->file->print_error(my_errno,MYF(0)); error= 1; } + bulk_insert_started= 0; if (!ignore) to->file->extra(HA_EXTRA_END_ALTER_COPY); cleanup_done= 1; to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); + if (backup_reset_alter_copy_lock(thd)) + error= 1; + if (unlikely(mysql_trans_commit_alter_copy_data(thd))) error= 1; err: - /* Free resources */ + if (bulk_insert_started) + (void) to->file->ha_end_bulk_insert(); + +/* Free resources */ if (init_read_record_done) end_read_record(&info); delete [] copy; @@ -10758,7 +10986,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, if (!cleanup_done) { - /* This happens if we get an error during initialzation of data */ + /* This happens if we get an error during initialization of data */ DBUG_ASSERT(error); to->file->ha_end_bulk_insert(); ha_enable_transaction(thd, TRUE); @@ -10897,7 +11125,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, (((t->file->ha_table_flags() & HA_HAS_OLD_CHECKSUM) && thd->variables.old_mode) || ((t->file->ha_table_flags() & HA_HAS_NEW_CHECKSUM) && !thd->variables.old_mode))) { - if (t->file->info(HA_STATUS_VARIABLE)) + if (t->file->info(HA_STATUS_VARIABLE) || t->file->stats.checksum_null) protocol->store_null(); else protocol->store((longlong)t->file->stats.checksum); @@ -10917,7 +11145,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, thd->protocol->remove_last_row(); goto err; } - if (error) + if (error || t->file->stats.checksum_null) protocol->store_null(); else protocol->store((longlong)t->file->stats.checksum); @@ -11001,10 +11229,10 @@ bool check_engine(THD *thd, const char *db_name, if (req_engine && req_engine != *new_engine) { push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, - ER_WARN_USING_OTHER_HANDLER, + ER_WARN_USING_OTHER_HANDLER, ER_THD(thd, ER_WARN_USING_OTHER_HANDLER), - ha_resolve_storage_engine_name(*new_engine), - table_name); + ha_resolve_storage_engine_name(*new_engine), + table_name); } if (create_info->tmp_table() && ha_check_storage_engine_flag(*new_engine, HTON_TEMPORARY_NOT_SUPPORTED)) @@ -11027,7 +11255,7 @@ bool Sql_cmd_create_table_like::execute(THD *thd) { DBUG_ENTER("Sql_cmd_create_table::execute"); LEX *lex= thd->lex; - SELECT_LEX *select_lex= &lex->select_lex; + SELECT_LEX *select_lex= lex->first_select_lex(); TABLE_LIST *first_table= select_lex->table_list.first; DBUG_ASSERT(first_table == lex->query_tables); DBUG_ASSERT(first_table != 0); @@ -11146,7 +11374,7 @@ bool Sql_cmd_create_table_like::execute(THD *thd) } #endif - if (select_lex->item_list.elements) // With select + if (select_lex->item_list.elements || select_lex->tvc) // With select or TVC { select_result *result; @@ -11215,11 +11443,7 @@ bool Sql_cmd_create_table_like::execute(THD *thd) goto end_with_restore_list; } - /* Copy temporarily the statement flags to thd for lock_table_names() */ - uint save_thd_create_info_options= thd->lex->create_info.options; - thd->lex->create_info.options|= create_info.options; res= open_and_lock_tables(thd, create_info, lex->query_tables, TRUE, 0); - thd->lex->create_info.options= save_thd_create_info_options; if (unlikely(res)) { /* Got error or warning. Set res to 1 if error */ @@ -11291,10 +11515,9 @@ bool Sql_cmd_create_table_like::execute(THD *thd) } else { - if (create_info.vers_fix_system_fields(thd, &alter_info, *create_table) || - create_info.vers_check_system_fields(thd, &alter_info, - create_table->table_name, - create_table->db)) + if (create_info.fix_create_fields(thd, &alter_info, *create_table) || + create_info.check_fields(thd, &alter_info, + create_table->table_name, create_table->db)) goto end_with_restore_list; /* @@ -11308,7 +11531,7 @@ bool Sql_cmd_create_table_like::execute(THD *thd) (!thd->is_current_stmt_binlog_format_row() || !create_info.tmp_table())) { - WSREP_TO_ISOLATION_BEGIN(create_table->db.str, create_table->table_name.str, NULL) + WSREP_TO_ISOLATION_BEGIN(create_table->db.str, create_table->table_name.str, NULL); } /* Regular CREATE TABLE */ res= mysql_create_table(thd, create_table, &create_info, &alter_info); @@ -11331,6 +11554,8 @@ bool Sql_cmd_create_table_like::execute(THD *thd) end_with_restore_list: DBUG_RETURN(res); -WSREP_ERROR_LABEL: +#ifdef WITH_WSREP +wsrep_error_label: DBUG_RETURN(true); +#endif } |