diff --git a/innobase/data/data0data.c b/innobase/data/data0data.c index 194213a04e1..19304a7a8e1 100644 --- a/innobase/data/data0data.c +++ b/innobase/data/data0data.c @@ -561,12 +561,12 @@ dtuple_convert_big_rec( } /* We do not store externally fields which are smaller than - DICT_MAX_COL_PREFIX_LEN */ + DICT_MAX_INDEX_COL_LEN */ - ut_a(DICT_MAX_COL_PREFIX_LEN > REC_1BYTE_OFFS_LIMIT); + ut_a(DICT_MAX_INDEX_COL_LEN > REC_1BYTE_OFFS_LIMIT); if (longest < BTR_EXTERN_FIELD_REF_SIZE + 10 - + DICT_MAX_COL_PREFIX_LEN) { + + DICT_MAX_INDEX_COL_LEN) { /* Cannot shorten more */ mem_heap_free(heap); @@ -588,10 +588,10 @@ dtuple_convert_big_rec( dfield = dtuple_get_nth_field(entry, longest_i); vector->fields[n_fields].field_no = longest_i; - ut_a(dfield->len > DICT_MAX_COL_PREFIX_LEN); + ut_a(dfield->len > DICT_MAX_INDEX_COL_LEN); vector->fields[n_fields].len = dfield->len - - DICT_MAX_COL_PREFIX_LEN; + - DICT_MAX_INDEX_COL_LEN; vector->fields[n_fields].data = mem_heap_alloc(heap, vector->fields[n_fields].len); diff --git a/innobase/dict/dict0dict.c b/innobase/dict/dict0dict.c index 5eee57c250b..9f2ae36c1c6 100644 --- a/innobase/dict/dict0dict.c +++ b/innobase/dict/dict0dict.c @@ -1625,7 +1625,7 @@ dict_index_add_col( variable-length fields, so that the extern flag can be embedded in the length word. */ - if (field->fixed_len > DICT_MAX_COL_PREFIX_LEN) { + if (field->fixed_len > DICT_MAX_INDEX_COL_LEN) { field->fixed_len = 0; } diff --git a/innobase/include/dict0mem.h b/innobase/include/dict0mem.h index ff6c4ec9b28..7eec86d0bcb 100644 --- a/innobase/include/dict0mem.h +++ b/innobase/include/dict0mem.h @@ -152,12 +152,12 @@ struct dict_col_struct{ in some of the functions below */ }; -/* DICT_MAX_COL_PREFIX_LEN is measured in bytes. Starting from 4.1.6, we -set max col prefix len to < 3 * 256, so that one can create a column prefix -index on 255 characters of a TEXT field also in the UTF-8 charset. In that -charset, a character may take at most 3 bytes. */ +/* DICT_MAX_INDEX_COL_LEN is measured in bytes and is the max index column +length + 1. Starting from 4.1.6, we set it to < 3 * 256, so that one can +create a column prefix index on 255 characters of a TEXT field also in the +UTF-8 charset. In that charset, a character may take at most 3 bytes. */ -#define DICT_MAX_COL_PREFIX_LEN 768 +#define DICT_MAX_INDEX_COL_LEN 768 /* Data structure for a field in an index */ struct dict_field_struct{ @@ -169,12 +169,12 @@ struct dict_field_struct{ prefix in bytes in a MySQL index of type, e.g., INDEX (textcol(25)); must be smaller than - DICT_MAX_COL_PREFIX_LEN; NOTE that + DICT_MAX_INDEX_COL_LEN; NOTE that in the UTF-8 charset, MySQL sets this to 3 * the prefix len in UTF-8 chars */ ulint fixed_len; /* 0 or the fixed length of the column if smaller than - DICT_MAX_COL_PREFIX_LEN */ + DICT_MAX_INDEX_COL_LEN */ ulint fixed_offs; /* offset to the field, or ULINT_UNDEFINED if it is not fixed within the record (due to preceding diff --git a/innobase/include/row0mysql.h b/innobase/include/row0mysql.h index a61705b90be..b5da4634d98 100644 --- a/innobase/include/row0mysql.h +++ b/innobase/include/row0mysql.h @@ -335,8 +335,14 @@ int row_create_index_for_mysql( /*=======================*/ /* out: error number or DB_SUCCESS */ - dict_index_t* index, /* in: index defintion */ - trx_t* trx); /* in: transaction handle */ + dict_index_t* index, /* in: index definition */ + trx_t* trx, /* in: transaction handle */ + const ulint* field_lengths); /* in: if not NULL, must contain + dict_index_get_n_fields(index) + actual field lengths for the + index columns, which are + then checked for not being too + large. */ /************************************************************************* Scans a table create SQL string and adds to the data dictionary the foreign key constraints declared in the string. This function diff --git a/innobase/rem/rem0rec.c b/innobase/rem/rem0rec.c index fbc33aea669..9480c978755 100644 --- a/innobase/rem/rem0rec.c +++ b/innobase/rem/rem0rec.c @@ -621,7 +621,7 @@ rec_set_nth_field_extern_bit_new( if (field->fixed_len) { /* fixed-length fields cannot be external (Fixed-length fields longer than - DICT_MAX_COL_PREFIX_LEN will be treated as + DICT_MAX_INDEX_COL_LEN will be treated as variable-length ones in dict_index_add_col().) */ ut_ad(i != ith); continue; diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c index 26aae117d1d..82f7daf2ed8 100644 --- a/innobase/row/row0mysql.c +++ b/innobase/row/row0mysql.c @@ -1973,13 +1973,20 @@ row_create_index_for_mysql( /*=======================*/ /* out: error number or DB_SUCCESS */ dict_index_t* index, /* in: index definition */ - trx_t* trx) /* in: transaction handle */ + trx_t* trx, /* in: transaction handle */ + const ulint* field_lengths) /* in: if not NULL, must contain + dict_index_get_n_fields(index) + actual field lengths for the + index columns, which are + then checked for not being too + large. */ { ind_node_t* node; mem_heap_t* heap; que_thr_t* thr; ulint err; ulint i, j; + ulint len; #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX)); @@ -2018,10 +2025,16 @@ row_create_index_for_mysql( } } - /* Check also that prefix_len < DICT_MAX_COL_PREFIX_LEN */ + /* Check also that prefix_len and actual length + < DICT_MAX_INDEX_COL_LEN */ - if (dict_index_get_nth_field(index, i)->prefix_len - >= DICT_MAX_COL_PREFIX_LEN) { + len = dict_index_get_nth_field(index, i)->prefix_len; + + if (field_lengths) { + len = ut_max(len, field_lengths[i]); + } + + if (len >= DICT_MAX_INDEX_COL_LEN) { err = DB_TOO_BIG_RECORD; goto error_handling; diff --git a/mysql-test/r/innodb.result b/mysql-test/r/innodb.result index ffc4ab08ab4..8d44a2a12ab 100644 --- a/mysql-test/r/innodb.result +++ b/mysql-test/r/innodb.result @@ -2559,3 +2559,37 @@ FOREIGN KEY (b) REFERENCES test.t1(id) ) ENGINE=InnoDB; Got one of the listed errors DROP TABLE t1; +create table t1 (col1 varchar(2000), index (col1(767))) +character set = latin1 engine = innodb; +create table t2 (col1 char(255), index (col1)) +character set = latin1 engine = innodb; +create table t3 (col1 binary(255), index (col1)) +character set = latin1 engine = innodb; +create table t4 (col1 varchar(767), index (col1)) +character set = latin1 engine = innodb; +create table t5 (col1 varchar(767) primary key) +character set = latin1 engine = innodb; +create table t6 (col1 varbinary(767) primary key) +character set = latin1 engine = innodb; +create table t7 (col1 text, index(col1(767))) +character set = latin1 engine = innodb; +create table t8 (col1 blob, index(col1(767))) +character set = latin1 engine = innodb; +create table t9 (col1 varchar(512), col2 varchar(512), index(col1, col2)) +character set = latin1 engine = innodb; +drop table t1, t2, t3, t4, t5, t6, t7, t8, t9; +create table t1 (col1 varchar(768), index (col1)) +character set = latin1 engine = innodb; +ERROR HY000: Can't create table './test/t1.frm' (errno: 139) +create table t2 (col1 varchar(768) primary key) +character set = latin1 engine = innodb; +ERROR HY000: Can't create table './test/t2.frm' (errno: 139) +create table t3 (col1 varbinary(768) primary key) +character set = latin1 engine = innodb; +ERROR HY000: Can't create table './test/t3.frm' (errno: 139) +create table t4 (col1 text, index(col1(768))) +character set = latin1 engine = innodb; +ERROR HY000: Can't create table './test/t4.frm' (errno: 139) +create table t5 (col1 blob, index(col1(768))) +character set = latin1 engine = innodb; +ERROR HY000: Can't create table './test/t5.frm' (errno: 139) diff --git a/mysql-test/t/innodb.test b/mysql-test/t/innodb.test index 94f33738c8c..d8c9830a214 100644 --- a/mysql-test/t/innodb.test +++ b/mysql-test/t/innodb.test @@ -1482,3 +1482,49 @@ CREATE TEMPORARY TABLE t2 FOREIGN KEY (b) REFERENCES test.t1(id) ) ENGINE=InnoDB; DROP TABLE t1; + +# +# Test that index column max sizes are checked (bug #13315) +# + +# prefix index +create table t1 (col1 varchar(2000), index (col1(767))) + character set = latin1 engine = innodb; + +# normal indexes +create table t2 (col1 char(255), index (col1)) + character set = latin1 engine = innodb; +create table t3 (col1 binary(255), index (col1)) + character set = latin1 engine = innodb; +create table t4 (col1 varchar(767), index (col1)) + character set = latin1 engine = innodb; +create table t5 (col1 varchar(767) primary key) + character set = latin1 engine = innodb; +create table t6 (col1 varbinary(767) primary key) + character set = latin1 engine = innodb; +create table t7 (col1 text, index(col1(767))) + character set = latin1 engine = innodb; +create table t8 (col1 blob, index(col1(767))) + character set = latin1 engine = innodb; + +# multi-column indexes are allowed to be longer +create table t9 (col1 varchar(512), col2 varchar(512), index(col1, col2)) + character set = latin1 engine = innodb; + +drop table t1, t2, t3, t4, t5, t6, t7, t8, t9; + +--error 1005 +create table t1 (col1 varchar(768), index (col1)) + character set = latin1 engine = innodb; +--error 1005 +create table t2 (col1 varchar(768) primary key) + character set = latin1 engine = innodb; +--error 1005 +create table t3 (col1 varbinary(768) primary key) + character set = latin1 engine = innodb; +--error 1005 +create table t4 (col1 text, index(col1(768))) + character set = latin1 engine = innodb; +--error 1005 +create table t5 (col1 blob, index(col1(768))) + character set = latin1 engine = innodb; diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 9e10873ad63..007194e09b3 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -4492,7 +4492,8 @@ create_index( ulint is_unsigned; ulint i; ulint j; - + ulint* field_lengths; + DBUG_ENTER("create_index"); key = form->key_info + key_num; @@ -4514,6 +4515,10 @@ create_index( index = dict_mem_index_create((char*) table_name, key->name, 0, ind_type, n_fields); + + field_lengths = (ulint*) my_malloc(sizeof(ulint) * n_fields, + MYF(MY_FAE)); + for (i = 0; i < n_fields; i++) { key_part = key->key_part + i; @@ -4568,6 +4573,8 @@ create_index( prefix_len = 0; } + field_lengths[i] = key_part->length; + /* We assume all fields should be sorted in ascending order, hence the '0': */ @@ -4576,10 +4583,12 @@ create_index( 0, prefix_len); } - error = row_create_index_for_mysql(index, trx); + error = row_create_index_for_mysql(index, trx, field_lengths); error = convert_error_code_to_mysql(error, NULL); + my_free((gptr) field_lengths, MYF(0)); + DBUG_RETURN(error); } @@ -4602,7 +4611,7 @@ create_clustered_index_when_no_primary( index = dict_mem_index_create((char*) table_name, (char*) "GEN_CLUST_INDEX", 0, DICT_CLUSTERED, 0); - error = row_create_index_for_mysql(index, trx); + error = row_create_index_for_mysql(index, trx, NULL); error = convert_error_code_to_mysql(error, NULL);