MDEV-33658 1/2 Refactoring: extract Key length initialization

mysql_prepare_create_table: Extract a Key initialization part that
relates to length calculation and long unique index designation.

append_system_key_parts call also moves there.

Move this initialization before the duplicate elimination.

Extract WITHOUT OVERPLAPS check into a separate function. It had to be moved
earlier in the code to preserve the order of the error checks, as in the tests.
This commit is contained in:
Nikita Malyavin 2024-10-12 21:32:18 +02:00
parent e33064e0fc
commit ecaedbe299
10 changed files with 362 additions and 251 deletions

View file

@ -38,7 +38,7 @@ CREATE TABLE t_stmt (a VARCHAR(100)) ENGINE = EXAMPLE;
ALTER TABLE t_stmt ADD COLUMN b INT;
CREATE TRIGGER trig_stmt BEFORE INSERT ON t_stmt FOR EACH ROW INSERT INTO t_stmt VALUES (1);
CREATE INDEX i ON t_stmt(a);
ERROR 42000: Too many key parts specified; max 0 parts allowed
ERROR 42000: Specified key was too long; max key length is 0 bytes
CREATE TABLE t_stmt_new ENGINE = EXAMPLE SELECT * FROM t_stmt;
ERROR HY000: Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = ROW and at least one table uses a storage engine limited to statement-based logging
DROP TABLE t_stmt;

View file

@ -75,7 +75,7 @@ ALTER TABLE t_stmt ADD COLUMN b INT;
CREATE TRIGGER trig_stmt BEFORE INSERT ON t_stmt FOR EACH ROW INSERT INTO t_stmt VALUES (1);
--error ER_TOO_MANY_KEY_PARTS
--error ER_TOO_LONG_KEY
CREATE INDEX i ON t_stmt(a);
--error ER_BINLOG_ROW_MODE_AND_STMT_ENGINE

View file

@ -659,6 +659,8 @@ NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL 15000
NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL 16000
ALTER TABLE testdb_wl5522.t1 ADD INDEX idx1 (col_1);
ALTER TABLE testdb_wl5522.t1 ADD INDEX idx6 (col_1(255));
Warnings:
Note 1831 Duplicate index `idx6`. This is deprecated and will be disallowed in a future release
ALTER TABLE testdb_wl5522.t1 ADD INDEX idx10 (col_10(255));
SELECT
col_1 = REPEAT("col1_00001",10),

View file

