Commit e618f7e9 authored by Nikita Malyavin's avatar Nikita Malyavin

MDEV-22506 Malformed error message for ER_KEY_CONTAINS_PERIOD_FIELDS

Though this is an error message task, the problem was deep in the
`mysql_prepare_create_table` implementation. The problem is described as
follows:

1. `append_system_key_parts` was called before
`mysql_prepare_create_table`, though key name generation was done close to
the latest stage of the latter.
2. We can't move `append_system_key_parts` in the end, because system keys
should be appended before some checks done.
3. If the checks from `append_system_key_parts` are moved to the end of
`mysql_prepare_create_table`, then some other inappropriate errors are
issued. like `ER_DUP_FIELDNAME`.

To have key name specified in error message, name generation should be done
before the checks, which consequenced in more changes.

The final design for key initialization in `mysql_prepare_create_table`
follows. The initialization is done in three phases:
1. Calculate a total number of keys created with respect to keys ignored.
 Allocate KEY* buffer.
2. Generate unique names; calculate a total number of key parts.
 Make early checks. Allocate KEY_PART_INFO* buffer.
3. Initialize key parts, make the rest of the checks.
parent a79c6e36
...@@ -127,7 +127,7 @@ ERROR HY000: Period `p` is not found in table ...@@ -127,7 +127,7 @@ ERROR HY000: Period `p` is not found in table
create or replace table t(id int, s date, e date, create or replace table t(id int, s date, e date,
period for p(s,e), period for p(s,e),
primary key(id, s, p without overlaps)); primary key(id, s, p without overlaps));
ERROR HY000: Key `(null)` cannot explicitly include column `s` ERROR HY000: Key `PRIMARY` cannot explicitly include column `s`
create or replace table t(id int, s date, e date, create or replace table t(id int, s date, e date,
period for p(s,e), period for p(s,e),
primary key(id)); primary key(id));
......
...@@ -75,9 +75,8 @@ static int copy_data_between_tables(THD *, TABLE *,TABLE *, ...@@ -75,9 +75,8 @@ static int copy_data_between_tables(THD *, TABLE *,TABLE *,
ha_rows *, ha_rows *, ha_rows *, ha_rows *,
Alter_info::enum_enable_or_disable, Alter_info::enum_enable_or_disable,
Alter_table_ctx *); Alter_table_ctx *);
static bool append_system_key_parts(THD *thd, HA_CREATE_INFO *create_info, static int append_system_key_parts(THD *thd, HA_CREATE_INFO *create_info,
Alter_info *alter_info, KEY **key_info, Key *key);
uint key_count);
static int mysql_prepare_create_table(THD *, HA_CREATE_INFO *, Alter_info *, static int mysql_prepare_create_table(THD *, HA_CREATE_INFO *, Alter_info *,
uint *, handler *, KEY **, uint *, int); uint *, handler *, KEY **, uint *, int);
static uint blob_length_by_type(enum_field_types type); static uint blob_length_by_type(enum_field_types type);
...@@ -1823,10 +1822,6 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) ...@@ -1823,10 +1822,6 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
strxmov(shadow_frm_name, shadow_path, reg_ext, NullS); strxmov(shadow_frm_name, shadow_path, reg_ext, NullS);
if (flags & WFRM_WRITE_SHADOW) if (flags & WFRM_WRITE_SHADOW)
{ {
if (append_system_key_parts(lpt->thd, lpt->create_info, lpt->alter_info,
&lpt->key_info_buffer, 0))
DBUG_RETURN(true);
if (mysql_prepare_create_table(lpt->thd, lpt->create_info, lpt->alter_info, if (mysql_prepare_create_table(lpt->thd, lpt->create_info, lpt->alter_info,
&lpt->db_options, lpt->table->file, &lpt->db_options, lpt->table->file,
&lpt->key_info_buffer, &lpt->key_count, &lpt->key_info_buffer, &lpt->key_count,
...@@ -3557,7 +3552,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, ...@@ -3557,7 +3552,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
Create_field *sql_field,*dup_field; Create_field *sql_field,*dup_field;
uint field,null_fields,max_key_length; uint field,null_fields,max_key_length;
ulong record_offset= 0; ulong record_offset= 0;
KEY *key_info;
KEY_PART_INFO *key_part_info; KEY_PART_INFO *key_part_info;
int field_no,dup_no; int field_no,dup_no;
int select_field_pos,auto_increment=0; int select_field_pos,auto_increment=0;
...@@ -3875,6 +3869,57 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, ...@@ -3875,6 +3869,57 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
thd->abort_on_warning= sav_abort_on_warning; thd->abort_on_warning= sav_abort_on_warning;
} }
} }
KEY *key_info= *key_info_buffer= (KEY*)thd->calloc(sizeof(KEY) * (*key_count));
if (!*key_info_buffer)
DBUG_RETURN(true); // Out of memory
key_iterator.rewind();
while ((key=key_iterator++))
{
if (key->name.str == ignore_key || key->type == Key::FOREIGN_KEY)
continue;
/* Create the key->ame based on the first column (if not given) */
if (key->type == Key::PRIMARY)
{
if (primary_key)
{
my_message(ER_MULTIPLE_PRI_KEY, ER_THD(thd, ER_MULTIPLE_PRI_KEY),
MYF(0));
DBUG_RETURN(true);
}
key_name=primary_key_name;
primary_key=1;
}
else if (!(key_name= key->name.str))
{
auto field_name= key->columns.elem(0)->field_name;
it.rewind();
while ((sql_field=it++) &&
lex_string_cmp(system_charset_info,
&field_name,
&sql_field->field_name));
if (sql_field)
field_name= sql_field->field_name;
key_name=make_unique_key_name(thd, field_name.str,
*key_info_buffer, key_info);
}
if (check_if_keyname_exists(key_name, *key_info_buffer, key_info))
{
my_error(ER_DUP_KEYNAME, MYF(0), key_name);
DBUG_RETURN(true);
}
key_info->name.str= (char*) key_name;
key_info->name.length= strlen(key_name);
key->name= key_info->name;
int parts_added= append_system_key_parts(thd, create_info, key);
if (parts_added < 0)
DBUG_RETURN(true);
key_parts += parts_added;
key_info++;
}
tmp=file->max_keys(); tmp=file->max_keys();
if (*key_count > tmp) if (*key_count > tmp)
{ {
...@@ -3882,11 +3927,11 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, ...@@ -3882,11 +3927,11 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
} }
(*key_info_buffer)= key_info= (KEY*) thd->calloc(sizeof(KEY) * (*key_count));
key_part_info=(KEY_PART_INFO*) thd->calloc(sizeof(KEY_PART_INFO)*key_parts); key_part_info=(KEY_PART_INFO*) thd->calloc(sizeof(KEY_PART_INFO)*key_parts);
if (!*key_info_buffer || ! key_part_info) if (!key_part_info)
DBUG_RETURN(TRUE); // Out of memory DBUG_RETURN(true); // Out of memory
key_info= *key_info_buffer;
key_iterator.rewind(); key_iterator.rewind();
key_number=0; key_number=0;
for (; (key=key_iterator++) ; key_number++) for (; (key=key_iterator++) ; key_number++)
...@@ -4257,32 +4302,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, ...@@ -4257,32 +4302,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
key_length+= key_part_length; key_length+= key_part_length;
key_part_info++; key_part_info++;
/* Create the key name based on the first column (if not given) */
if (column_nr == 0)
{
if (key->type == Key::PRIMARY)
{
if (primary_key)
{
my_message(ER_MULTIPLE_PRI_KEY, ER_THD(thd, ER_MULTIPLE_PRI_KEY),
MYF(0));
DBUG_RETURN(TRUE);
}
key_name=primary_key_name;
primary_key=1;
}
else if (!(key_name= key->name.str))
key_name=make_unique_key_name(thd, sql_field->field_name.str,
*key_info_buffer, key_info);
if (check_if_keyname_exists(key_name, *key_info_buffer, key_info))
{
my_error(ER_DUP_KEYNAME, MYF(0), key_name);
DBUG_RETURN(TRUE);
}
key_info->name.str= (char*) key_name;
key_info->name.length= strlen(key_name);
}
} }
if (!key_info->name.str || check_column_name(key_info->name.str)) if (!key_info->name.str || check_column_name(key_info->name.str))
{ {
...@@ -4715,72 +4734,75 @@ bool Column_definition::sp_prepare_create_field(THD *thd, MEM_ROOT *mem_root) ...@@ -4715,72 +4734,75 @@ bool Column_definition::sp_prepare_create_field(THD *thd, MEM_ROOT *mem_root)
} }
static bool append_system_key_parts(THD *thd, HA_CREATE_INFO *create_info, /**
Alter_info *alter_info, KEY **key_info, Appends key parts generated by mariadb server.
uint key_count) Adds row_end in UNIQUE keys for system versioning,
and period fields for WITHOUT OVERLAPS.
@param thd Thread data
@param create_info Table create info
@param key Parsed key
@return a number of key parts added to key.
*/
static int append_system_key_parts(THD *thd, HA_CREATE_INFO *create_info,
Key *key)
{ {
const Lex_ident &row_start_field= create_info->vers_info.as_row.start; const Lex_ident &row_start_field= create_info->vers_info.as_row.start;
const Lex_ident &row_end_field= create_info->vers_info.as_row.end; const Lex_ident &row_end_field= create_info->vers_info.as_row.end;
DBUG_ASSERT(!create_info->versioned() || (row_start_field && row_end_field)); DBUG_ASSERT(!create_info->versioned() || (row_start_field && row_end_field));
List_iterator<Key> key_it(alter_info->key_list); int result = 0;
Key *key= NULL; if (create_info->versioned() && (key->type == Key::PRIMARY
|| key->type == Key::UNIQUE))
if (create_info->versioned())
{ {
while ((key=key_it++)) Key_part_spec *key_part=NULL;
List_iterator<Key_part_spec> part_it(key->columns);
while ((key_part=part_it++))
{ {
if (key->type != Key::PRIMARY && key->type != Key::UNIQUE) if (row_start_field.streq(key_part->field_name) ||
continue; row_end_field.streq(key_part->field_name))
break;
Key_part_spec *key_part=NULL; }
List_iterator<Key_part_spec> part_it(key->columns); if (!key_part)
while ((key_part=part_it++)) {
{ key->columns.push_back(new (thd->mem_root)
if (row_start_field.streq(key_part->field_name) ||
row_end_field.streq(key_part->field_name))
break;
}
if (!key_part)
key->columns.push_back(new (thd->mem_root)
Key_part_spec(&row_end_field, 0, true)); Key_part_spec(&row_end_field, 0, true));
result++;
} }
key_it.rewind();
} }
while ((key=key_it++)) if (key->without_overlaps)
{ {
if (key->without_overlaps) DBUG_ASSERT(key->type == Key::PRIMARY || key->type == Key::UNIQUE);
if (!create_info->period_info.is_set()
|| !key->period.streq(create_info->period_info.name))
{ {
DBUG_ASSERT(key->type == Key::PRIMARY || key->type == Key::UNIQUE); my_error(ER_PERIOD_NOT_FOUND, MYF(0), key->period.str);
if (!create_info->period_info.is_set() return -1;
|| !key->period.streq(create_info->period_info.name)) }
{
my_error(ER_PERIOD_NOT_FOUND, MYF(0), key->period.str);
return true;
}
const auto &period_start= create_info->period_info.period.start; const auto &period_start= create_info->period_info.period.start;
const auto &period_end= create_info->period_info.period.end; const auto &period_end= create_info->period_info.period.end;
List_iterator<Key_part_spec> part_it(key->columns); List_iterator<Key_part_spec> part_it(key->columns);
while (Key_part_spec *key_part= part_it++) while (Key_part_spec *key_part= part_it++)
{
if (period_start.streq(key_part->field_name)
|| period_end.streq(key_part->field_name))
{ {
if (period_start.streq(key_part->field_name) my_error(ER_KEY_CONTAINS_PERIOD_FIELDS, MYF(0), key->name.str,
|| period_end.streq(key_part->field_name)) key_part->field_name.str);
{ return -1;
my_error(ER_KEY_CONTAINS_PERIOD_FIELDS, MYF(0), key->name.str,
key_part->field_name);
return true;
}
} }
key->columns.push_back(new (thd->mem_root)
Key_part_spec(&period_end, 0));
key->columns.push_back(new (thd->mem_root)
Key_part_spec(&period_start, 0));
} }
const auto &period= create_info->period_info.period;
key->columns.push_back(new (thd->mem_root)
Key_part_spec(&period.end, 0, true));
key->columns.push_back(new (thd->mem_root)
Key_part_spec(&period.start, 0, true));
result += 2;
} }
return false; return result;
} }
handler *mysql_create_frm_image(THD *thd, const LEX_CSTRING &db, handler *mysql_create_frm_image(THD *thd, const LEX_CSTRING &db,
...@@ -5018,10 +5040,6 @@ handler *mysql_create_frm_image(THD *thd, const LEX_CSTRING &db, ...@@ -5018,10 +5040,6 @@ handler *mysql_create_frm_image(THD *thd, const LEX_CSTRING &db,
} }
#endif #endif
if (append_system_key_parts(thd, create_info, alter_info, key_info,
*key_count))
goto err;
if (mysql_prepare_create_table(thd, create_info, alter_info, &db_options, 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; goto err;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment