From a7ee3bc58bc23ad7ac5ced54dc745d0e65bee524 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Mon, 22 Jan 2024 18:02:11 +0100 Subject: [PATCH] MDEV-29954 Unique hash key on column prefix is computed incorrectly use the original, not the truncated, field in the long unique prefix, that is, in the hash(left(field, length)) expression. because MyISAM CHECK/REPAIR in compute_vcols() moves table->field but not prefix fields from keyparts. Also, implement Field_string::cmp_prefix() for prefix comparison of CHAR columns to work. --- mysql-test/main/long_unique_bugs.result | 9 +++++++++ mysql-test/main/long_unique_bugs.test | 8 ++++++++ sql/field.cc | 13 +++++++++++++ sql/field.h | 2 ++ sql/key.cc | 2 +- sql/table.cc | 5 ++--- 6 files changed, 35 insertions(+), 4 deletions(-) diff --git a/mysql-test/main/long_unique_bugs.result b/mysql-test/main/long_unique_bugs.result index e5c14afd9c400..30ba3d0af9a73 100644 --- a/mysql-test/main/long_unique_bugs.result +++ b/mysql-test/main/long_unique_bugs.result @@ -651,5 +651,14 @@ f1 f2 f3 f4 f5 f6 f7 4 00004 0001009089999 netstes psit e drop table t1; # +# MDEV-29954 Unique hash key on column prefix is computed incorrectly +# +create table t1 (c char(10),unique key a using hash (c(1))); +insert into t1 values (0); +check table t1 extended; +Table Op Msg_type Msg_text +test.t1 check status OK +drop table t1; +# # End of 10.5 tests # diff --git a/mysql-test/main/long_unique_bugs.test b/mysql-test/main/long_unique_bugs.test index 5fed7c72d9a26..12367b1deb8ce 100644 --- a/mysql-test/main/long_unique_bugs.test +++ b/mysql-test/main/long_unique_bugs.test @@ -634,6 +634,14 @@ replace t1 (f2, f3, f4, f5, f6, f7) values ('00004', '0001009089999', '', 'netst select * from t1; drop table t1; +--echo # +--echo # MDEV-29954 Unique hash key on column prefix is computed incorrectly +--echo # +create table t1 (c char(10),unique key a using hash (c(1))); +insert into t1 values (0); +check table t1 extended; +drop table t1; + --echo # --echo # End of 10.5 tests --echo # diff --git a/sql/field.cc b/sql/field.cc index 9e1e0931ef134..fc828dcc86c88 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -7566,6 +7566,19 @@ int Field_string::cmp(const uchar *a_ptr, const uchar *b_ptr) const } +int Field_string::cmp_prefix(const uchar *a_ptr, const uchar *b_ptr, + size_t prefix_char_len) const +{ + size_t field_len= table->field[field_index]->field_length; + + return field_charset()->coll->strnncollsp_nchars(field_charset(), + a_ptr, field_len, + b_ptr, field_len, + prefix_char_len, + 0); +} + + void Field_string::sort_string(uchar *to,uint length) { #ifdef DBUG_ASSERT_EXISTS diff --git a/sql/field.h b/sql/field.h index 4a95a6e05568c..aeb2ae0d20a80 100644 --- a/sql/field.h +++ b/sql/field.h @@ -4038,6 +4038,8 @@ class Field_string final :public Field_longstr { String *val_str(String *, String *) override; my_decimal *val_decimal(my_decimal *) override; int cmp(const uchar *,const uchar *) const override; + int cmp_prefix(const uchar *a, const uchar *b, size_t prefix_char_len) const + override; void sort_string(uchar *buff,uint length) override; void update_data_type_statistics(Data_type_statistics *st) const override { diff --git a/sql/key.cc b/sql/key.cc index d801bcc982b8f..2fa7d7066a4b5 100644 --- a/sql/key.cc +++ b/sql/key.cc @@ -605,7 +605,7 @@ int key_rec_cmp(void *key_p, uchar *first_rec, uchar *second_rec) } /* No null values in the fields - We use the virtual method cmp_max with a max length parameter. + We use the virtual method cmp_prefix with a max length parameter. For most field types this translates into a cmp without max length. The exceptions are the BLOB and VARCHAR field types that take the max length into account. diff --git a/sql/table.cc b/sql/table.cc index a5e33052d3f5b..f3d186f4d9db8 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1272,12 +1272,11 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table, if (keypart->key_part_flag & HA_PART_KEY_SEG) { int length= keypart->length/keypart->field->charset()->mbmaxlen; + Field *kpf= table->field[keypart->field->field_index]; list_item= new (mem_root) Item_func_left(thd, - new (mem_root) Item_field(thd, keypart->field), + new (mem_root) Item_field(thd, kpf), new (mem_root) Item_int(thd, length)); list_item->fix_fields(thd, NULL); - keypart->field->vcol_info= - table->field[keypart->field->field_index]->vcol_info; } else list_item= new (mem_root) Item_field(thd, keypart->field);