@ -1075,6 +1075,8 @@ col1 POINT,
col2 POINT
);
CREATE SPATIAL INDEX idx0 ON t2 (col1, col2);
ERROR 42000: Key column 'col1' doesn't exist in table
CREATE SPATIAL INDEX idx0 ON t2 (col0, col2);
ERROR HY000: Incorrect arguments to SPATIAL INDEX
CREATE TABLE t4 (
col0 INTEGER NOT NULL,

View file

@ -1562,7 +1562,7 @@ ALTER TABLE child ADD FOREIGN KEY(p) REFERENCES parent(p);
ERROR HY000: Can't create table `test`.`child` (errno: 150 "Foreign key constraint is incorrectly formed")
show warnings;
Level Code Message
Warning 150 Alter table `test`.`child` with foreign key (p) constraint failed. There is no index in the referenced table where the referenced columns appear as the first columns.
Warning 150 Alter table `test`.`child` with foreign key (p) constraint failed. There is only prefix index in the referenced table where the referenced columns appear as the first columns.
Error 1005 Can't create table `test`.`child` (errno: 150 "Foreign key constraint is incorrectly formed")
Warning 1215 Cannot add foreign key constraint for `child`
ALTER TABLE parent DROP INDEX idx1;
@ -1570,7 +1570,7 @@ ALTER TABLE child ADD FOREIGN KEY(p) REFERENCES parent(p);
Got one of the listed errors
show warnings;
Level Code Message
Warning 150 Alter table `test`.`child` with foreign key (p) constraint failed. There is no index in the referenced table where the referenced columns appear as the first columns.
Warning 150 Alter table `test`.`child` with foreign key (p) constraint failed. There is only prefix index in the referenced table where the referenced columns appear as the first columns.
Error 1005 Can't create table `test`.`child` (errno: 150 "Foreign key constraint is incorrectly formed")
Warning 1215 Cannot add foreign key constraint for `child`
ALTER TABLE child DROP INDEX idx2;

View file

@ -839,8 +839,10 @@ CREATE TABLE t3 (
);
# --error ER_TOO_MANY_KEY_PARTS
--error ER_WRONG_ARGUMENTS
--error ER_KEY_COLUMN_DOES_NOT_EXITS
CREATE SPATIAL INDEX idx0 ON t2 (col1, col2);
--error ER_WRONG_ARGUMENTS
CREATE SPATIAL INDEX idx0 ON t2 (col0, col2);
CREATE TABLE t4 (

View file

@ -171,9 +171,11 @@ Note 1071 Specified key was too long; max key length is 1536 bytes
create index idx3 on worklog5743_8(a2(3072));
Warnings:
Note 1071 Specified key was too long; max key length is 1536 bytes
Note 1831 Duplicate index `idx3`. This is deprecated and will be disallowed in a future release
show warnings;
Level Code Message
Note 1071 Specified key was too long; max key length is 1536 bytes
Note 1831 Duplicate index `idx3`. This is deprecated and will be disallowed in a future release
create index idx4 on worklog5743_8(a1, a2(1533));
ERROR 42000: Specified key was too long; max key length is 1536 bytes
show warnings;
@ -355,6 +357,7 @@ Note 1071 Specified key was too long; max key length is 1536 bytes
create index idx2 on worklog5743(a(3072));
Warnings:
Note 1071 Specified key was too long; max key length is 1536 bytes
Note 1831 Duplicate index `idx2`. This is deprecated and will be disallowed in a future release
SET sql_mode= default;
show create table worklog5743;
Table Create Table

View file

@ -176,7 +176,8 @@ Key::Key(const Key &rhs, MEM_ROOT *mem_root)
name(rhs.name),
option_list(rhs.option_list),
generated(rhs.generated), invisible(false),
without_overlaps(rhs.without_overlaps), old(rhs.old), period(rhs.period)
without_overlaps(rhs.without_overlaps), old(rhs.old), length(rhs.length),
period(rhs.period)
{
list_copy_and_replace_each_value(columns, mem_root);
}

View file

@ -424,6 +424,7 @@ public:
bool invisible;
bool without_overlaps;
bool old;
uint length;
Lex_ident period;
Key(enum Keytype type_par, const LEX_CSTRING *name_arg,
@ -431,7 +432,7 @@ public:
:DDL_options(ddl_options),
type(type_par), key_create_info(default_key_create_info),
name(*name_arg), option_list(NULL), generated(generated_arg),
invisible(false), without_overlaps(false), old(false)
invisible(false), without_overlaps(false), old(false), length(0)
{
key_create_info.algorithm= algorithm_arg;
}
@ -442,7 +443,7 @@ public:
:DDL_options(ddl_options),
type(type_par), key_create_info(*key_info_arg), columns(*cols),
name(*name_arg), option_list(create_opt), generated(generated_arg),
invisible(false), without_overlaps(false), old(false)
invisible(false), without_overlaps(false), old(false), length(0)
{}
Key(const Key &rhs, MEM_ROOT *mem_root);
virtual ~Key() = default;

View file

@ -3558,6 +3558,303 @@ key_add_part_check_null(const handler *file, KEY *key_info,
}
static
my_bool key_check_without_overlaps(THD *thd, HA_CREATE_INFO *create_info,
Alter_info *alter_info,
Key &key)
{
DBUG_ENTER("key_check_without_overlaps");
if (!key.without_overlaps)
DBUG_RETURN(FALSE);
// append_system_key_parts is already called, so we should check all the
// columns except the last two.
const auto &period_start= create_info->period_info.period.start;
const auto &period_end= create_info->period_info.period.end;
List_iterator<Key_part_spec> part_it_forwarded(key.columns);
List_iterator<Key_part_spec> part_it(key.columns);
part_it_forwarded++;
part_it_forwarded++;
while (part_it_forwarded++)
{
Key_part_spec *key_part= part_it++;
if (period_start.streq(key_part->field_name)
|| period_end.streq(key_part->field_name))
{
my_error(ER_KEY_CONTAINS_PERIOD_FIELDS, MYF(0), key.name.str,
key_part->field_name.str);
DBUG_RETURN(TRUE);
}
}
if (key.key_create_info.algorithm == HA_KEY_ALG_HASH ||
key.key_create_info.algorithm == HA_KEY_ALG_LONG_HASH)
{
my_error(ER_KEY_CANT_HAVE_WITHOUT_OVERLAPS, MYF(0), key.name.str);
DBUG_RETURN(TRUE);
}
for (Key &key2: alter_info->key_list)
{
if (key2.type != Key::FOREIGN_KEY)
continue;
DBUG_ASSERT(&key != &key2);
const Foreign_key &fk= (Foreign_key&)key2;
if (fk.update_opt != FK_OPTION_CASCADE)
continue;
for (Key_part_spec& kp: key.columns)
{
for (Key_part_spec& kp2: fk.columns)
{
if (kp.field_name.streq(kp2.field_name))
{
my_error(ER_KEY_CANT_HAVE_WITHOUT_OVERLAPS, MYF(0), key.name.str);
DBUG_RETURN(TRUE);
}
}
}
}
create_info->period_info.unique_keys++;
DBUG_RETURN(FALSE);
}
static
my_bool init_key_part_spec(THD *thd, Alter_info *alter_info,
const handler *file,
const Key &key, Key_part_spec &kp,
uint max_key_length, uint max_key_part_length,
bool *is_hash_field_needed)
{
DBUG_ENTER("init_key_part_spec");
const Lex_ident &field_name= kp.field_name;
Create_field *column= NULL;
for (Create_field &c: alter_info->create_list)
if (c.field_name.streq(field_name))
column= &c;
/*
Either field is not present or field visibility is > INVISIBLE_USER
*/
if (!column || (column->invisible > INVISIBLE_USER && !kp.generated))
{
my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), field_name.str);
DBUG_RETURN(TRUE);
}
if (DBUG_EVALUATE_IF("test_invisible_index", 0, 1)
&& column->invisible > INVISIBLE_USER
&& !(column->flags & VERS_SYSTEM_FIELD) && !key.invisible)
{
my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name.str);
DBUG_RETURN(TRUE);
}
const Type_handler *type_handler= column->type_handler();
switch(key.type)
{
case Key::FULLTEXT:
if (type_handler->Key_part_spec_init_ft(&kp, *column))
{
my_error(ER_BAD_FT_COLUMN, MYF(0), field_name.str);
DBUG_RETURN(-1);
}
break;
case Key::SPATIAL:
if (type_handler->Key_part_spec_init_spatial(&kp, *column))
DBUG_RETURN(TRUE);
break;
case Key::PRIMARY:
if (column->vcol_info)
{
my_error(ER_PRIMARY_KEY_BASED_ON_GENERATED_COLUMN, MYF(0));
DBUG_RETURN(TRUE);
}
if (type_handler->Key_part_spec_init_primary(&kp, *column, file))
DBUG_RETURN(TRUE);
break;
case Key::MULTIPLE:
if (type_handler->Key_part_spec_init_multiple(&kp, *column, file))
DBUG_RETURN(TRUE);
break;
case Key::FOREIGN_KEY:
if (type_handler->Key_part_spec_init_foreign(&kp, *column, file))
DBUG_RETURN(TRUE);
break;
case Key::UNIQUE:
if (type_handler->Key_part_spec_init_unique(&kp, *column, file,
is_hash_field_needed))
DBUG_RETURN(TRUE);
break;
}
uint key_part_length= type_handler->calc_key_length(*column);
if (kp.length)
{
if (f_is_blob(column->pack_flag))
{
key_part_length= MY_MIN(kp.length,
blob_length_by_type(column->real_field_type())
* column->charset->mbmaxlen);
if (key_part_length > max_key_length ||
key_part_length > max_key_part_length)
{
if (key.type == Key::MULTIPLE)
{
key_part_length= MY_MIN(max_key_length, 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 % column->charset->mbmaxlen;
}
}
}
// Catch invalid use of partial keys
else if (!f_is_geom(column->pack_flag) &&
// is the key partial?
kp.length != key_part_length &&
// is prefix length bigger than field length?
(kp.length > key_part_length ||
// can the field have a partial key?
!type_handler->type_can_have_key_part() ||
// a packed field can't be used in a partial key
f_is_packed(column->pack_flag) ||
// does the storage engine allow prefixed search?
((file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS) &&
// and is this a 'unique' key?
(key.type == Key::PRIMARY || key.type == Key::UNIQUE))))
{
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= kp.length;
}
else if (key_part_length == 0 && (column->flags & NOT_NULL_FLAG) &&
!*is_hash_field_needed)
{
my_error(ER_WRONG_KEY_COLUMN, MYF(0), file->table_type(), field_name.str);
DBUG_RETURN(TRUE);
}
if (key_part_length > max_key_part_length && key.type != Key::FULLTEXT)
{
if (key.type == Key::MULTIPLE)
{
key_part_length= 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 % column->charset->mbmaxlen;
}
else if (key.type != Key::UNIQUE)
{
key_part_length= MY_MIN(max_key_length, max_key_part_length);
my_error(ER_TOO_LONG_KEY, MYF(0), key_part_length);
DBUG_RETURN(TRUE);
}
}
if (key.type == Key::UNIQUE && key_part_length > MY_MIN(max_key_length,
max_key_part_length))
*is_hash_field_needed= true;
/* We can not store key_part_length more than 2^16 - 1 in frm. */
if (*is_hash_field_needed && kp.length > UINT_MAX16)
{
my_error(ER_TOO_LONG_KEYPART, MYF(0), UINT_MAX16);
DBUG_RETURN(TRUE);
}
kp.length= key_part_length;
DBUG_RETURN(FALSE);
}
/**
@brief Initialize the key length and algorithm (if long hash).
This function does:
1. Append system key parts (versioning, periods)
2. Call Type_handler key_part initialization function.
3. Determine the length of each key_part.
4. Calculate the total Key length.
5. Determine if the key is long unique based on its length
smd result from type handler. It'll be saved in
key_create_info.algorithm as HA_KEY_ALG_LONG_HASH.
@return FALSE OK
@return TRUE error
*/
static
my_bool init_key_info(THD *thd, Alter_info *alter_info,
HA_CREATE_INFO *create_info,
const handler *file)
{
DBUG_ENTER("init_key_info");
uint max_key_length= file->max_key_length();
uint max_key_part_length= file->max_key_part_length();
for (Key &key: alter_info->key_list)
{
if (key.type == Key::FOREIGN_KEY)
continue;
int parts_added= append_system_key_parts(thd, create_info, &key);
if (parts_added < 0)
DBUG_RETURN(true);
bool is_hash_field_needed= false;
for (Key_part_spec &kp: key.columns)
{
if (init_key_part_spec(thd, alter_info, file, key, kp,
max_key_length, max_key_part_length,
&is_hash_field_needed))
DBUG_RETURN(TRUE);
key.length+= kp.length;
if (key.length > max_key_length && key.type == Key::UNIQUE)
is_hash_field_needed= true; // for case "a BLOB UNIQUE"
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);
DBUG_RETURN(TRUE);
}
KEY_CREATE_INFO *key_cinfo= &key.key_create_info;
if (is_hash_field_needed)
{
if (key_cinfo->algorithm == HA_KEY_ALG_UNDEF)
key_cinfo->algorithm= HA_KEY_ALG_LONG_HASH;
if (key_cinfo->algorithm != HA_KEY_ALG_HASH &&
key_cinfo->algorithm != HA_KEY_ALG_LONG_HASH)
{
my_error(ER_TOO_LONG_KEY, MYF(0), max_key_length);
DBUG_RETURN(TRUE);
}
}
}
}
DBUG_RETURN(FALSE);
}
/*
Preparation for table creation
@ -3592,7 +3889,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
{
const char *key_name;
Create_field *sql_field,*dup_field;
uint field,null_fields,max_key_length;
uint field,null_fields;
ulong record_offset= 0;
KEY_PART_INFO *key_part_info;
int field_no,dup_no;
@ -3603,7 +3900,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
int select_field_count= C_CREATE_SELECT(create_table_mode);
bool tmp_table= create_table_mode == C_ALTER_TABLE;
const bool create_simple= thd->lex->create_simple();
bool is_hash_field_needed= false;
const Column_derived_attributes dattr(create_info->default_table_charset);
const Column_bulk_alter_attributes
battr(create_info->alter_table_convert_to_charset);
@ -3639,7 +3935,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
create_info->versioned());
null_fields= 0;
create_info->varchar= 0;
max_key_length= file->max_key_length();
/* Handle creation of sequences */
if (create_info->sequence)
@ -3838,6 +4133,9 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
/* special marker for keys to be ignored */
static char ignore_key[1];
if (init_key_info(thd, alter_info, create_info, file))
DBUG_RETURN(TRUE);
/* Calculate number of key segements */
*key_count= 0;
@ -3967,10 +4265,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
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();
@ -3989,11 +4283,11 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
key_number=0;
for (; (key=key_iterator++) ; key_number++)
{
uint key_length=0;
Create_field *auto_increment_key= 0;
Key_part_spec *column;
is_hash_field_needed= false;
bool is_hash_field_needed= key->key_create_info.algorithm
== HA_KEY_ALG_LONG_HASH;
if (key->name.str == ignore_key)
{
/* ignore redundant keys */
@ -4004,6 +4298,9 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
break;
}
if (key_check_without_overlaps(thd, create_info, alter_info, *key))
DBUG_RETURN(true);
switch (key->type) {
case Key::MULTIPLE:
key_info->flags= 0;
@ -4034,10 +4331,12 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
if (key->generated)
key_info->flags|= HA_GENERATED_KEY;
key_info->key_length= key->length;
key_info->user_defined_key_parts=(uint8) key->columns.elements;
key_info->key_part=key_part_info;
key_info->usable_key_parts= key_number;
key_info->algorithm= key->key_create_info.algorithm;
key_info->without_overlaps= key->without_overlaps;
key_info->option_list= key->option_list;
extend_option_list(thd, create_info->db_type, !key->old,
&key_info->option_list, create_info->db_type->index_options);
@ -4121,37 +4420,11 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
CHARSET_INFO *ft_key_charset=0; // for FULLTEXT
for (uint column_nr=0 ; (column=cols++) ; column_nr++)
{
Key_part_spec *dup_column;
it.rewind();
field=0;
while ((sql_field=it++) &&
lex_string_cmp(scs, &column->field_name, &sql_field->field_name))
field++;
/*
Either field is not present or field visibility is > INVISIBLE_USER
*/
if (!sql_field || (sql_field->invisible > INVISIBLE_USER &&
!column->generated))
{
my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name.str);
DBUG_RETURN(TRUE);
}
if (sql_field->invisible > INVISIBLE_USER &&
!(sql_field->flags & VERS_SYSTEM_FIELD) &&
!key->invisible && DBUG_EVALUATE_IF("test_invisible_index", 0, 1))
{
my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name.str);
DBUG_RETURN(TRUE);
}
while ((dup_column= cols2++) != column)
{
if (!lex_string_cmp(scs, &column->field_name, &dup_column->field_name))
{
my_error(ER_DUP_FIELDNAME, MYF(0), column->field_name.str);
DBUG_RETURN(TRUE);
}
}
if (sql_field->compression_method())
{
@ -4161,12 +4434,10 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
}
cols2.rewind();
switch(key->type) {
switch(key->type)
{
case Key::FULLTEXT:
if (sql_field->type_handler()->Key_part_spec_init_ft(column,
*sql_field) ||
(ft_key_charset && sql_field->charset != ft_key_charset))
if (ft_key_charset && sql_field->charset != ft_key_charset)
{
my_error(ER_BAD_FT_COLUMN, MYF(0), column->field_name.str);
DBUG_RETURN(-1);
@ -4174,64 +4445,46 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
ft_key_charset= sql_field->charset;
break;
case Key::SPATIAL:
if (sql_field->type_handler()->Key_part_spec_init_spatial(column,
*sql_field) ||
sql_field->check_vcol_for_key(thd))
DBUG_RETURN(TRUE);
if (!(sql_field->flags & NOT_NULL_FLAG))
{
my_message(ER_SPATIAL_CANT_HAVE_NULL,
ER_THD(thd, ER_SPATIAL_CANT_HAVE_NULL), MYF(0));
DBUG_RETURN(TRUE);
}
break;
case Key::PRIMARY:
if (sql_field->vcol_info)
{
my_error(ER_PRIMARY_KEY_BASED_ON_GENERATED_COLUMN, MYF(0));
DBUG_RETURN(TRUE);
}
if (sql_field->type_handler()->Key_part_spec_init_primary(column,
*sql_field,
file))
DBUG_RETURN(TRUE);
if (!(sql_field->flags & NOT_NULL_FLAG))
{
/* Implicitly set primary key fields to NOT NULL for ISO conf. */
/* Implicitly set primary key fields to NOT NULL for ISO conformance. */
sql_field->flags|= NOT_NULL_FLAG;
sql_field->pack_flag&= ~FIELDFLAG_MAYBE_NULL;
null_fields--;
}
break;
case Key::MULTIPLE:
if (sql_field->type_handler()->Key_part_spec_init_multiple(column,
*sql_field,
file) ||
sql_field->check_vcol_for_key(thd) ||
key_add_part_check_null(file, key_info, sql_field, column))
DBUG_RETURN(TRUE);
break;
case Key::FOREIGN_KEY:
if (sql_field->type_handler()->Key_part_spec_init_foreign(column,
*sql_field,
file) ||
sql_field->check_vcol_for_key(thd) ||
key_add_part_check_null(file, key_info, sql_field, column))
DBUG_RETURN(TRUE);
break;
case Key::UNIQUE:
if (sql_field->type_handler()->Key_part_spec_init_unique(column,
*sql_field, file,
&is_hash_field_needed) ||
sql_field->check_vcol_for_key(thd) ||
key_add_part_check_null(file, key_info, sql_field, column))
case Key::MULTIPLE:
case Key::FOREIGN_KEY:
if (key_add_part_check_null(file, key_info, sql_field, column))
DBUG_RETURN(TRUE);
if (sql_field->check_vcol_for_key(thd))
DBUG_RETURN(TRUE);
break;
case Key::SPATIAL:
if (!(sql_field->flags & NOT_NULL_FLAG))
{
my_message(ER_SPATIAL_CANT_HAVE_NULL,
ER_THD(thd, ER_SPATIAL_CANT_HAVE_NULL), MYF(0));
DBUG_RETURN(TRUE);
}
if (sql_field->check_vcol_for_key(thd))
DBUG_RETURN(TRUE);
break;
}
for (const Key_part_spec &kp2: key->columns)
{
if (column == &kp2)
break;
if (kp2.field_name.streq(column->field_name))
{
my_error(ER_DUP_FIELDNAME, MYF(0), column->field_name.str);
DBUG_RETURN(TRUE);
}
}
if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
@ -4246,111 +4499,21 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
key_part_info->fieldnr= field;
key_part_info->offset= (uint16) sql_field->offset;
key_part_info->key_type=sql_field->pack_flag;
uint key_part_length= sql_field->type_handler()->
calc_key_length(*sql_field);
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())
{
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;
}
}
}
// Catch invalid use of partial keys
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?
(column->length > key_part_length ||
// can the field have a partial key?
!sql_field->type_handler()->type_can_have_key_part() ||
// a packed field can't be used in a partial key
f_is_packed(sql_field->pack_flag) ||
// does the storage engine allow prefixed search?
((file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS) &&
// 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;
}
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);
DBUG_RETURN(TRUE);
}
if (key_part_length > file->max_key_part_length() &&
key->type != Key::FULLTEXT)
{
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
{
if (key->type != Key::UNIQUE)
{
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);
}
}
}
if (key->type == Key::UNIQUE
&& key_part_length > MY_MIN(max_key_length,
file->max_key_part_length()))
is_hash_field_needed= 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);
}
else
key_part_info->length= (uint16) key_part_length;
key_part_info->length= column->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) &&
(column->length >= KEY_DEFAULT_PACK_LENGTH) &&
!is_hash_field_needed)
{
key_info->flags|= sql_field->type_handler()->KEY_pack_flags(column_nr);
}
/* Check if the key segment is partial, set the key flag accordingly */
if (key_part_length != sql_field->type_handler()->
if (column->length != sql_field->type_handler()->
calc_key_length(*sql_field) &&
key_part_length != sql_field->type_handler()->max_octet_length())
column->length != sql_field->type_handler()->max_octet_length())
key_info->flags|= HA_KEY_HAS_PART_KEY_SEG;
key_length+= key_part_length;
key_part_info++;
}
if (!key_info->name.str || check_column_name(key_info->name.str))
@ -4360,15 +4523,6 @@ 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_info->key_length > max_key_length && key->type == Key::UNIQUE)
is_hash_field_needed= true; // for case "a BLOB UNIQUE"
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);
DBUG_RETURN(TRUE);
}
/* Check long unique keys */
if (is_hash_field_needed)
@ -4380,12 +4534,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
key_info->name.str);
DBUG_RETURN(TRUE);
}
if (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 &&
@ -4425,40 +4573,6 @@ 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->without_overlaps= key->without_overlaps;
if (key_info->without_overlaps)
{
if (key_info->algorithm == HA_KEY_ALG_HASH ||
key_info->algorithm == HA_KEY_ALG_LONG_HASH)
{
without_overlaps_err:
my_error(ER_KEY_CANT_HAVE_WITHOUT_OVERLAPS, MYF(0), key_info->name.str);
DBUG_RETURN(true);
}
key_iterator2.rewind();
while ((key2 = key_iterator2++))
{
if (key2->type != Key::FOREIGN_KEY)
continue;
DBUG_ASSERT(key != key2);
Foreign_key *fk= (Foreign_key*) key2;
if (fk->update_opt != FK_OPTION_CASCADE)
continue;
for (Key_part_spec& kp: key->columns)
{
for (Key_part_spec& kp2: fk->columns)
{
if (!lex_string_cmp(scs, &kp.field_name, &kp2.field_name))
{
goto without_overlaps_err;
}
}
}
}
create_info->period_info.unique_keys++;
}
key_info++;
}
@ -4886,20 +5000,6 @@ static int append_system_key_parts(THD *thd, HA_CREATE_INFO *create_info,
my_error(ER_PERIOD_NOT_FOUND, MYF(0), key->period.str);
return -1;
}
const auto &period_start= create_info->period_info.period.start;
const auto &period_end= create_info->period_info.period.end;
List_iterator<Key_part_spec> part_it(key->columns);
while (Key_part_spec *key_part= part_it++)
{
if (period_start.streq(key_part->field_name)
|| period_end.streq(key_part->field_name))
{
my_error(ER_KEY_CONTAINS_PERIOD_FIELDS, MYF(0), key->name.str,
key_part->field_name.str);
return -1;
}
}
const auto &period= create_info->period_info.period;
key->columns.push_back(new (thd->mem_root)
Key_part_spec(&period.end, 0, true));