summaryrefslogtreecommitdiff
path: root/sql/sql_table.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_table.cc')
-rw-r--r--sql/sql_table.cc1289
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, &not_used_1, &not_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, &not_used, 0);
- thd->lex->create_info.options= save_thd_create_info_options;
+ res= open_tables(thd, *create_info, &thd->lex->query_tables, &not_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
}