diff options
Diffstat (limited to 'sql/sql_table.cc')
-rw-r--r-- | sql/sql_table.cc | 670 |
1 files changed, 466 insertions, 204 deletions
diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 758757ea7dd..c9194bcb276 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2000, 2015, Oracle and/or its affiliates. + Copyright (c) 2000, 2016, Oracle and/or its affiliates. Copyright (c) 2010, 2016, MariaDB This program is free software; you can redistribute it and/or modify @@ -25,7 +25,7 @@ #include "sql_table.h" #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 +#include "sql_base.h" // lock_table_names #include "lock.h" // mysql_unlock_tables #include "strfunc.h" // find_type2, find_set #include "sql_truncate.h" // regenerate_locked_table @@ -37,8 +37,7 @@ #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, - // make_unireg_sortorder +#include "sql_select.h" // setup_order #include "sql_handler.h" // mysql_ha_rm_tables #include "discover.h" // readfrm #include "my_pthread.h" // pthread_mutex_t @@ -65,6 +64,9 @@ const char *primary_key_name="PRIMARY"; static bool 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 void make_unique_constraint_name(THD *thd, LEX_STRING *name, + List<Virtual_column_info> *vcol, + uint *nr); static int copy_data_between_tables(THD *thd, TABLE *from,TABLE *to, List<Create_field> &create, bool ignore, uint order_num, ORDER *order, @@ -1642,7 +1644,7 @@ void execute_ddl_log_recovery() /* To be able to run this from boot, we allocate a temporary THD */ - if (!(thd=new THD)) + if (!(thd=new THD(0))) DBUG_VOID_RETURN; thd->thread_stack= (char*) &thd; thd->store_globals(); @@ -2030,7 +2032,7 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, LEX_STRING db_name= { table->db, table->db_length }; LEX_STRING table_name= { table->table_name, table->table_name_length }; if (table->open_type == OT_BASE_ONLY || - !find_temporary_table(thd, table)) + !thd->find_temporary_table(table)) (void) delete_statistics_for_table(thd, &db_name, &table_name); } } @@ -2283,23 +2285,17 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, */ DBUG_ASSERT(!(thd->locked_tables_mode && table->open_type != OT_BASE_ONLY && - find_temporary_table(thd, table) && + thd->find_temporary_table(table) && table->mdl_request.ticket != NULL)); - /* - drop_temporary_table may return one of the following error codes: - . 0 - a temporary table was successfully dropped. - . 1 - a temporary table was not found. - . -1 - a temporary table is used by an outer statement. - */ if (table->open_type == OT_BASE_ONLY || !is_temporary_table(table)) error= 1; else { table_creation_was_logged= table->table->s->table_creation_was_logged; - if ((error= drop_temporary_table(thd, table->table, &is_trans)) == -1) + if (thd->drop_temporary_table(table->table, &is_trans, true)) { - DBUG_ASSERT(thd->in_sub_stmt); + error= 1; goto err; } table->table= 0; @@ -2882,7 +2878,8 @@ int prepare_create_field(Column_definition *sql_field, uint *blob_columns, longlong table_flags) { - unsigned int dup_val_count; + uint dup_val_count; + uint decimals= sql_field->decimals; DBUG_ENTER("prepare_create_field"); /* @@ -3000,8 +2997,18 @@ int prepare_create_field(Column_definition *sql_field, FIELDFLAG_DECIMAL) | (sql_field->flags & ZEROFILL_FLAG ? FIELDFLAG_ZEROFILL : 0) | - (sql_field->decimals << FIELDFLAG_DEC_SHIFT)); + (decimals << FIELDFLAG_DEC_SHIFT)); break; + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: + /* + User specified FLOAT() or DOUBLE() without precision. Change to + FLOATING_POINT_DECIMALS to keep things compatible with earlier MariaDB + versions. + */ + if (decimals >= FLOATING_POINT_DECIMALS) + decimals= FLOATING_POINT_DECIMALS; + /* fall-trough */ case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_TIMESTAMP2: /* fall-through */ @@ -3012,7 +3019,7 @@ int prepare_create_field(Column_definition *sql_field, (sql_field->flags & ZEROFILL_FLAG ? FIELDFLAG_ZEROFILL : 0) | f_settype((uint) sql_field->sql_type) | - (sql_field->decimals << FIELDFLAG_DEC_SHIFT)); + (decimals << FIELDFLAG_DEC_SHIFT)); break; } if (!(sql_field->flags & NOT_NULL_FLAG) || @@ -3065,19 +3072,20 @@ CHARSET_INFO* get_sql_field_charset(Create_field *sql_field, @param column_definitions The list of column definitions, in the physical order in which they appear in the table. - */ +*/ + void promote_first_timestamp_column(List<Create_field> *column_definitions) { - List_iterator<Create_field> it(*column_definitions); + List_iterator_fast<Create_field> it(*column_definitions); Create_field *column_definition; while ((column_definition= it++) != NULL) { - if (is_timestamp_type(column_definition->sql_type) || // 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, - column_definition->def == NULL && // no constant default, + column_definition->default_value == NULL && // no constant default, column_definition->unireg_check == Field::NONE && // no function default column_definition->vcol_info == NULL) { @@ -3116,8 +3124,8 @@ static void check_duplicate_key(THD *thd, if (!key->key_create_info.check_for_duplicate_indexes || key->generated) return; - List_iterator<Key> key_list_iterator(*key_list); - List_iterator<Key_part_spec> key_column_iterator(key->columns); + 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++)) @@ -3141,7 +3149,7 @@ static void check_duplicate_key(THD *thd, Check that the keys have identical columns in the same order. */ - List_iterator<Key_part_spec> k_column_iterator(k->columns); + List_iterator_fast<Key_part_spec> k_column_iterator(k->columns); bool all_columns_are_identical= true; @@ -3219,7 +3227,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, KEY_PART_INFO *key_part_info; int field_no,dup_no; int select_field_pos,auto_increment=0; - List_iterator<Create_field> it(alter_info->create_list); + List_iterator_fast<Create_field> it(alter_info->create_list); 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); @@ -3247,37 +3255,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, !(sql_field->charset= find_bin_collation(sql_field->charset))) DBUG_RETURN(TRUE); - /* - Convert the default value from client character - set into the column character set if necessary. - */ - if (sql_field->def && - save_cs != sql_field->def->collation.collation && - (sql_field->sql_type == MYSQL_TYPE_VAR_STRING || - sql_field->sql_type == MYSQL_TYPE_STRING || - sql_field->sql_type == MYSQL_TYPE_SET || - sql_field->sql_type == MYSQL_TYPE_ENUM)) - { - /* - Starting from 5.1 we work here with a copy of Create_field - created by the caller, not with the instance that was - originally created during parsing. It's OK to create - a temporary item and initialize with it a member of the - copy -- this item will be thrown away along with the copy - at the end of execution, and thus not introduce a dangling - pointer in the parsed tree of a prepared statement or a - stored procedure statement. - */ - sql_field->def= sql_field->def->safe_charset_converter(thd, save_cs); - - if (sql_field->def == NULL) - { - /* Could not convert */ - my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name); - DBUG_RETURN(TRUE); - } - } - if (sql_field->sql_type == MYSQL_TYPE_SET || sql_field->sql_type == MYSQL_TYPE_ENUM) { @@ -3343,36 +3320,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (sql_field->sql_type == MYSQL_TYPE_SET) { uint32 field_length; - if (sql_field->def != NULL) - { - char *not_used; - uint not_used2; - bool not_found= 0; - String str, *def= sql_field->def->val_str(&str); - if (def == NULL) /* SQL "NULL" maps to NULL */ - { - if ((sql_field->flags & NOT_NULL_FLAG) != 0) - { - my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name); - DBUG_RETURN(TRUE); - } - - /* else, NULL is an allowed value */ - (void) find_set(interval, NULL, 0, - cs, ¬_used, ¬_used2, ¬_found); - } - else /* not NULL */ - { - (void) find_set(interval, def->ptr(), def->length(), - cs, ¬_used, ¬_used2, ¬_found); - } - - if (not_found) - { - my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name); - DBUG_RETURN(TRUE); - } - } calculate_interval_lengths(cs, interval, &dummy, &field_length); sql_field->length= field_length + (interval->count - 1); } @@ -3380,29 +3327,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, { uint32 field_length; DBUG_ASSERT(sql_field->sql_type == MYSQL_TYPE_ENUM); - if (sql_field->def != NULL) - { - String str, *def= sql_field->def->val_str(&str); - if (def == NULL) /* SQL "NULL" maps to NULL */ - { - if ((sql_field->flags & NOT_NULL_FLAG) != 0) - { - my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name); - DBUG_RETURN(TRUE); - } - - /* else, the defaults yield the correct length for NULLs. */ - } - else /* not NULL */ - { - def->length(cs->cset->lengthsp(cs, def->ptr(), def->length())); - if (find_type2(interval, def->ptr(), def->length(), cs) == 0) /* not found */ - { - my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name); - DBUG_RETURN(TRUE); - } - } - } calculate_interval_lengths(cs, interval, &field_length, &dummy); sql_field->length= field_length; } @@ -3422,6 +3346,112 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (prepare_blob_field(thd, sql_field)) DBUG_RETURN(TRUE); + if (sql_field->default_value) + { + Virtual_column_info *def= sql_field->default_value; + + if (!sql_field->has_default_expression()) + def->expr_str= null_lex_str; + + if (!def->expr_item->basic_const_item() && !def->flags) + { + Item *expr= def->expr_item; + int err= !expr->fixed && // may be already fixed if ALTER TABLE + expr->fix_fields(thd, &expr); + if (!err) + { + if (expr->result_type() == REAL_RESULT) + { // don't convert floats to string and back, it can be lossy + double res= expr->val_real(); + if (expr->null_value) + expr= new (thd->mem_root) Item_null(thd); + else + expr= new (thd->mem_root) Item_float(thd, res, expr->decimals); + } + else + { + StringBuffer<MAX_FIELD_WIDTH> buf; + String *res= expr->val_str(&buf); + if (expr->null_value) + expr= new (thd->mem_root) Item_null(thd); + else + { + char *str= (char*) thd->strmake(res->ptr(), res->length()); + expr= new (thd->mem_root) Item_string(thd, str, res->length(), res->charset()); + } + } + thd->change_item_tree(&def->expr_item, expr); + } + } + } + + /* + Convert the default value from client character + set into the column character set if necessary. + We can only do this for constants as we have not yet run fix_fields. + */ + if (sql_field->default_value && + sql_field->default_value->expr_item->basic_const_item() && + save_cs != sql_field->default_value->expr_item->collation.collation && + (sql_field->sql_type == MYSQL_TYPE_VAR_STRING || + sql_field->sql_type == MYSQL_TYPE_STRING || + sql_field->sql_type == MYSQL_TYPE_SET || + sql_field->sql_type == MYSQL_TYPE_TINY_BLOB || + sql_field->sql_type == MYSQL_TYPE_MEDIUM_BLOB || + sql_field->sql_type == MYSQL_TYPE_LONG_BLOB || + sql_field->sql_type == MYSQL_TYPE_BLOB || + sql_field->sql_type == MYSQL_TYPE_ENUM)) + { + Item *item; + if (!(item= sql_field->default_value->expr_item-> + safe_charset_converter(thd, save_cs))) + { + /* Could not convert */ + my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name); + DBUG_RETURN(TRUE); + } + /* Fix for prepare statement */ + thd->change_item_tree(&sql_field->default_value->expr_item, item); + } + + if (sql_field->default_value && + sql_field->default_value->expr_item->basic_const_item() && + (sql_field->sql_type == MYSQL_TYPE_SET || + sql_field->sql_type == MYSQL_TYPE_ENUM)) + { + StringBuffer<MAX_FIELD_WIDTH> str; + String *def= sql_field->default_value->expr_item->val_str(&str); + bool not_found; + if (def == NULL) /* SQL "NULL" maps to NULL */ + { + not_found= sql_field->flags & NOT_NULL_FLAG; + } + else + { + not_found= false; + if (sql_field->sql_type == MYSQL_TYPE_SET) + { + char *not_used; + uint not_used2; + find_set(sql_field->interval, def->ptr(), def->length(), + sql_field->charset, ¬_used, ¬_used2, ¬_found); + } + else /* MYSQL_TYPE_ENUM */ + { + def->length(sql_field->charset->cset->lengthsp(sql_field->charset, + def->ptr(), def->length())); + not_found= !find_type2(sql_field->interval, def->ptr(), + def->length(), sql_field->charset); + } + } + + if (not_found) + { + my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name); + DBUG_RETURN(TRUE); + } + } + if (!(sql_field->flags & NOT_NULL_FLAG)) null_fields++; @@ -3459,7 +3489,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, file->ha_table_flags() & HA_CAN_BIT_FIELD) total_uneven_bit_length-= sql_field->length & 7; - sql_field->def= dup_field->def; + sql_field->default_value= dup_field->default_value; sql_field->sql_type= dup_field->sql_type; /* @@ -3573,7 +3603,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, therefore mark it as unsafe. */ if (select_field_count > 0 && auto_increment) - thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_CREATE_SELECT_AUTOINC); + thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_CREATE_SELECT_AUTOINC); /* Create keys */ @@ -4124,7 +4154,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, it is NOT NULL, not an AUTO_INCREMENT field, not a TIMESTAMP and not updated trough a NOW() function. */ - if (!sql_field->def && + if (!sql_field->default_value && !sql_field->has_default_function() && (sql_field->flags & NOT_NULL_FLAG) && !is_timestamp_type(sql_field->sql_type)) @@ -4134,7 +4164,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->default_value && is_timestamp_type(sql_field->sql_type) && (sql_field->flags & NOT_NULL_FLAG) && (type == Field::NONE || type == Field::TIMESTAMP_UN_FIELD)) @@ -4158,6 +4188,31 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, } } + /* Check table level constraints */ + create_info->check_constraint_list= &alter_info->check_constraint_list; + { + uint nr= 1; + List_iterator_fast<Virtual_column_info> c_it(alter_info->check_constraint_list); + Virtual_column_info *check; + while ((check= c_it++)) + { + if (!check->name.length) + make_unique_constraint_name(thd, &check->name, + &alter_info->check_constraint_list, + &nr); + + if (check_string_char_length(&check->name, 0, NAME_CHAR_LEN, + system_charset_info, 1)) + { + my_error(ER_TOO_LONG_IDENT, MYF(0), key->name.str); + DBUG_RETURN(TRUE); + } + if (check_expression(check, "CONSTRAINT CHECK", + check->name.str ? check->name.str : "", 0)) + DBUG_RETURN(TRUE); + } + } + /* Give warnings for not supported table options */ #if defined(WITH_ARIA_STORAGE_ENGINE) extern handlerton *maria_hton; @@ -4226,7 +4281,7 @@ bool validate_comment_length(THD *thd, LEX_STRING *comment, size_t max_len, create_info Table create information DESCRIPTION - If the table character set was not given explicitely, + If the table character set was not given explicitly, let's fetch the database default character set and apply it to the table. */ @@ -4273,7 +4328,7 @@ static bool prepare_blob_field(THD *thd, Column_definition *sql_field) /* Convert long VARCHAR columns to TEXT or BLOB */ char warn_buff[MYSQL_ERRMSG_SIZE]; - if (sql_field->def || thd->is_strict_mode()) + if (thd->is_strict_mode()) { my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), sql_field->field_name, static_cast<ulong>(MAX_FIELD_VARCHARLENGTH / @@ -4352,7 +4407,7 @@ void sp_prepare_create_field(THD *thd, Column_definition *sql_field) FIELDFLAG_TREAT_BIT_AS_CHAR; } sql_field->create_length_to_internal_length(); - DBUG_ASSERT(sql_field->def == 0); + DBUG_ASSERT(sql_field->default_value == 0); /* Can't go wrong as sql_field->def is not defined */ (void) prepare_blob_field(thd, sql_field); } @@ -4635,7 +4690,8 @@ err: 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 one creates a temporary table, its is automatically opened and its + TABLE_SHARE is added to THD::all_temp_tables list. Note that this function assumes that caller already have taken exclusive metadata lock on table being created or used some other @@ -4695,20 +4751,22 @@ int create_table_impl(THD *thd, /* Check if table exists */ if (create_info->tmp_table()) { - TABLE *tmp_table; - if (find_and_use_temporary_table(thd, db, table_name, &tmp_table)) - goto err; + /* + 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, table_name); + if (tmp_table) { bool table_creation_was_logged= tmp_table->s->table_creation_was_logged; if (options.or_replace()) { - bool tmp; /* We are using CREATE OR REPLACE on an existing temporary table Remove the old table so that we can re-create it. */ - if (drop_temporary_table(thd, tmp_table, &tmp)) + if (thd->drop_temporary_table(tmp_table, NULL, true)) goto err; } else if (options.if_not_exists()) @@ -4847,17 +4905,12 @@ int create_table_impl(THD *thd, create_info->table= 0; 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, create_info->db_type, frm, path, - db, table_name, true, true); + TABLE *table= thd->create_and_open_tmp_table(create_info->db_type, frm, + path, db, table_name, true); if (!table) { - (void) rm_temporary_table(create_info->db_type, path); + (void) thd->rm_temporary_table(create_info->db_type, path); goto err; } @@ -4889,7 +4942,7 @@ int create_table_impl(THD *thd, open_table_from_share(thd, &share, "", 0, (uint) READ_ALL, 0, &table, true)); if (!result) - (void) closefrm(&table, 0); + (void) closefrm(&table); free_table_share(&share); @@ -5119,6 +5172,38 @@ make_unique_key_name(THD *thd, const char *field_name,KEY *start,KEY *end) return (char*) "not_specified"; // Should never happen } +/** + Make an unique name for constraints without a name +*/ + +static void make_unique_constraint_name(THD *thd, LEX_STRING *name, + 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 (;;) + { + Virtual_column_info *check; + char *real_end= int10_to_str((*nr)++, end, 10); + it.rewind(); + while ((check= it++)) + { + if (check->name.str && + !my_strcasecmp(system_charset_info, buff, check->name.str)) + break; + } + if (!check) // Found unique name + { + name->length= (size_t) (real_end - buff); + name->str= thd->strmake(buff, name->length); + return; + } + } +} + /**************************************************************************** ** Alter a table definition @@ -5424,6 +5509,9 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, /* We have to write the query before we unlock the tables. */ + if (thd->is_current_stmt_binlog_disabled()) + goto err; + if (thd->is_current_stmt_binlog_format_row()) { /* @@ -5494,6 +5582,21 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, */ if (!table->view) { + /* + After opening a MERGE table add the children to the query list of + tables, so that children tables info can be used on "CREATE TABLE" + statement generation by the binary log. + Note that placeholders don't have the handler open. + */ + if (table->table->file->extra(HA_EXTRA_ADD_CHILDREN_LIST)) + goto err; + + /* + As the reference table is temporary and may not exist on slave, we must + force the ENGINE to be present into CREATE TABLE. + */ + create_info->used_fields|= HA_CREATE_USED_ENGINE; + int result __attribute__((unused))= show_create_table(thd, table, &query, create_info, WITH_DB_NAME); @@ -5797,7 +5900,19 @@ drop_create_field: } } } - else /* Alter_drop::KEY */ + else if (drop->type == Alter_drop::CHECK_CONSTRAINT) + { + for (uint i=table->s->field_check_constraints; i < table->s->table_check_constraints; i++) + { + if (my_strcasecmp(system_charset_info, drop->name, + table->check_constraints[i]->name.str) == 0) + { + remove_drop= FALSE; + break; + } + } + } + else /* Alter_drop::KEY and Alter_drop::FOREIGN_KEY */ { uint n_key; if (drop->type != Alter_drop::FOREIGN_KEY) @@ -5854,7 +5969,7 @@ drop_create_field: push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_CANT_DROP_FIELD_OR_KEY, ER_THD(thd, ER_CANT_DROP_FIELD_OR_KEY), - drop->name); + drop->type_name(), drop->name); drop_it.remove(); if (alter_info->drop_list.is_empty()) alter_info->flags&= ~(Alter_info::ALTER_DROP_COLUMN | @@ -6173,6 +6288,10 @@ static bool fill_alter_inplace_info(THD *thd, /* Check for: ALTER TABLE FORCE, ALTER TABLE ENGINE and OPTIMIZE TABLE. */ if (alter_info->flags & Alter_info::ALTER_RECREATE) ha_alter_info->handler_flags|= Alter_inplace_info::RECREATE_TABLE; + if (alter_info->flags & Alter_info::ALTER_ADD_CHECK_CONSTRAINT) + ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_ADD_CHECK_CONSTRAINT; + if (alter_info->flags & Alter_info::ALTER_DROP_CHECK_CONSTRAINT) + ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_DROP_CHECK_CONSTRAINT; /* If we altering table with old VARCHAR fields we will be automatically @@ -6920,6 +7039,14 @@ static bool is_inplace_alter_impossible(TABLE *table, if (!table->s->mysql_version) DBUG_RETURN(true); + /* + If we are using a MySQL 5.7 table with virtual fields, ALTER TABLE must + recreate the table as we need to rewrite generated fields + */ + if (table->s->mysql_version > 50700 && table->s->mysql_version < 100000 && + table->s->virtual_fields) + DBUG_RETURN(TRUE); + DBUG_RETURN(false); } @@ -7143,7 +7270,8 @@ static bool mysql_inplace_alter_table(THD *thd, HA_EXTRA_NOT_USED, NULL); table_list->table= table= NULL; - close_temporary_table(thd, altered_table, true, false); + + thd->drop_temporary_table(altered_table, NULL, false); /* Replace the old .FRM with the new .FRM, but keep the old name for now. @@ -7233,7 +7361,7 @@ 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 ? */ } - close_temporary_table(thd, altered_table, true, false); + 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); @@ -7329,6 +7457,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, List_iterator<Create_field> find_it(new_create_list); List_iterator<Create_field> field_it(new_create_list); List<Key_part_spec> key_parts; + List<Virtual_column_info> new_constraint_list; uint db_create_options= (table->s->db_create_options & ~(HA_OPTION_PACK_RECORD)); uint used_fields; @@ -7473,12 +7602,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, } if (alter) { - if (def->sql_type == MYSQL_TYPE_BLOB) - { - my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), def->change); - goto err; - } - if ((def->def=alter->def)) // Use new default + if ((def->default_value= alter->default_value)) def->flags&= ~NO_DEFAULT_VALUE_FLAG; else def->flags|= NO_DEFAULT_VALUE_FLAG; @@ -7752,6 +7876,33 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, } } + /* Add all table level constraints which are not in the drop list */ + if (table->s->table_check_constraints) + { + TABLE_SHARE *share= table->s; + + for (uint i= share->field_check_constraints; + i < share->table_check_constraints ; i++) + { + Virtual_column_info *check= table->check_constraints[i]; + Alter_drop *drop; + drop_it.rewind(); + while ((drop=drop_it++)) + { + if (drop->type == Alter_drop::CHECK_CONSTRAINT && + !my_strcasecmp(system_charset_info, check->name.str, drop->name)) + { + drop_it.remove(); + break; + } + } + if (!drop) + new_constraint_list.push_back(check, thd->mem_root); + } + } + /* Add new constraints */ + new_constraint_list.append(&alter_info->check_constraint_list); + if (alter_info->drop_list.elements) { Alter_drop *drop; @@ -7760,8 +7911,9 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, switch (drop->type) { case Alter_drop::KEY: case Alter_drop::COLUMN: - my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0), - alter_info->drop_list.head()->name); + case Alter_drop::CHECK_CONSTRAINT: + 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. @@ -7769,12 +7921,6 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, } } } - if (alter_info->alter_list.elements) - { - my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0), - alter_info->alter_list.head()->name); - goto err; - } if (!create_info->comment.str) { @@ -7807,6 +7953,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, rc= FALSE; alter_info->create_list.swap(new_create_list); alter_info->key_list.swap(new_key_list); + alter_info->check_constraint_list.swap(new_constraint_list); err: DBUG_RETURN(rc); } @@ -8129,6 +8276,72 @@ static bool fk_prepare_copy_alter_table(THD *thd, TABLE *table, DBUG_RETURN(false); } +/** + Rename temporary table and/or turn indexes on/off without touching .FRM. + Its a variant of simple_rename_or_index_change() to be used exclusively + for temporary tables. + + @param thd Thread handler + @param table_list TABLE_LIST for the table to change + @param keys_onoff ENABLE or DISABLE KEYS? + @param alter_ctx ALTER TABLE runtime context. + + @return Operation status + @retval false Success + @retval true Failure +*/ +static bool +simple_tmp_rename_or_index_change(THD *thd, TABLE_LIST *table_list, + Alter_info::enum_enable_or_disable keys_onoff, + Alter_table_ctx *alter_ctx) +{ + DBUG_ENTER("simple_tmp_rename_or_index_change"); + + TABLE *table= table_list->table; + bool error= false; + + DBUG_ASSERT(table->s->tmp_table); + + if (keys_onoff != Alter_info::LEAVE_AS_IS) + { + THD_STAGE_INFO(thd, stage_manage_keys); + error= alter_table_manage_keys(table, table->file->indexes_are_disabled(), + keys_onoff); + } + + if (!error && alter_ctx->is_table_renamed()) + { + THD_STAGE_INFO(thd, stage_rename); + + /* + If THD::rename_temporary_table() fails, there is no need to rename it + back to the original name (unlike the case for non-temporary tables), + as it was an allocation error and the table was not renamed. + */ + error= thd->rename_temporary_table(table, alter_ctx->new_db, + alter_ctx->new_alias); + } + + if (!error) + { + int res= 0; + /* + We do not replicate alter table statement on temporary tables under + ROW-based replication. + */ + if (!thd->is_current_stmt_binlog_format_row()) + { + res= write_bin_log(thd, true, thd->query(), thd->query_length()); + } + if (res != 0) + error= true; + else + my_ok(thd); + } + + DBUG_RETURN(error); +} + /** Rename table and/or turn indexes on/off without touching .FRM @@ -8165,6 +8378,7 @@ simple_rename_or_index_change(THD *thd, TABLE_LIST *table_list, if (lock_tables(thd, table_list, alter_ctx->tables_opened, 0)) DBUG_RETURN(true); + THD_STAGE_INFO(thd, stage_manage_keys); error= alter_table_manage_keys(table, table->file->indexes_are_disabled(), keys_onoff); @@ -8385,7 +8599,12 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, { if (table->s->tmp_table != NO_TMP_TABLE) { - if (find_temporary_table(thd, alter_ctx.new_db, alter_ctx.new_name)) + /* + Check whether a temporary table exists with same requested new name. + If such table exists, there must be a corresponding TABLE_SHARE in + THD::all_temp_tables list. + */ + if (thd->find_tmp_table_share(alter_ctx.new_db, alter_ctx.new_name)) { my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alter_ctx.new_alias); DBUG_RETURN(true); @@ -8480,7 +8699,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, till this point for the alter operation. */ if ((alter_info->flags & Alter_info::ADD_FOREIGN_KEY) && - check_fk_parent_table_access(thd, create_info, alter_info)) + check_fk_parent_table_access(thd, create_info, alter_info, new_db)) DBUG_RETURN(true); /* @@ -8538,29 +8757,48 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, thd->get_stmt_da()->current_statement_warn_count()); my_ok(thd, 0L, 0L, alter_ctx.tmp_name); - if (write_bin_log(thd, true, thd->query(), thd->query_length())) - DBUG_RETURN(true); + /* We don't replicate alter table statement on temporary tables */ + if (table->s->tmp_table == NO_TMP_TABLE || + !thd->is_current_stmt_binlog_format_row()) + { + if (write_bin_log(thd, true, thd->query(), thd->query_length())) + DBUG_RETURN(true); + } DBUG_RETURN(false); } + /* + Test if we are only doing RENAME or KEYS ON/OFF. This works + as we are testing if flags == 0 above. + */ if (!(alter_info->flags & ~(Alter_info::ALTER_RENAME | Alter_info::ALTER_KEYS_ONOFF)) && alter_info->requested_algorithm != - Alter_info::ALTER_TABLE_ALGORITHM_COPY && - !table->s->tmp_table) // no need to touch frm + Alter_info::ALTER_TABLE_ALGORITHM_COPY) // No need to touch frm. { - // This requires X-lock, no other lock levels supported. - if (alter_info->requested_lock != Alter_info::ALTER_TABLE_LOCK_DEFAULT && - alter_info->requested_lock != Alter_info::ALTER_TABLE_LOCK_EXCLUSIVE) + bool res; + + if (!table->s->tmp_table) { - my_error(ER_ALTER_OPERATION_NOT_SUPPORTED, MYF(0), - "LOCK=NONE/SHARED", "LOCK=EXCLUSIVE"); - DBUG_RETURN(true); + // This requires X-lock, no other lock levels supported. + if (alter_info->requested_lock != Alter_info::ALTER_TABLE_LOCK_DEFAULT && + alter_info->requested_lock != Alter_info::ALTER_TABLE_LOCK_EXCLUSIVE) + { + my_error(ER_ALTER_OPERATION_NOT_SUPPORTED, MYF(0), + "LOCK=NONE/SHARED", "LOCK=EXCLUSIVE"); + DBUG_RETURN(true); + } + res= simple_rename_or_index_change(thd, table_list, + alter_info->keys_onoff, + &alter_ctx); + } + else + { + res= simple_tmp_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); } @@ -8821,11 +9059,11 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, // We assume that the table is non-temporary. DBUG_ASSERT(!table->s->tmp_table); - if (!(altered_table= open_table_uncached(thd, new_db_type, &frm, - alter_ctx.get_tmp_path(), - alter_ctx.new_db, - alter_ctx.tmp_name, - true, false))) + if (!(altered_table= + thd->create_and_open_tmp_table(new_db_type, &frm, + alter_ctx.get_tmp_path(), + alter_ctx.new_db, alter_ctx.tmp_name, + false))) goto err_new_table_cleanup; /* Set markers for fields in TABLE object for altered table. */ @@ -8839,7 +9077,10 @@ 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); restore_record(altered_table, s->default_values); // Create empty record - if (altered_table->default_field && altered_table->update_default_fields()) + /* Check that we can call default functions with default field values */ + altered_table->reset_default_fields(); + if (altered_table->default_field && + altered_table->update_default_fields(0, 1)) goto err_new_table_cleanup; // Ask storage engine whether to use copy or in-place @@ -8865,7 +9106,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, { ha_alter_info.report_unsupported_error("LOCK=NONE/SHARED", "LOCK=EXCLUSIVE"); - close_temporary_table(thd, altered_table, true, false); + thd->drop_temporary_table(altered_table, NULL, false); goto err_new_table_cleanup; } break; @@ -8876,7 +9117,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, Alter_info::ALTER_TABLE_LOCK_NONE) { ha_alter_info.report_unsupported_error("LOCK=NONE", "LOCK=SHARED"); - close_temporary_table(thd, altered_table, true, false); + thd->drop_temporary_table(altered_table, NULL, false); goto err_new_table_cleanup; } break; @@ -8890,7 +9131,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, { ha_alter_info.report_unsupported_error("ALGORITHM=INPLACE", "ALGORITHM=COPY"); - close_temporary_table(thd, altered_table, true, false); + thd->drop_temporary_table(altered_table, NULL, false); goto err_new_table_cleanup; } // COPY with LOCK=NONE is not supported, no point in trying. @@ -8898,7 +9139,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, Alter_info::ALTER_TABLE_LOCK_NONE) { ha_alter_info.report_unsupported_error("LOCK=NONE", "LOCK=SHARED"); - close_temporary_table(thd, altered_table, true, false); + thd->drop_temporary_table(altered_table, NULL, false); goto err_new_table_cleanup; } // Otherwise use COPY @@ -8906,7 +9147,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, break; case HA_ALTER_ERROR: default: - close_temporary_table(thd, altered_table, true, false); + thd->drop_temporary_table(altered_table, NULL, false); goto err_new_table_cleanup; } @@ -8925,7 +9166,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, } else { - close_temporary_table(thd, altered_table, true, false); + thd->drop_temporary_table(altered_table, NULL, false); } } @@ -8978,13 +9219,18 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, if (create_info->tmp_table()) { - if (!open_table_uncached(thd, new_db_type, &frm, - alter_ctx.get_tmp_path(), - alter_ctx.new_db, alter_ctx.tmp_name, - true, true)) + TABLE *tmp_table= + thd->create_and_open_tmp_table(new_db_type, &frm, + alter_ctx.get_tmp_path(), + alter_ctx.new_db, alter_ctx.tmp_name, + true); + if (!tmp_table) + { goto err_new_table_cleanup; + } } + /* Open the table since we need to copy the data. */ if (table->s->tmp_table != NO_TMP_TABLE) { @@ -8992,18 +9238,24 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, tbl.init_one_table(alter_ctx.new_db, strlen(alter_ctx.new_db), alter_ctx.tmp_name, strlen(alter_ctx.tmp_name), alter_ctx.tmp_name, TL_READ_NO_INSERT); - /* Table is in thd->temporary_tables */ - (void) open_temporary_table(thd, &tbl); + /* + Table can be found in the list of open tables in THD::all_temp_tables + list. + */ + tbl.table= thd->find_temporary_table(&tbl); new_table= tbl.table; } else { - /* table is a normal table: Create temporary table in same directory */ - /* Open our intermediate table. */ - new_table= open_table_uncached(thd, new_db_type, &frm, - alter_ctx.get_tmp_path(), - alter_ctx.new_db, alter_ctx.tmp_name, - true, true); + /* + table is a normal table: Create temporary table in same directory. + Open our intermediate table. + */ + new_table= + thd->create_and_open_tmp_table(new_db_type, &frm, + alter_ctx.get_tmp_path(), + alter_ctx.new_db, alter_ctx.tmp_name, + true); } if (!new_table) goto err_new_table_cleanup; @@ -9071,10 +9323,10 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, new_table->s->table_creation_was_logged= table->s->table_creation_was_logged; /* Remove link to old table and rename the new one */ - close_temporary_table(thd, table, true, true); + thd->drop_temporary_table(table, NULL, true); /* Should pass the 'new_name' as we store table name in the cache */ - if (rename_temporary_table(thd, new_table, - alter_ctx.new_db, alter_ctx.new_name)) + if (thd->rename_temporary_table(new_table, alter_ctx.new_db, + alter_ctx.new_name)) goto err_new_table_cleanup; /* We don't replicate alter table statement on temporary tables */ if (!thd->is_current_stmt_binlog_format_row() && @@ -9086,10 +9338,10 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, /* Close the intermediate table that will be the new table, but do - not delete it! Even altough MERGE tables do not have their children - attached here it is safe to call close_temporary_table(). + not delete it! Even though MERGE tables do not have their children + attached here it is safe to call THD::drop_temporary_table(). */ - close_temporary_table(thd, new_table, true, false); + thd->drop_temporary_table(new_table, NULL, false); new_table= NULL; DEBUG_SYNC(thd, "alter_table_before_rename_result_table"); @@ -9231,8 +9483,7 @@ err_new_table_cleanup: my_free(const_cast<uchar*>(frm.str)); if (new_table) { - /* close_temporary_table() frees the new_table pointer. */ - close_temporary_table(thd, new_table, true, true); + thd->drop_temporary_table(new_table, NULL, true); } else (void) quick_rm_table(thd, new_db_type, @@ -9278,6 +9529,9 @@ err_new_table_cleanup: err_with_mdl_after_alter: /* the table was altered. binlog the operation */ + DBUG_ASSERT(!(mysql_bin_log.is_open() && + thd->is_current_stmt_binlog_format_row() && + (create_info->tmp_table()))); write_bin_log(thd, true, thd->query(), thd->query_length()); err_with_mdl: @@ -9416,7 +9670,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, Old fields keep their current values, and therefore should not be present in the set of autoupdate fields. */ - if ((*ptr)->has_insert_default_function()) + if ((*ptr)->default_value) { *(dfield_ptr++)= *ptr; ++to->s->default_fields; @@ -9463,7 +9717,9 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, 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(); - to->mark_virtual_columns_for_write(TRUE); + /* Add virtual columns to vcol_set to ensure they are updated */ + if (to->vfield) + to->mark_virtual_columns_for_write(TRUE); if (init_read_record(&info, thd, from, (SQL_SELECT *) 0, file_sort, 1, 1, FALSE)) goto err; @@ -9473,8 +9729,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, to->file->extra(HA_EXTRA_IGNORE_DUP_KEY); thd->get_stmt_da()->reset_current_row_for_warning(); restore_record(to, s->default_values); // Create empty record - if (to->default_field && to->update_default_fields()) - goto err; + to->reset_default_fields(); thd->progress.max_counter= from->file->records(); time_to_report_progress= MY_HOW_OFTEN_TO_WRITE/10; @@ -9515,8 +9770,14 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, copy_ptr->do_copy(copy_ptr); } prev_insert_id= to->file->next_insert_id; + if (to->default_field) + to->update_default_fields(0, ignore); if (to->vfield) update_virtual_fields(thd, to, VCOL_UPDATE_FOR_WRITE); + + /* This will set thd->is_error() if fatal failure */ + if (to->verify_constraints(ignore) == VIEW_CHECK_SKIP) + continue; if (thd->is_error()) { error= 1; @@ -9586,7 +9847,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, THD_STAGE_INFO(thd, stage_enabling_keys); thd_progress_next_stage(thd); - if (error > 0) + if (error > 0 && !from->s->tmp_table) { /* We are going to drop the temporary table */ to->file->extra(HA_EXTRA_PREPARE_FOR_DROP); @@ -9615,7 +9876,8 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, to->file->ha_release_auto_increment(); if (to->file->ha_external_lock(thd,F_UNLCK)) error=1; - if (error < 0 && to->file->extra(HA_EXTRA_PREPARE_FOR_RENAME)) + if (error < 0 && !from->s->tmp_table && + to->file->extra(HA_EXTRA_PREPARE_FOR_RENAME)) error= 1; thd_progress_end(thd); DBUG_RETURN(error > 0 ? -1 : 0); @@ -9732,7 +9994,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, /* Allow to open real tables only. */ table->required_type= FRMTYPE_TABLE; - if (open_temporary_tables(thd, table) || + if (thd->open_temporary_tables(table) || open_and_lock_tables(thd, table, FALSE, 0)) { t= NULL; |