From cb9c49a9b20dd8d1eee39641176134e207a4d84f Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Thu, 21 May 2020 18:16:27 +0400 Subject: [PATCH 1/8] MDEV-22111 ERROR 1064 & 1033 and SIGSEGV on CREATE TABLE w/ various charsets on 10.4/5 optimized builds | Assertion `(uint) (table_check_constraints - share->check_constraints) == (uint) (share->table_check_constraints - share->field_check_constraints)' failed The code incorrectly assumed in multiple places that TYPELIB values cannot have 0x00 bytes inside. In fact they can: CREATE TABLE t1 (a ENUM(0x61, 0x0062) CHARACTER SET BINARY); Note, the TYPELIB value encoding used in FRM is ambiguous about 0x00. So this fix is partial. It fixes 0x00 bytes in many (but not all) places: - In the middle or in the end of a value: CREATE TABLE t1 (a ENUM(0x6100) ...); CREATE TABLE t1 (a ENUM(0x610062) ...); - In the beginning of the first value: CREATE TABLE t1 (a ENUM(0x0061)); CREATE TABLE t1 (a ENUM(0x0061), b ENUM('b')); - In the beginning of the second (and following) value of the *last* ENUM/SET in the table: CREATE TABLE t1 (a ENUM('a',0x0061)); CREATE TABLE t1 (a ENUM('a'), b ENUM('b',0x0061)); However, it does not fix 0x00 when: - 0x00 byte is in the beginning of a value of a non-last ENUM/SET causes an error: CREATE TABLE t1 (a ENUM('a',0x0061), b ENUM('b')); ERROR 1033 (HY000): Incorrect information in file: './test/t1.frm' This is an ambuguous case and will be fixed separately. We need a new TYPELIB encoding to fix this. Details: - unireg.cc The function pack_header() incorrectly used strlen() to detect a TYPELIB value length. Adding a new function typelib_values_packed_length() which uses TYPELIB::type_lengths[n] to detect the n-th value length, and reusing the new function in pack_header() and packed_fields_length() - table.cc fix_type_pointers() assumed in multiple places that values cannot have 0x00 inside and used strlen(TYPELIB::type_names[n]) to set the corresponding TYPELIB::type_lengths[n]. Also, fix_type_pointers() did not check the encoded data for consistency. Rewriting fix_type_pointers() code to populate TYPELIB::type_names[n] and TYPELIB::type_lengths[n] at the same time, so no additional loop with strlen() is needed any more. Adding many data consistency tests. Fixing the main loop in fix_type_pointers() to use memchr() instead of strchr() to handle 0x00 properly. Fixing create_key_infos() to return the result in a LEX_STRING rather that in a char*. --- mysql-test/r/ctype_binary.result | 93 +++++++++++++++++ mysql-test/t/ctype_binary.test | 60 +++++++++++ sql/table.cc | 174 +++++++++++++++++++++---------- sql/unireg.cc | 23 ++-- 4 files changed, 288 insertions(+), 62 deletions(-) diff --git a/mysql-test/r/ctype_binary.result b/mysql-test/r/ctype_binary.result index 15b44d07ddf..aad9621966f 100644 --- a/mysql-test/r/ctype_binary.result +++ b/mysql-test/r/ctype_binary.result @@ -3171,5 +3171,98 @@ Warnings: Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where ((`test`.`t1`.`a` = 'a') and (weight_string(`test`.`t1`.`a`) = 'a')) DROP TABLE t1; # +# MDEV-22111 ERROR 1064 & 1033 and SIGSEGV on CREATE TABLE w/ various charsets on 10.4/5 optimized builds | Assertion `(uint) (table_check_constraints - share->check_constraints) == (uint) (share->table_check_constraints - share->field_check_constraints)' failed +# +CREATE TABLE t1(a ENUM(0x6100,0x6200,0x6300) CHARACTER SET 'Binary'); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` enum('a\0','b\0','c\0') CHARACTER SET binary DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +INSERT INTO t1 VALUES (1),(2),(3); +SELECT HEX(a) FROM t1 ORDER BY a; +HEX(a) +6100 +6200 +6300 +DROP TABLE t1; +0x00 in the middle or in the end of a value +CREATE TABLE t1 (a ENUM(0x6100)); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` enum('a\0') DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +INSERT INTO t1 VALUES (1); +SELECT HEX(a) FROM t1; +HEX(a) +6100 +DROP TABLE t1; +CREATE TABLE t1 (a ENUM(0x610062)); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` enum('a\0b') DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +INSERT INTO t1 VALUES (1); +SELECT HEX(a) FROM t1; +HEX(a) +610062 +DROP TABLE t1; +0x00 in the beginning of the first value: +CREATE TABLE t1 (a ENUM(0x0061)); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` enum('\0a') DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +INSERT INTO t1 VALUES(1); +SELECT * FROM t1; +a +a +DROP TABLE t1; +CREATE TABLE t1 (a ENUM(0x0061), b ENUM('b')); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` enum('\0a') DEFAULT NULL, + `b` enum('b') DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +INSERT INTO t1 VALUES (1,1); +SELECT HEX(a), HEX(b) FROM t1; +HEX(a) HEX(b) +0061 62 +DROP TABLE t1; +# 0x00 in the beginning of the second (and following) value of the *last* ENUM/SET in the table: +CREATE TABLE t1 (a ENUM('a',0x0061)); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` enum('a','\0a') DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +INSERT INTO t1 VALUES (1),(2); +SELECT HEX(a) FROM t1 ORDER BY a; +HEX(a) +61 +0061 +DROP TABLE t1; +CREATE TABLE t1 (a ENUM('a'), b ENUM('b',0x0061)); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` enum('a') DEFAULT NULL, + `b` enum('b','\0a') DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +INSERT INTO t1 VALUES (1,1); +INSERT INTO t1 VALUES (1,2); +SELECT HEX(a), HEX(b) FROM t1 ORDER BY a, b; +HEX(a) HEX(b) +61 62 +61 0061 +DROP TABLE t1; +0x00 in the beginning of a value of a non-last ENUM/SET causes an error: +CREATE TABLE t1 (a ENUM('a',0x0061), b ENUM('b')); +ERROR HY000: Incorrect information in file: 'DIR/t1.frm' +# # End of 10.1 tests # diff --git a/mysql-test/t/ctype_binary.test b/mysql-test/t/ctype_binary.test index 155d8548f77..19a58180187 100644 --- a/mysql-test/t/ctype_binary.test +++ b/mysql-test/t/ctype_binary.test @@ -74,6 +74,66 @@ EXPLAIN EXTENDED SELECT * FROM t1 WHERE COERCIBILITY(a)=2 AND a='a'; EXPLAIN EXTENDED SELECT * FROM t1 WHERE WEIGHT_STRING(a)='a' AND a='a'; DROP TABLE t1; + +--echo # +--echo # MDEV-22111 ERROR 1064 & 1033 and SIGSEGV on CREATE TABLE w/ various charsets on 10.4/5 optimized builds | Assertion `(uint) (table_check_constraints - share->check_constraints) == (uint) (share->table_check_constraints - share->field_check_constraints)' failed +--echo # + +CREATE TABLE t1(a ENUM(0x6100,0x6200,0x6300) CHARACTER SET 'Binary'); +SHOW CREATE TABLE t1; +INSERT INTO t1 VALUES (1),(2),(3); +SELECT HEX(a) FROM t1 ORDER BY a; +DROP TABLE t1; + +--echo 0x00 in the middle or in the end of a value + +CREATE TABLE t1 (a ENUM(0x6100)); +SHOW CREATE TABLE t1; +INSERT INTO t1 VALUES (1); +SELECT HEX(a) FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a ENUM(0x610062)); +SHOW CREATE TABLE t1; +INSERT INTO t1 VALUES (1); +SELECT HEX(a) FROM t1; +DROP TABLE t1; + +--echo 0x00 in the beginning of the first value: + +CREATE TABLE t1 (a ENUM(0x0061)); +SHOW CREATE TABLE t1; +INSERT INTO t1 VALUES(1); +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a ENUM(0x0061), b ENUM('b')); +SHOW CREATE TABLE t1; +INSERT INTO t1 VALUES (1,1); +SELECT HEX(a), HEX(b) FROM t1; +DROP TABLE t1; + +--echo # 0x00 in the beginning of the second (and following) value of the *last* ENUM/SET in the table: + +CREATE TABLE t1 (a ENUM('a',0x0061)); +SHOW CREATE TABLE t1; +INSERT INTO t1 VALUES (1),(2); +SELECT HEX(a) FROM t1 ORDER BY a; +DROP TABLE t1; + +CREATE TABLE t1 (a ENUM('a'), b ENUM('b',0x0061)); +SHOW CREATE TABLE t1; +INSERT INTO t1 VALUES (1,1); +INSERT INTO t1 VALUES (1,2); +SELECT HEX(a), HEX(b) FROM t1 ORDER BY a, b; +DROP TABLE t1; + +--echo 0x00 in the beginning of a value of a non-last ENUM/SET causes an error: +--replace_regex /'.*t1.frm'/'DIR\/t1.frm'/ +--error ER_NOT_FORM_FILE +CREATE TABLE t1 (a ENUM('a',0x0061), b ENUM('b')); + + --echo # --echo # End of 10.1 tests --echo # diff --git a/sql/table.cc b/sql/table.cc index f1a4b322a80..192faa0d00e 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -64,8 +64,11 @@ LEX_STRING parse_vcol_keyword= { C_STRING_WITH_LEN("PARSE_VCOL_EXPR ") }; /* Functions defined in this file */ -static void fix_type_pointers(const char ***array, TYPELIB *point_to_type, - uint types, char **names); +static bool fix_type_pointers(const char ***typelib_value_names, + uint **typelib_value_lengths, + TYPELIB *point_to_type, uint types, + char *names, size_t names_length); + static uint find_field(Field **fields, uchar *record, uint start, uint length); inline bool is_system_table_name(const char *name, uint length); @@ -670,7 +673,8 @@ static bool create_key_infos(const uchar *strpos, const uchar *frm_image_end, uint keys, KEY *keyinfo, uint new_frm_ver, uint &ext_key_parts, TABLE_SHARE *share, uint len, - KEY *first_keyinfo, char* &keynames) + KEY *first_keyinfo, + LEX_STRING *keynames) { uint i, j, n_length; KEY_PART_INFO *key_part= NULL; @@ -813,10 +817,13 @@ static bool create_key_infos(const uchar *strpos, const uchar *frm_image_end, } share->ext_key_parts+= keyinfo->ext_key_parts; } - keynames=(char*) key_part; - strpos+= strnmov(keynames, (char *) strpos, frm_image_end - strpos) - keynames; + keynames->str= (char*) key_part; + keynames->length= strnmov(keynames->str, (char *) strpos, + frm_image_end - strpos) - keynames->str; + strpos+= keynames->length; if (*strpos++) // key names are \0-terminated return 1; + keynames->length++; // Include '\0', to make fix_type_pointers() happy. //reading index comments for (keyinfo= share->key_info, i=0; i < keys; i++, keyinfo++) @@ -918,12 +925,14 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, TABLE_SHARE *share= this; uint new_frm_ver, field_pack_length, new_field_pack_flag; uint interval_count, interval_parts, read_length, int_length; + uint total_typelib_value_count; uint db_create_options, keys, key_parts, n_length; uint com_length, null_bit_pos; uint extra_rec_buf_length; uint i; bool use_hash; - char *keynames, *names, *comment_pos; + LEX_STRING keynames= {NULL, 0}; + char *names, *comment_pos; const uchar *forminfo, *extra2; const uchar *frm_image_end = frm_image + frm_length; uchar *record, *null_flags, *null_pos; @@ -935,6 +944,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, KEY_PART_INFO *key_part= NULL; Field **field_ptr, *reg_field; const char **interval_array; + uint *typelib_value_lengths= NULL; enum legacy_db_type legacy_db_type; my_bitmap_map *bitmaps; bool null_bits_are_used; @@ -1237,7 +1247,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, if (create_key_infos(disk_buff + 6, frm_image_end, keys, keyinfo, new_frm_ver, ext_key_parts, - share, len, &first_keyinfo, keynames)) + share, len, &first_keyinfo, &keynames)) goto err; if (next_chunk + 5 < buff_end) @@ -1330,7 +1340,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, { if (create_key_infos(disk_buff + 6, frm_image_end, keys, keyinfo, new_frm_ver, ext_key_parts, - share, len, &first_keyinfo, keynames)) + share, len, &first_keyinfo, &keynames)) goto err; } @@ -1372,6 +1382,24 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, DBUG_PRINT("info",("i_count: %d i_parts: %d index: %d n_length: %d int_length: %d com_length: %d vcol_screen_length: %d", interval_count,interval_parts, keys,n_length,int_length, com_length, vcol_screen_length)); + /* + We load the following things into TYPELIBs: + - One TYPELIB for field names + - interval_count TYPELIBs for ENUM/SET values + - One TYPELIB for key names + Every TYPELIB requires one extra value with a NULL pointer and zero length, + which is the end-of-values marker. + TODO-10.5+: + Note, we should eventually reuse this total_typelib_value_count + to allocate interval_array. The above code reserves less space + than total_typelib_value_count pointers. So it seems `interval_array` + and `names` overlap in the memory. Too dangerous to fix in 10.1. + */ + total_typelib_value_count= + (share->fields + 1/*end-of-values marker*/) + + (interval_parts + interval_count/*end-of-values markers*/) + + (keys + 1/*end-of-values marker*/); + if (!(field_ptr = (Field **) alloc_root(&share->mem_root, @@ -1383,6 +1411,12 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, vcol_screen_length))))) goto err; /* purecov: inspected */ + + if (!(typelib_value_lengths= (uint *) alloc_root(&share->mem_root, + total_typelib_value_count * + sizeof(uint *)))) + goto err; + share->field= field_ptr; read_length=(uint) (share->fields * field_pack_length + pos+ (uint) (n_length+int_length+com_length+ @@ -1391,6 +1425,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, share->intervals= (TYPELIB*) (field_ptr+share->fields+1); interval_array= (const char **) (share->intervals+interval_count); + // This looks wrong: shouldn't it be (+2+interval_count) instread of (+3) ? names= (char*) (interval_array+share->fields+interval_parts+keys+3); if (!interval_count) share->intervals= 0; // For better debugging @@ -1403,34 +1438,21 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, memcpy(vcol_screen_pos, disk_buff+read_length-vcol_screen_length, vcol_screen_length); - fix_type_pointers(&interval_array, &share->fieldnames, 1, &names); - if (share->fieldnames.count != share->fields) + if (fix_type_pointers(&interval_array, &typelib_value_lengths, + &share->fieldnames, 1, names, n_length) || + share->fieldnames.count != share->fields) goto err; - fix_type_pointers(&interval_array, share->intervals, interval_count, - &names); - { - /* Set ENUM and SET lengths */ - TYPELIB *interval; - for (interval= share->intervals; - interval < share->intervals + interval_count; - interval++) - { - uint count= (uint) (interval->count + 1) * sizeof(uint); - if (!(interval->type_lengths= (uint *) alloc_root(&share->mem_root, - count))) - goto err; - for (count= 0; count < interval->count; count++) - { - char *val= (char*) interval->type_names[count]; - interval->type_lengths[count]= strlen(val); - } - interval->type_lengths[count]= 0; - } - } + if (fix_type_pointers(&interval_array, &typelib_value_lengths, + share->intervals, interval_count, + names + n_length, int_length)) + goto err; - if (keynames) - fix_type_pointers(&interval_array, &share->keynames, 1, &keynames); + if (keynames.length && + (fix_type_pointers(&interval_array, &typelib_value_lengths, + &share->keynames, 1, keynames.str, keynames.length) || + share->keynames.count != keys)) + goto err; /* Allocate handler */ if (!(handler_file= get_new_handler(share, thd->mem_root, @@ -3216,37 +3238,81 @@ void open_table_error(TABLE_SHARE *share, enum open_frm_error error, ** with a '\0' */ -static void -fix_type_pointers(const char ***array, TYPELIB *point_to_type, uint types, - char **names) +static bool +fix_type_pointers(const char ***typelib_value_names, + uint **typelib_value_lengths, + TYPELIB *point_to_type, uint types, + char *ptr, size_t length) { - char *type_name, *ptr; - char chr; + const char *end= ptr + length; - ptr= *names; while (types--) { + char sep; point_to_type->name=0; - point_to_type->type_names= *array; + point_to_type->type_names= *typelib_value_names; + point_to_type->type_lengths= *typelib_value_lengths; - if ((chr= *ptr)) /* Test if empty type */ + /* + Typelib can be encoded as: + 1) 0x00 - empty typelib + 2) 0xFF 0x00 - empty typelib (index names) + 3) sep (value sep)... 0x00 - non-empty typelib (where sep is a separator) + */ + if (length == 2 && ptr[0] == (char) 0xFF && ptr[1] == '\0') { - while ((type_name=strchr(ptr+1,chr)) != NullS) - { - *((*array)++) = ptr+1; - *type_name= '\0'; /* End string */ - ptr=type_name; - } - ptr+=2; /* Skip end mark and last 0 */ + /* + This is a special case #2. + If there are no indexes at all, index names can be encoded + as a two byte sequence: 0xFF 0x00 + TODO: Check if it's a bug in the FRM packing routine. + It should probably write just 0x00 instead of 0xFF00. + */ + ptr+= 2; } - else - ptr++; - point_to_type->count= (uint) (*array - point_to_type->type_names); + else if ((sep= *ptr++)) // A non-empty typelib + { + for ( ; ptr < end; ) + { + // Now scan the next value+sep pair + char *vend= (char*) memchr(ptr, sep, end - ptr); + if (!vend) + return true; // Bad format + *((*typelib_value_names)++)= ptr; + *((*typelib_value_lengths)++)= vend - ptr; + *vend= '\0'; // Change sep to '\0' + ptr= vend + 1; // Shift from sep to the next byte + /* + Now we can have either: + - the end-of-typelib marker (0x00) + - more value+sep pairs + */ + if (!*ptr) + { + /* + We have an ambiguity here. 0x00 can be an end-of-typelib marker, + but it can also be a part of the next value: + CREATE TABLE t1 (a ENUM(0x61, 0x0062) CHARACTER SET BINARY); + If this is the last ENUM/SET in the table and there is still more + packed data left after 0x00, then we know for sure that 0x00 + is a part of the next value. + TODO-10.5+: we should eventually introduce a new unambiguous + typelib encoding for FRM. + */ + if (!types && ptr + 1 < end) + continue; // A binary value starting with 0x00 + ptr++; // Consume the end-of-typelib marker + break; // End of the current typelib + } + } + } + point_to_type->count= (uint) (*typelib_value_names - + point_to_type->type_names); point_to_type++; - *((*array)++)= NullS; /* End of type */ + *((*typelib_value_names)++)= NullS; /* End of type */ + *((*typelib_value_lengths)++)= 0; /* End of type */ } - *names=ptr; /* Update end */ - return; + return ptr != end; } /* fix_type_pointers */ diff --git a/sql/unireg.cc b/sql/unireg.cc index b116218b60e..2ccc440f71b 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -498,6 +498,18 @@ static uint pack_keys(uchar *keybuff, uint key_count, KEY *keyinfo, } /* pack_keys */ +static uint typelib_values_packed_length(const TYPELIB *t) +{ + uint length= 0; + for (uint i= 0; t->type_names[i]; i++) + { + length+= t->type_lengths[i]; + length++; /* Separator */ + } + return length; +} + + /* Make formheader */ static bool pack_header(THD *thd, uchar *forminfo, @@ -619,9 +631,8 @@ static bool pack_header(THD *thd, uchar *forminfo, field->interval_id=get_interval_id(&int_count,create_fields,field); if (old_int_count != int_count) { - for (const char **pos=field->interval->type_names ; *pos ; pos++) - int_length+=(uint) strlen(*pos)+1; // field + suffix prefix - int_parts+=field->interval->count+1; + int_length+= typelib_values_packed_length(field->interval); + int_parts+= field->interval->count + 1; } } if (f_maybe_null(field->pack_flag)) @@ -710,11 +721,7 @@ static size_t packed_fields_length(List &create_fields) { int_count= field->interval_id; length++; - for (int i=0; field->interval->type_names[i]; i++) - { - length+= field->interval->type_lengths[i]; - length++; - } + length+= typelib_values_packed_length(field->interval); length++; } if (field->vcol_info) From 6462af1c2e2cddf3a96818dc7884e187d8ba7f24 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Fri, 22 May 2020 15:00:29 +0400 Subject: [PATCH 2/8] MDEV-22111 ERROR 1064 & 1033 and SIGSEGV on CREATE TABLE w/ various charsets on 10.4/5 optimized builds | Assertion `(uint) (table_check_constraints - share->check_constraints) == (uint) (share->table_check_constraints - share->field_check_constraints)' failed Additional 10.2 specific tests (with JSON) --- mysql-test/r/ctype_binary.result | 49 +++++++++++++++++++++++++++++ mysql-test/r/ctype_utf16_uca.result | 41 ++++++++++++++++++++++++ mysql-test/r/ctype_utf32.result | 20 ++++++++++++ mysql-test/t/ctype_binary.test | 41 ++++++++++++++++++++++++ mysql-test/t/ctype_utf16_uca.test | 25 +++++++++++++++ mysql-test/t/ctype_utf32.test | 13 ++++++++ 6 files changed, 189 insertions(+) diff --git a/mysql-test/r/ctype_binary.result b/mysql-test/r/ctype_binary.result index 2a69984d81d..e71e600b81f 100644 --- a/mysql-test/r/ctype_binary.result +++ b/mysql-test/r/ctype_binary.result @@ -3267,3 +3267,52 @@ ERROR HY000: Incorrect information in file: 'DIR/t1.frm' # # End of 10.1 tests # +# +# Start of 10.2 tests +# +# +# MDEV-22111 ERROR 1064 & 1033 and SIGSEGV on CREATE TABLE w/ various charsets on 10.4/5 optimized builds | Assertion `(uint) (table_check_constraints - share->check_constraints) == (uint) (share->table_check_constraints - share->field_check_constraints)' failed +# 10.2 tests +# +SET NAMES latin1; +CREATE TABLE t1(c ENUM(0x0061) CHARACTER SET 'Binary', d JSON); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c` enum('\0a') CHARACTER SET binary DEFAULT NULL, + `d` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +INSERT INTO t1 (c) VALUES (1); +SELECT HEX(c) FROM t1; +HEX(c) +0061 +DROP TABLE t1; +CREATE TABLE t1( +c ENUM(0x0061) CHARACTER SET 'Binary', +d INT DEFAULT NULL CHECK (d>0) +); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c` enum('\0a') CHARACTER SET binary DEFAULT NULL, + `d` int(11) DEFAULT NULL CHECK (`d` > 0) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +INSERT INTO t1 VALUES (1,1); +SELECT HEX(c), d FROM t1; +HEX(c) d +0061 1 +DROP TABLE t1; +CREATE TABLE t1(c ENUM(0x0061) CHARACTER SET 'Binary' CHECK (c>0)); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c` enum('\0a') CHARACTER SET binary DEFAULT NULL CHECK (`c` > 0) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +INSERT INTO t1 VALUES (1); +SELECT HEX(c) FROM t1; +HEX(c) +0061 +DROP TABLE t1; +# +# End of 10.2 tests +# diff --git a/mysql-test/r/ctype_utf16_uca.result b/mysql-test/r/ctype_utf16_uca.result index de7852f9a41..fdfc22a94e0 100644 --- a/mysql-test/r/ctype_utf16_uca.result +++ b/mysql-test/r/ctype_utf16_uca.result @@ -7899,5 +7899,46 @@ a b DROP TABLE t1; SET NAMES utf8; # +# MDEV-22111 ERROR 1064 & 1033 and SIGSEGV on CREATE TABLE w/ various charsets on 10.4/5 optimized builds | Assertion `(uint) (table_check_constraints - share->check_constraints) == (uint) (share->table_check_constraints - share->field_check_constraints)' failed +# 10.2 tests +# +SET NAMES utf8, COLLATION_CONNECTION=utf16_hungarian_ci; +CREATE TABLE t1(c ENUM('aaaaaaaa') CHARACTER SET 'Binary',d JSON); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c` enum('\0a\0a\0a\0a\0a\0a\0a\0a') CHARACTER SET binary DEFAULT NULL, + `d` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +INSERT INTO t1 (c) VALUES (1); +SELECT HEX(c) FROM t1; +HEX(c) +00610061006100610061006100610061 +DROP TABLE t1; +CREATE OR REPLACE TABLE t1(c ENUM('aaaaaaaaa') CHARACTER SET 'Binary',d JSON); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c` enum('\0a\0a\0a\0a\0a\0a\0a\0a\0a') CHARACTER SET binary DEFAULT NULL, + `d` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +INSERT INTO t1 (c) VALUES (1); +SELECT HEX(c) FROM t1; +HEX(c) +006100610061006100610061006100610061 +DROP TABLE t1; +CREATE OR REPLACE TABLE t1(c ENUM('aaaaaaaaaa') CHARACTER SET 'Binary',d JSON); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c` enum('\0a\0a\0a\0a\0a\0a\0a\0a\0a\0a') CHARACTER SET binary DEFAULT NULL, + `d` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +INSERT INTO t1 (c) VALUES (1); +SELECT HEX(c) FROM t1; +HEX(c) +0061006100610061006100610061006100610061 +DROP TABLE t1; +# # End of 10.2 tests # diff --git a/mysql-test/r/ctype_utf32.result b/mysql-test/r/ctype_utf32.result index 7bab78a10bf..20395d26da0 100644 --- a/mysql-test/r/ctype_utf32.result +++ b/mysql-test/r/ctype_utf32.result @@ -2868,5 +2868,25 @@ DROP TABLE t1; # SET STORAGE_ENGINE=Default; # +# MDEV-22111 ERROR 1064 & 1033 and SIGSEGV on CREATE TABLE w/ various charsets on 10.4/5 optimized builds | Assertion `(uint) (table_check_constraints - share->check_constraints) == (uint) (share->table_check_constraints - share->field_check_constraints)' failed +# 10.2 tests +# +SET NAMES utf8, COLLATION_CONNECTION=utf32_bin; +CREATE TABLE t1(c1 ENUM('a','b','ac') CHARACTER SET 'Binary',c2 JSON,c3 INT); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` enum('\0\0\0a','\0\0\0b','\0\0\0a\0\0\0c') CHARACTER SET binary DEFAULT NULL, + `c2` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + `c3` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +INSERT INTO t1 (c1) VALUES (1),(2),(3); +SELECT HEX(c1) FROM t1 ORDER BY c1; +HEX(c1) +00000061 +00000062 +0000006100000063 +DROP TABLE t1; +# # End of 10.2 tests # diff --git a/mysql-test/t/ctype_binary.test b/mysql-test/t/ctype_binary.test index 0e37759a89a..b871a41309b 100644 --- a/mysql-test/t/ctype_binary.test +++ b/mysql-test/t/ctype_binary.test @@ -137,3 +137,44 @@ CREATE TABLE t1 (a ENUM('a',0x0061), b ENUM('b')); --echo # --echo # End of 10.1 tests --echo # + +--echo # +--echo # Start of 10.2 tests +--echo # + +--echo # +--echo # MDEV-22111 ERROR 1064 & 1033 and SIGSEGV on CREATE TABLE w/ various charsets on 10.4/5 optimized builds | Assertion `(uint) (table_check_constraints - share->check_constraints) == (uint) (share->table_check_constraints - share->field_check_constraints)' failed +--echo # 10.2 tests +--echo # + +SET NAMES latin1; +CREATE TABLE t1(c ENUM(0x0061) CHARACTER SET 'Binary', d JSON); +SHOW CREATE TABLE t1; +INSERT INTO t1 (c) VALUES (1); +SELECT HEX(c) FROM t1; +DROP TABLE t1; + +CREATE TABLE t1( + c ENUM(0x0061) CHARACTER SET 'Binary', + d INT DEFAULT NULL CHECK (d>0) +); +SHOW CREATE TABLE t1; +INSERT INTO t1 VALUES (1,1); +SELECT HEX(c), d FROM t1; +DROP TABLE t1; + +CREATE TABLE t1(c ENUM(0x0061) CHARACTER SET 'Binary' CHECK (c>0)); +SHOW CREATE TABLE t1; +INSERT INTO t1 VALUES (1); +SELECT HEX(c) FROM t1; +DROP TABLE t1; + + + + + + + +--echo # +--echo # End of 10.2 tests +--echo # diff --git a/mysql-test/t/ctype_utf16_uca.test b/mysql-test/t/ctype_utf16_uca.test index 46d572fbe81..93807232bab 100644 --- a/mysql-test/t/ctype_utf16_uca.test +++ b/mysql-test/t/ctype_utf16_uca.test @@ -243,6 +243,31 @@ SET NAMES utf8, collation_connection=utf16_unicode_520_nopad_ci; SET NAMES utf8; +--echo # +--echo # MDEV-22111 ERROR 1064 & 1033 and SIGSEGV on CREATE TABLE w/ various charsets on 10.4/5 optimized builds | Assertion `(uint) (table_check_constraints - share->check_constraints) == (uint) (share->table_check_constraints - share->field_check_constraints)' failed +--echo # 10.2 tests +--echo # + +SET NAMES utf8, COLLATION_CONNECTION=utf16_hungarian_ci; +CREATE TABLE t1(c ENUM('aaaaaaaa') CHARACTER SET 'Binary',d JSON); # ERROR 1064 (42000): You have an error in your SQL syntax +SHOW CREATE TABLE t1; +INSERT INTO t1 (c) VALUES (1); +SELECT HEX(c) FROM t1; +DROP TABLE t1; + +CREATE OR REPLACE TABLE t1(c ENUM('aaaaaaaaa') CHARACTER SET 'Binary',d JSON); # ERROR 1033 (HY000): Incorrect information in file: './test/t.frm' +SHOW CREATE TABLE t1; +INSERT INTO t1 (c) VALUES (1); +SELECT HEX(c) FROM t1; +DROP TABLE t1; + +CREATE OR REPLACE TABLE t1(c ENUM('aaaaaaaaaa') CHARACTER SET 'Binary',d JSON); # Sig 11 +SHOW CREATE TABLE t1; +INSERT INTO t1 (c) VALUES (1); +SELECT HEX(c) FROM t1; +DROP TABLE t1; + + --echo # --echo # End of 10.2 tests --echo # diff --git a/mysql-test/t/ctype_utf32.test b/mysql-test/t/ctype_utf32.test index 2e739ebfdbc..9821b5de5b2 100644 --- a/mysql-test/t/ctype_utf32.test +++ b/mysql-test/t/ctype_utf32.test @@ -1035,6 +1035,19 @@ let $coll='utf32_nopad_bin'; let $coll_pad='utf32_bin'; --source include/ctype_pad_all_engines.inc + +--echo # +--echo # MDEV-22111 ERROR 1064 & 1033 and SIGSEGV on CREATE TABLE w/ various charsets on 10.4/5 optimized builds | Assertion `(uint) (table_check_constraints - share->check_constraints) == (uint) (share->table_check_constraints - share->field_check_constraints)' failed +--echo # 10.2 tests +--echo # + +SET NAMES utf8, COLLATION_CONNECTION=utf32_bin; +CREATE TABLE t1(c1 ENUM('a','b','ac') CHARACTER SET 'Binary',c2 JSON,c3 INT); +SHOW CREATE TABLE t1; +INSERT INTO t1 (c1) VALUES (1),(2),(3); +SELECT HEX(c1) FROM t1 ORDER BY c1; +DROP TABLE t1; + --echo # --echo # End of 10.2 tests --echo # From be647ff14d6196af825f05020acee8f18af4773d Mon Sep 17 00:00:00 2001 From: Monty Date: Fri, 22 May 2020 18:02:24 +0300 Subject: [PATCH 3/8] Fixed deadlock with LOCK TABLES and ALTER TABLE MDEV-21398 Deadlock (server hang) or assertion failure in Diagnostics_area::set_error_status upon ALTER under lock This failure could only happen if one locked the same table multiple times and then did an ALTER TABLE on the table. Major change is to change all instances of table->m_needs_reopen= true; to table->mark_table_for_reopen(); The main fix for the problem was to ensure that we mark all instances of the table in the locked_table_list and when we reopen the tables, we first close all tables before reopening and locking them. Other things: - Don't call thd->locked_tables_list.reopen_tables if there are no tables marked for reopen. (performance) --- mysql-test/r/lock.result | 14 +++++ mysql-test/t/lock.test | 14 +++++ sql/sql_admin.cc | 4 +- sql/sql_base.cc | 77 +++++++++++++++++++++------ sql/sql_class.h | 10 ++-- sql/sql_parse.cc | 3 +- sql/sql_partition.cc | 8 +-- sql/sql_plugin.cc | 2 +- sql/sql_table.cc | 6 ++- sql/sql_udf.cc | 4 +- sql/table.cc | 11 ++++ sql/table.h | 12 ++++- sql/temporary_tables.cc | 2 +- sql/tztime.cc | 2 +- storage/innobase/handler/ha_innodb.cc | 2 +- 15 files changed, 136 insertions(+), 35 deletions(-) diff --git a/mysql-test/r/lock.result b/mysql-test/r/lock.result index 339cfcaa441..2801407130d 100644 --- a/mysql-test/r/lock.result +++ b/mysql-test/r/lock.result @@ -505,3 +505,17 @@ disconnect con1; connection default; UNLOCK TABLES; DROP TABLE t1, t2; +# +# MDEV-21398 Deadlock (server hang) or assertion failure in +# Diagnostics_area::set_error_status upon ALTER under lock +# +CREATE TABLE t1 (a INT) ENGINE=MyISAM; +LOCK TABLE t1 WRITE, t1 AS t1a READ; +ALTER TABLE t1 CHANGE COLUMN IF EXISTS x xx INT; +Warnings: +Note 1054 Unknown column 'x' in 't1' +UNLOCK TABLES; +DROP TABLE t1; +# +# End of 10.2 tests +# diff --git a/mysql-test/t/lock.test b/mysql-test/t/lock.test index ff77b4991c0..af4a35a5139 100644 --- a/mysql-test/t/lock.test +++ b/mysql-test/t/lock.test @@ -619,3 +619,17 @@ UNLOCK TABLES; UNLOCK TABLES; DROP TABLE t1, t2; +--echo # +--echo # MDEV-21398 Deadlock (server hang) or assertion failure in +--echo # Diagnostics_area::set_error_status upon ALTER under lock +--echo # + +CREATE TABLE t1 (a INT) ENGINE=MyISAM; +LOCK TABLE t1 WRITE, t1 AS t1a READ; +ALTER TABLE t1 CHANGE COLUMN IF EXISTS x xx INT; +UNLOCK TABLES; +DROP TABLE t1; + +--echo # +--echo # End of 10.2 tests +--echo # diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index 0613495f202..7ace3144dc3 100644 --- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -1087,7 +1087,7 @@ send_result_message: } /* Make sure this table instance is not reused after the operation. */ if (table->table) - table->table->m_needs_reopen= true; + table->table->mark_table_for_reopen(); } result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK; table->next_local= save_next_local; @@ -1212,7 +1212,7 @@ err: trans_rollback(thd); if (table && table->table) { - table->table->m_needs_reopen= true; + table->table->mark_table_for_reopen(); table->table= 0; } close_thread_tables(thd); // Shouldn't be needed diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 765c743aba2..6c7ef7a85c9 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2161,9 +2161,9 @@ Locked_tables_list::init_locked_tables(THD *thd) in reopen_tables(). reopen_tables() is a critical path and we don't want to complicate it with extra allocations. */ - m_reopen_array= (TABLE**)alloc_root(&m_locked_tables_root, - sizeof(TABLE*) * - (m_locked_tables_count+1)); + m_reopen_array= (TABLE_LIST**)alloc_root(&m_locked_tables_root, + sizeof(TABLE_LIST*) * + (m_locked_tables_count+1)); if (m_reopen_array == NULL) { reset(); @@ -2273,6 +2273,7 @@ void Locked_tables_list::reset() m_locked_tables_last= &m_locked_tables; m_reopen_array= NULL; m_locked_tables_count= 0; + some_table_marked_for_reopen= 0; } @@ -2368,7 +2369,7 @@ unlink_all_closed_tables(THD *thd, MYSQL_LOCK *lock, size_t reopen_count) in reopen_tables() always links the opened table to the beginning of the open_tables list. */ - DBUG_ASSERT(thd->open_tables == m_reopen_array[reopen_count]); + DBUG_ASSERT(thd->open_tables == m_reopen_array[reopen_count]->table); thd->open_tables->pos_in_locked_tables->table= NULL; thd->open_tables->pos_in_locked_tables= 0; @@ -2398,10 +2399,36 @@ unlink_all_closed_tables(THD *thd, MYSQL_LOCK *lock, size_t reopen_count) } +/* + Mark all instances of the table to be reopened + + This is only needed when LOCK TABLES is active +*/ + +void Locked_tables_list::mark_table_for_reopen(THD *thd, TABLE *table) +{ + TABLE_SHARE *share= table->s; + + for (TABLE_LIST *table_list= m_locked_tables; + table_list; table_list= table_list->next_global) + { + if (table_list->table->s == share) + table_list->table->internal_set_needs_reopen(true); + } + /* This is needed in the case where lock tables where not used */ + table->internal_set_needs_reopen(true); + some_table_marked_for_reopen= 1; +} + + /** Reopen the tables locked with LOCK TABLES and temporarily closed by a DDL statement or FLUSH TABLES. + @param need_reopen If set, reopen open tables that are marked with + for reopen. + If not set, reopen tables that where closed. + @note This function is a no-op if we're not under LOCK TABLES. @return TRUE if an error reopening the tables. May happen in @@ -2419,6 +2446,12 @@ Locked_tables_list::reopen_tables(THD *thd, bool need_reopen) MYSQL_LOCK *merged_lock; DBUG_ENTER("Locked_tables_list::reopen_tables"); + DBUG_ASSERT(some_table_marked_for_reopen || !need_reopen); + + + /* Reset flag that some table was marked for reopen */ + some_table_marked_for_reopen= 0; + for (TABLE_LIST *table_list= m_locked_tables; table_list; table_list= table_list->next_global) { @@ -2442,24 +2475,32 @@ Locked_tables_list::reopen_tables(THD *thd, bool need_reopen) else { if (table_list->table) /* The table was not closed */ - continue; + continue; } - /* Links into thd->open_tables upon success */ - if (open_table(thd, table_list, &ot_ctx)) - { - unlink_all_closed_tables(thd, 0, reopen_count); - DBUG_RETURN(TRUE); - } - table_list->table->pos_in_locked_tables= table_list; - /* See also the comment on lock type in init_locked_tables(). */ - table_list->table->reginfo.lock_type= table_list->lock_type; - DBUG_ASSERT(reopen_count < m_locked_tables_count); - m_reopen_array[reopen_count++]= table_list->table; + m_reopen_array[reopen_count++]= table_list; } if (reopen_count) { + TABLE **tables= (TABLE**) my_alloca(reopen_count * sizeof(TABLE*)); + + for (uint i= 0 ; i < reopen_count ; i++) + { + TABLE_LIST *table_list= m_reopen_array[i]; + /* Links into thd->open_tables upon success */ + if (open_table(thd, table_list, &ot_ctx)) + { + unlink_all_closed_tables(thd, 0, i); + my_afree((void*) tables); + DBUG_RETURN(TRUE); + } + tables[i]= table_list->table; + table_list->table->pos_in_locked_tables= table_list; + /* See also the comment on lock type in init_locked_tables(). */ + table_list->table->reginfo.lock_type= table_list->lock_type; + } + thd->in_lock_tables= 1; /* We re-lock all tables with mysql_lock_tables() at once rather @@ -2472,7 +2513,7 @@ Locked_tables_list::reopen_tables(THD *thd, bool need_reopen) works fine. Patching legacy code of thr_lock.c is risking to break something else. */ - lock= mysql_lock_tables(thd, m_reopen_array, reopen_count, + lock= mysql_lock_tables(thd, tables, reopen_count, MYSQL_OPEN_REOPEN); thd->in_lock_tables= 0; if (lock == NULL || (merged_lock= @@ -2481,9 +2522,11 @@ Locked_tables_list::reopen_tables(THD *thd, bool need_reopen) unlink_all_closed_tables(thd, lock, reopen_count); if (! thd->killed) my_error(ER_LOCK_DEADLOCK, MYF(0)); + my_afree((void*) tables); DBUG_RETURN(TRUE); } thd->lock= merged_lock; + my_afree((void*) tables); } DBUG_RETURN(FALSE); } diff --git a/sql/sql_class.h b/sql/sql_class.h index 111c407f117..80aa5710b62 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1823,20 +1823,23 @@ private: TABLE_LIST *m_locked_tables; TABLE_LIST **m_locked_tables_last; /** An auxiliary array used only in reopen_tables(). */ - TABLE **m_reopen_array; + TABLE_LIST **m_reopen_array; /** Count the number of tables in m_locked_tables list. We can't rely on thd->lock->table_count because it excludes non-transactional temporary tables. We need to know an exact number of TABLE objects. */ - size_t m_locked_tables_count; + uint m_locked_tables_count; public: + bool some_table_marked_for_reopen; + Locked_tables_list() :m_locked_tables(NULL), m_locked_tables_last(&m_locked_tables), m_reopen_array(NULL), - m_locked_tables_count(0) + m_locked_tables_count(0), + some_table_marked_for_reopen(0) { init_sql_alloc(&m_locked_tables_root, MEM_ROOT_BLOCK_SIZE, 0, MYF(MY_THREAD_SPECIFIC)); @@ -1859,6 +1862,7 @@ public: bool restore_lock(THD *thd, TABLE_LIST *dst_table_list, TABLE *table, MYSQL_LOCK *lock); void add_back_last_deleted_lock(TABLE_LIST *dst_table_list); + void mark_table_for_reopen(THD *thd, TABLE *table); }; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 13576daae98..f1c9479adc7 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5998,7 +5998,8 @@ finish: lex->unit.cleanup(); /* close/reopen tables that were marked to need reopen under LOCK TABLES */ - if (! thd->lex->requires_prelocking()) + if (unlikely(thd->locked_tables_list.some_table_marked_for_reopen) && + !thd->lex->requires_prelocking()) thd->locked_tables_list.reopen_tables(thd, true); if (! thd->in_sub_stmt) diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 4a200dd670b..071c9c05129 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -4592,7 +4592,7 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, { *fast_alter_table= true; /* Force table re-open for consistency with the main case. */ - table->m_needs_reopen= true; + table->mark_table_for_reopen(); } else { @@ -4640,7 +4640,7 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, must be reopened. */ *fast_alter_table= true; - table->m_needs_reopen= true; + table->mark_table_for_reopen(); } else { @@ -6418,7 +6418,7 @@ void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt, THD *thd= lpt->thd; TABLE *table= lpt->table; DBUG_ENTER("handle_alter_part_error"); - DBUG_ASSERT(table->m_needs_reopen); + DBUG_ASSERT(table->needs_reopen()); if (close_table) { @@ -6637,7 +6637,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, bool frm_install= FALSE; MDL_ticket *mdl_ticket= table->mdl_ticket; DBUG_ENTER("fast_alter_partition_table"); - DBUG_ASSERT(table->m_needs_reopen); + DBUG_ASSERT(table->needs_reopen()); part_info= table->part_info; lpt->thd= thd; diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 44b2a8220b3..42d2d110f2b 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -1844,7 +1844,7 @@ static void plugin_load(MEM_ROOT *tmp_root) sql_print_error(ER_THD(new_thd, ER_GET_ERRNO), my_errno, table->file->table_type()); end_read_record(&read_record_info); - table->m_needs_reopen= TRUE; // Force close to free memory + table->mark_table_for_reopen(); close_mysql_tables(new_thd); end: delete new_thd; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index e53d51777b4..8d81911bf29 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -7783,7 +7783,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, if (field->default_value) field->default_value->expr->walk(&Item::rename_fields_processor, 1, &column_rename_param); - table->m_needs_reopen= 1; // because new column name is on thd->mem_root + // Force reopen because new column name is on thd->mem_root + table->mark_table_for_reopen(); } /* Check if field is changed */ @@ -8195,7 +8196,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, { check->expr->walk(&Item::rename_fields_processor, 1, &column_rename_param); - table->m_needs_reopen= 1; // because new column name is on thd->mem_root + // Force reopen because new column name is on thd->mem_root + table->mark_table_for_reopen(); } new_constraint_list.push_back(check, thd->mem_root); } diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index 2a72c86707c..9099345a64c 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -255,7 +255,9 @@ void udf_init() if (error > 0) sql_print_error("Got unknown error: %d", my_errno); end_read_record(&read_record_info); - table->m_needs_reopen= TRUE; // Force close to free memory + + // Force close to free memory + table->mark_table_for_reopen(); end: close_mysql_tables(new_thd); diff --git a/sql/table.cc b/sql/table.cc index 995852651d0..611b8d1296f 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -8647,3 +8647,14 @@ void TABLE::initialize_quick_structures() bzero(quick_costs, sizeof(quick_costs)); bzero(quick_n_ranges, sizeof(quick_n_ranges)); } + +/* + Mark table to be reopened after query +*/ + +void TABLE::mark_table_for_reopen() +{ + THD *thd= in_use; + DBUG_ASSERT(thd); + thd->locked_tables_list.mark_table_for_reopen(thd, this); +} diff --git a/sql/table.h b/sql/table.h index 0cf0e4efe00..ba912438a93 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1290,8 +1290,8 @@ public: bool insert_or_update; /* Can be used by the handler */ bool alias_name_used; /* true if table_name is alias */ bool get_fields_in_item_tree; /* Signal to fix_field */ - bool m_needs_reopen; private: + bool m_needs_reopen; bool created; /* For tmp tables. TRUE <=> tmp table was actually created.*/ public: #ifdef HAVE_REPLICATION @@ -1401,6 +1401,16 @@ public: /** Should this instance of the table be reopened? */ inline bool needs_reopen() { return !db_stat || m_needs_reopen; } + /* + Mark that all current connection instances of the table should be + reopen at end of statement + */ + void mark_table_for_reopen(); + /* Should only be called from Locked_tables_list::mark_table_for_reopen() */ + void internal_set_needs_reopen(bool value) + { + m_needs_reopen= value; + } bool alloc_keys(uint key_count); bool check_tmp_key(uint key, uint key_parts, diff --git a/sql/temporary_tables.cc b/sql/temporary_tables.cc index e2179a71625..005a520ff64 100644 --- a/sql/temporary_tables.cc +++ b/sql/temporary_tables.cc @@ -1065,7 +1065,7 @@ TABLE *THD::find_temporary_table(const char *key, uint key_length, case TMP_TABLE_ANY: found= true; break; } } - if (table && unlikely(table->m_needs_reopen)) + if (table && unlikely(table->needs_reopen())) { share->all_tmp_tables.remove(table); free_temporary_table(table); diff --git a/sql/tztime.cc b/sql/tztime.cc index 941530b9656..f58801bf4b7 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -1699,7 +1699,7 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap) { tl->table->use_all_columns(); /* Force close at the end of the function to free memory. */ - tl->table->m_needs_reopen= TRUE; + tl->table->mark_table_for_reopen(); } /* diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 11f6c137b67..ac196f58416 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -3468,7 +3468,7 @@ ha_innobase::reset_template(void) /* Force table to be freed in close_thread_table(). */ DBUG_EXECUTE_IF("free_table_in_fts_query", if (m_prebuilt->in_fts_query) { - table->m_needs_reopen = true; + table->mark_table_for_reopen(); } ); From d8e2fa0c49d506bde037fe448dc9840e8e14cf92 Mon Sep 17 00:00:00 2001 From: Monty Date: Sat, 23 May 2020 14:36:33 +0300 Subject: [PATCH 4/8] Fixed compiler failure on windows --- sql/table.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/table.cc b/sql/table.cc index 611b8d1296f..3bb321d3306 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -3684,7 +3684,7 @@ fix_type_pointers(const char ***typelib_value_names, if (!vend) return true; // Bad format *((*typelib_value_names)++)= ptr; - *((*typelib_value_lengths)++)= vend - ptr; + *((*typelib_value_lengths)++)= (uint) (vend - ptr); *vend= '\0'; // Change sep to '\0' ptr= vend + 1; // Shift from sep to the next byte /* From cf52dd174ecf0e6055f4443be9c1fea8bc37b65e Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Wed, 20 May 2020 13:34:51 +0200 Subject: [PATCH 5/8] MDEV-22545: my_vsnprintf behaves not as in C standard Added parameter %T for string which should be visibly truncated. --- extra/comp_err.c | 1 + include/mysql/service_my_snprintf.h | 6 +- mysql-test/r/perror-win.result | 2 +- mysql-test/r/perror.result | 2 +- mysql-test/suite/rpl/r/rpl_stm_EE_err2.result | 2 +- sql/item.cc | 2 +- sql/item_xmlfunc.cc | 34 +- sql/share/errmsg-utf8.txt | 356 +++++++++--------- strings/my_vsnprintf.c | 55 ++- unittest/mysys/my_vsnprintf-t.c | 30 +- 10 files changed, 267 insertions(+), 223 deletions(-) diff --git a/extra/comp_err.c b/extra/comp_err.c index 39b2915bc00..9408d925a2d 100644 --- a/extra/comp_err.c +++ b/extra/comp_err.c @@ -787,6 +787,7 @@ static ha_checksum checksum_format_specifier(const char* msg) case 'x': case 's': case 'M': + case 'T': chksum= my_checksum(chksum, (uchar*) start, (uint) (p + 1 - start)); start= 0; /* Not in format specifier anymore */ break; diff --git a/include/mysql/service_my_snprintf.h b/include/mysql/service_my_snprintf.h index bd1f069c527..6757a658fb6 100644 --- a/include/mysql/service_my_snprintf.h +++ b/include/mysql/service_my_snprintf.h @@ -55,7 +55,8 @@ Supported formats are 's' (null pointer is accepted, printed as "(null)"), 'b' (extension, see below), 'c', 'd', 'i', 'u', 'x', 'o', - 'X', 'p' (works as 0x%x), 'f', 'g', 'M' (extension, see below). + 'X', 'p' (works as 0x%x), 'f', 'g', 'M' (extension, see below), + 'T' (extension, see below). Standard syntax for positional arguments $n is supported. @@ -69,6 +70,9 @@ Format 'M': takes one integer, prints this integer, space, double quote error message, double quote. In other words printf("%M", n) === printf("%d \"%s\"", n, strerror(n)) + + Format 'T': takes string and print it like s but if the strints should be + truncated puts "..." at the end. */ #ifdef __cplusplus diff --git a/mysql-test/r/perror-win.result b/mysql-test/r/perror-win.result index 139b566757f..12200c88d5a 100644 --- a/mysql-test/r/perror-win.result +++ b/mysql-test/r/perror-win.result @@ -2,6 +2,6 @@ MySQL error code 150: Foreign key constraint is incorrectly formed Win32 error code 150: System trace information was not specified in your CONFIG.SYS file, or tracing is disallowed. OS error code 23: Too many open files in system Win32 error code 23: Data error (cyclic redundancy check). -MySQL error code 1062 (ER_DUP_ENTRY): Duplicate entry '%-.192s' for key %d +MySQL error code 1062 (ER_DUP_ENTRY): Duplicate entry '%-.192T' for key %d Win32 error code 1062: The service has not been started. Illegal error code: 30000 diff --git a/mysql-test/r/perror.result b/mysql-test/r/perror.result index 46554442721..e5eb9a083ee 100644 --- a/mysql-test/r/perror.result +++ b/mysql-test/r/perror.result @@ -1,5 +1,5 @@ Illegal error code: 10000 -MySQL error code 1062 (ER_DUP_ENTRY): Duplicate entry '%-.192s' for key %d +MySQL error code 1062 (ER_DUP_ENTRY): Duplicate entry '%-.192T' for key %d MySQL error code 1408 (ER_STARTUP): %s: ready for connections. Version: '%s' socket: '%s' port: %d %s MySQL error code 1459 (ER_TABLE_NEEDS_UPGRADE): Upgrade required. Please do "REPAIR %s %`s" or dump/reload to fix it! diff --git a/mysql-test/suite/rpl/r/rpl_stm_EE_err2.result b/mysql-test/suite/rpl/r/rpl_stm_EE_err2.result index 4d666c6e8bf..99e7a184e1f 100644 --- a/mysql-test/suite/rpl/r/rpl_stm_EE_err2.result +++ b/mysql-test/suite/rpl/r/rpl_stm_EE_err2.result @@ -11,7 +11,7 @@ drop table t1; connection slave; include/wait_for_slave_sql_to_stop.inc call mtr.add_suppression("Slave SQL.*Query caused different errors on master and slave.*Error on master:.* error code=1062.*Error on slave:.* error.* 0"); -Error: "Query caused different errors on master and slave. Error on master: message (format)='Duplicate entry '%-.192s' for key %d' error code=1062 ; Error on slave: actual message='no error', error code=0. Default database: 'test'. Query: 'insert into t1 values(1),(2)'" (expected different error codes on master and slave) +Error: "Query caused different errors on master and slave. Error on master: message (format)='Duplicate entry '%-.192T' for key %d' error code=1062 ; Error on slave: actual message='no error', error code=0. Default database: 'test'. Query: 'insert into t1 values(1),(2)'" (expected different error codes on master and slave) Errno: "0" (expected 0) drop table t1; include/stop_slave.inc diff --git a/sql/item.cc b/sql/item.cc index 141263f7bba..9451d4203ca 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -6661,7 +6661,7 @@ Item_float::Item_float(THD *thd, const char *str_arg, uint length): &error); if (error) { - char tmp[NAME_LEN + 1]; + char tmp[NAME_LEN + 2]; my_snprintf(tmp, sizeof(tmp), "%.*s", length, str_arg); my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "double", tmp); } diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc index 0e62e29bfe2..9cc123e57d6 100644 --- a/sql/item_xmlfunc.cc +++ b/sql/item_xmlfunc.cc @@ -997,11 +997,16 @@ static Item *create_comparator(MY_XPATH *xpath, b->type() == Item::XPATH_NODESET) { uint len= (uint)(xpath->query.end - context->beg); - set_if_smaller(len, 32); - my_printf_error(ER_UNKNOWN_ERROR, - "XPATH error: " - "comparison of two nodesets is not supported: '%.*s'", - MYF(0), len, context->beg); + if (len <= 32) + my_printf_error(ER_UNKNOWN_ERROR, + "XPATH error: " + "comparison of two nodesets is not supported: '%.*s'", + MYF(0), len, context->beg); + else + my_printf_error(ER_UNKNOWN_ERROR, + "XPATH error: " + "comparison of two nodesets is not supported: '%.32T'", + MYF(0), context->beg); return 0; // TODO: Comparison of two nodesets } @@ -2634,9 +2639,12 @@ my_xpath_parse_VariableReference(MY_XPATH *xpath) xpath->item= NULL; DBUG_ASSERT(xpath->query.end > dollar_pos); uint len= (uint)(xpath->query.end - dollar_pos); - set_if_smaller(len, 32); - my_printf_error(ER_UNKNOWN_ERROR, "Unknown XPATH variable at: '%.*s'", - MYF(0), len, dollar_pos); + if (len <= 32) + my_printf_error(ER_UNKNOWN_ERROR, "Unknown XPATH variable at: '%.*s'", + MYF(0), len, dollar_pos); + else + my_printf_error(ER_UNKNOWN_ERROR, "Unknown XPATH variable at: '%.32T'", + MYF(0), dollar_pos); } } return xpath->item ? 1 : 0; @@ -2767,9 +2775,13 @@ bool Item_xml_str_func::fix_fields(THD *thd, Item **ref) if (!rc) { uint clen= (uint)(xpath.query.end - xpath.lasttok.beg); - set_if_smaller(clen, 32); - my_printf_error(ER_UNKNOWN_ERROR, "XPATH syntax error: '%.*s'", - MYF(0), clen, xpath.lasttok.beg); + if (clen <= 32) + my_printf_error(ER_UNKNOWN_ERROR, "XPATH syntax error: '%.*s'", + MYF(0), clen, xpath.lasttok.beg); + else + my_printf_error(ER_UNKNOWN_ERROR, "XPATH syntax error: '%.32T'", + MYF(0), xpath.lasttok.beg); + return true; } diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 36e5fb9be1e..ff495f69a4f 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -1197,30 +1197,30 @@ ER_TABLE_EXISTS_ERROR 42S01 swe "Tabellen '%-.192s' finns redan" ukr "Таблиця '%-.192s' вже існує" ER_BAD_TABLE_ERROR 42S02 - cze "Neznámá tabulka '%-.100s'" - dan "Ukendt tabel '%-.100s'" - nla "Onbekende tabel '%-.100s'" - eng "Unknown table '%-.100s'" - est "Tundmatu tabel '%-.100s'" - fre "Table '%-.100s' inconnue" - ger "Unbekannte Tabelle '%-.100s'" - greek "Αγνωστος πίνακας '%-.100s'" - hindi "अज्ञात टेबल '%-.100s'" - hun "Ervenytelen tabla: '%-.100s'" - ita "Tabella '%-.100s' sconosciuta" - jpn "'%-.100s' は不明な表です。" - kor "테이블 '%-.100s'는 알수 없음" - nor "Ukjent tabell '%-.100s'" - norwegian-ny "Ukjent tabell '%-.100s'" - pol "Nieznana tabela '%-.100s'" - por "Tabela '%-.100s' desconhecida" - rum "Tabela '%-.100s' este invalida" - rus "Неизвестная таблица '%-.100s'" - serbian "Nepoznata tabela '%-.100s'" - slo "Neznáma tabuľka '%-.100s'" - spa "Tabla '%-.100s' desconocida" - swe "Okänd tabell '%-.100s'" - ukr "Невідома таблиця '%-.100s'" + cze "Neznámá tabulka '%-.100T'" + dan "Ukendt tabel '%-.100T'" + nla "Onbekende tabel '%-.100T'" + eng "Unknown table '%-.100T'" + est "Tundmatu tabel '%-.100T'" + fre "Table '%-.100T' inconnue" + ger "Unbekannte Tabelle '%-.100T'" + greek "Αγνωστος πίνακας '%-.100T'" + hindi "अज्ञात टेबल '%-.100T'" + hun "Ervenytelen tabla: '%-.100T'" + ita "Tabella '%-.100T' sconosciuta" + jpn "'%-.100T' は不明な表です。" + kor "테이블 '%-.100T'는 알수 없음" + nor "Ukjent tabell '%-.100T'" + norwegian-ny "Ukjent tabell '%-.100T'" + pol "Nieznana tabela '%-.100T'" + por "Tabela '%-.100T' desconhecida" + rum "Tabela '%-.100T' este invalida" + rus "Неизвестная таблица '%-.100T'" + serbian "Nepoznata tabela '%-.100T'" + slo "Neznáma tabuľka '%-.100T'" + spa "Tabla '%-.100T' desconocida" + swe "Okänd tabell '%-.100T'" + ukr "Невідома таблиця '%-.100T'" ER_NON_UNIQ_ERROR 23000 cze "Sloupec '%-.192s' v %-.192s není zcela jasný" dan "Felt: '%-.192s' i tabel %-.192s er ikke entydigt" @@ -1394,30 +1394,30 @@ ER_WRONG_VALUE_COUNT 21S01 swe "Antalet kolumner motsvarar inte antalet värden" ukr "Кількість стовбців не співпадає з кількістю значень" ER_TOO_LONG_IDENT 42000 S1009 - cze "Jméno identifikátoru '%-.100s' je příliš dlouhé" - dan "Navnet '%-.100s' er for langt" - nla "Naam voor herkenning '%-.100s' is te lang" - eng "Identifier name '%-.100s' is too long" - est "Identifikaatori '%-.100s' nimi on liiga pikk" - fre "Le nom de l'identificateur '%-.100s' est trop long" - ger "Name des Bezeichners '%-.100s' ist zu lang" - greek "Το identifier name '%-.100s' είναι πολύ μεγάλο" - hindi "पहचानकर्ता का नाम '%-.100s' बहुत लंबा है" - hun "A(z) '%-.100s' azonositonev tul hosszu" - ita "Il nome dell'identificatore '%-.100s' e` troppo lungo" - jpn "識別子名 '%-.100s' は長すぎます。" - kor "Identifier '%-.100s'는 너무 길군요." - nor "Identifikator '%-.100s' er for lang" - norwegian-ny "Identifikator '%-.100s' er for lang" - pol "Nazwa identyfikatora '%-.100s' jest zbyt długa" - por "Nome identificador '%-.100s' é longo demais" - rum "Numele indentificatorului '%-.100s' este prea lung" - rus "Слишком длинный идентификатор '%-.100s'" - serbian "Ime '%-.100s' je predugačko" - slo "Meno identifikátora '%-.100s' je príliš dlhé" - spa "El nombre del identificador '%-.100s' es demasiado grande" - swe "Kolumnnamn '%-.100s' är för långt" - ukr "Ім'я ідентифікатора '%-.100s' задовге" + cze "Jméno identifikátoru '%-.100T' je příliš dlouhé" + dan "Navnet '%-.100T' er for langt" + nla "Naam voor herkenning '%-.100T' is te lang" + eng "Identifier name '%-.100T' is too long" + est "Identifikaatori '%-.100T' nimi on liiga pikk" + fre "Le nom de l'identificateur '%-.100T' est trop long" + ger "Name des Bezeichners '%-.100T' ist zu lang" + greek "Το identifier name '%-.100T' είναι πολύ μεγάλο" + hindi "पहचानकर्ता का नाम '%-.100T' बहुत लंबा है" + hun "A(z) '%-.100T' azonositonev tul hosszu" + ita "Il nome dell'identificatore '%-.100T' e` troppo lungo" + jpn "識別子名 '%-.100T' は長すぎます。" + kor "Identifier '%-.100T'는 너무 길군요." + nor "Identifikator '%-.100T' er for lang" + norwegian-ny "Identifikator '%-.100T' er for lang" + pol "Nazwa identyfikatora '%-.100T' jest zbyt długa" + por "Nome identificador '%-.100T' é longo demais" + rum "Numele indentificatorului '%-.100T' este prea lung" + rus "Слишком длинный идентификатор '%-.100T'" + serbian "Ime '%-.100T' je predugačko" + slo "Meno identifikátora '%-.100T' je príliš dlhé" + spa "El nombre del identificador '%-.100T' es demasiado grande" + swe "Kolumnnamn '%-.100T' är för långt" + ukr "Ім'я ідентифікатора '%-.100T' задовге" ER_DUP_FIELDNAME 42S21 S1009 cze "Zdvojené jméno sloupce '%-.192s'" dan "Feltnavnet '%-.192s' findes allerede" @@ -1471,30 +1471,30 @@ ER_DUP_KEYNAME 42000 S1009 # When using this error code, please use ER(ER_DUP_ENTRY_WITH_KEY_NAME) # for the message string. See, for example, code in handler.cc. ER_DUP_ENTRY 23000 S1009 - cze "Zdvojený klíč '%-.192s' (číslo klíče %d)" - dan "Ens værdier '%-.192s' for indeks %d" - nla "Dubbele ingang '%-.192s' voor zoeksleutel %d" - eng "Duplicate entry '%-.192s' for key %d" - est "Kattuv väärtus '%-.192s' võtmele %d" - fre "Duplicata du champ '%-.192s' pour la clef %d" - ger "Doppelter Eintrag '%-.192s' für Schlüssel %d" - greek "Διπλή εγγραφή '%-.192s' για το κλειδί %d" - hindi "सामान प्रवेश '%-.192s' KEY %d के लिए" - hun "Duplikalt bejegyzes '%-.192s' a %d kulcs szerint" - ita "Valore duplicato '%-.192s' per la chiave %d" - jpn "'%-.192s' は索引 %d で重複しています。" - kor "중복된 입력 값 '%-.192s': key %d" - nor "Like verdier '%-.192s' for nøkkel %d" - norwegian-ny "Like verdiar '%-.192s' for nykkel %d" - pol "Powtórzone wystąpienie '%-.192s' dla klucza %d" - por "Entrada '%-.192s' duplicada para a chave %d" - rum "Cimpul '%-.192s' e duplicat pentru cheia %d" - rus "Дублирующаяся запись '%-.192s' по ключу %d" - serbian "Dupliran unos '%-.192s' za ključ '%d'" - slo "Opakovaný kľúč '%-.192s' (číslo kľúča %d)" - spa "Entrada duplicada '%-.192s' para la clave %d" - swe "Dublett '%-.192s' för nyckel %d" - ukr "Дублюючий запис '%-.192s' для ключа %d" + cze "Zdvojený klíč '%-.192T' (číslo klíče %d)" + dan "Ens værdier '%-.192T' for indeks %d" + nla "Dubbele ingang '%-.192T' voor zoeksleutel %d" + eng "Duplicate entry '%-.192T' for key %d" + est "Kattuv väärtus '%-.192T' võtmele %d" + fre "Duplicata du champ '%-.192T' pour la clef %d" + ger "Doppelter Eintrag '%-.192T' für Schlüssel %d" + greek "Διπλή εγγραφή '%-.192T' για το κλειδί %d" + hindi "सामान प्रवेश '%-.192T' KEY %d के लिए" + hun "Duplikalt bejegyzes '%-.192T' a %d kulcs szerint" + ita "Valore duplicato '%-.192T' per la chiave %d" + jpn "'%-.192T' は索引 %d で重複しています。" + kor "중복된 입력 값 '%-.192T': key %d" + nor "Like verdier '%-.192T' for nøkkel %d" + norwegian-ny "Like verdiar '%-.192T' for nykkel %d" + pol "Powtórzone wystąpienie '%-.192T' dla klucza %d" + por "Entrada '%-.192T' duplicada para a chave %d" + rum "Cimpul '%-.192T' e duplicat pentru cheia %d" + rus "Дублирующаяся запись '%-.192T' по ключу %d" + serbian "Dupliran unos '%-.192T' za ključ '%d'" + slo "Opakovaný kľúč '%-.192T' (číslo kľúča %d)" + spa "Entrada duplicada '%-.192T' para la clave %d" + swe "Dublett '%-.192T' för nyckel %d" + ukr "Дублюючий запис '%-.192T' для ключа %d" ER_WRONG_FIELD_SPEC 42000 S1009 cze "Chybná specifikace sloupce '%-.192s'" dan "Forkert kolonnespecifikaton for felt '%-.192s'" @@ -1521,30 +1521,30 @@ ER_WRONG_FIELD_SPEC 42000 S1009 swe "Felaktigt kolumntyp för kolumn '%-.192s'" ukr "Невірний специфікатор стовбця '%-.192s'" ER_PARSE_ERROR 42000 s1009 - cze "%s blízko '%-.80s' na řádku %d" - dan "%s nær '%-.80s' på linje %d" - nla "%s bij '%-.80s' in regel %d" - eng "%s near '%-.80s' at line %d" - est "%s '%-.80s' ligidal real %d" - fre "%s près de '%-.80s' à la ligne %d" - ger "%s bei '%-.80s' in Zeile %d" - greek "%s πλησίον '%-.80s' στη γραμμή %d" - hindi "%s के पास '%-.80s' लाइन %d में" - hun "A %s a '%-.80s'-hez kozeli a %d sorban" - ita "%s vicino a '%-.80s' linea %d" - jpn "%s : '%-.80s' 付近 %d 行目" - kor "'%s' 에러 같읍니다. ('%-.80s' 명령어 라인 %d)" - nor "%s nær '%-.80s' på linje %d" - norwegian-ny "%s attmed '%-.80s' på line %d" - pol "%s obok '%-.80s' w linii %d" - por "%s próximo a '%-.80s' na linha %d" - rum "%s linga '%-.80s' pe linia %d" - rus "%s около '%-.80s' на строке %d" - serbian "'%s' u iskazu '%-.80s' na liniji %d" - slo "%s blízko '%-.80s' na riadku %d" - spa "%s cerca '%-.80s' en la linea %d" - swe "%s nära '%-.80s' på rad %d" - ukr "%s біля '%-.80s' в строці %d" + cze "%s blízko '%-.80T' na řádku %d" + dan "%s nær '%-.80T' på linje %d" + nla "%s bij '%-.80T' in regel %d" + eng "%s near '%-.80T' at line %d" + est "%s '%-.80T' ligidal real %d" + fre "%s près de '%-.80T' à la ligne %d" + ger "%s bei '%-.80T' in Zeile %d" + greek "%s πλησίον '%-.80T' στη γραμμή %d" + hindi "%s के पास '%-.80T' लाइन %d में" + hun "A %s a '%-.80T'-hez kozeli a %d sorban" + ita "%s vicino a '%-.80T' linea %d" + jpn "%s : '%-.80T' 付近 %d 行目" + kor "'%s' 에러 같읍니다. ('%-.80T' 명령어 라인 %d)" + nor "%s nær '%-.80T' på linje %d" + norwegian-ny "%s attmed '%-.80T' på line %d" + pol "%s obok '%-.80T' w linii %d" + por "%s próximo a '%-.80T' na linha %d" + rum "%s linga '%-.80T' pe linia %d" + rus "%s около '%-.80T' на строке %d" + serbian "'%s' u iskazu '%-.80T' na liniji %d" + slo "%s blízko '%-.80T' na riadku %d" + spa "%s cerca '%-.80T' en la linea %d" + swe "%s nära '%-.80T' på rad %d" + ukr "%s біля '%-.80T' в строці %d" ER_EMPTY_QUERY 42000 cze "Výsledek dotazu je prázdný" dan "Forespørgsel var tom" @@ -2380,30 +2380,30 @@ ER_TABLE_NOT_LOCKED ER_UNUSED_17 eng "You should never see it" ER_WRONG_DB_NAME 42000 - cze "Nepřípustné jméno databáze '%-.100s'" - dan "Ugyldigt database navn '%-.100s'" - nla "Databasenaam '%-.100s' is niet getoegestaan" - eng "Incorrect database name '%-.100s'" - est "Vigane andmebaasi nimi '%-.100s'" - fre "Nom de base de donnée illégal: '%-.100s'" - ger "Unerlaubter Datenbankname '%-.100s'" - greek "Λάθος όνομα βάσης δεδομένων '%-.100s'" - hindi "डेटाबेस नाम '%-.100s' गलत है" - hun "Hibas adatbazisnev: '%-.100s'" - ita "Nome database errato '%-.100s'" - jpn "データベース名 '%-.100s' は不正です。" - kor "'%-.100s' 데이타베이스의 이름이 부정확합니다." - nor "Ugyldig database navn '%-.100s'" - norwegian-ny "Ugyldig database namn '%-.100s'" - pol "Niedozwolona nazwa bazy danych '%-.100s'" - por "Nome de banco de dados '%-.100s' incorreto" - rum "Numele bazei de date este incorect '%-.100s'" - rus "Некорректное имя базы данных '%-.100s'" - serbian "Pogrešno ime baze '%-.100s'" - slo "Neprípustné meno databázy '%-.100s'" - spa "Nombre de base de datos ilegal '%-.100s'" - swe "Felaktigt databasnamn '%-.100s'" - ukr "Невірне ім'я бази данних '%-.100s'" + cze "Nepřípustné jméno databáze '%-.100T'" + dan "Ugyldigt database navn '%-.100T'" + nla "Databasenaam '%-.100T' is niet getoegestaan" + eng "Incorrect database name '%-.100T'" + est "Vigane andmebaasi nimi '%-.100T'" + fre "Nom de base de donnée illégal: '%-.100T'" + ger "Unerlaubter Datenbankname '%-.100T'" + greek "Λάθος όνομα βάσης δεδομένων '%-.100T'" + hindi "डेटाबेस नाम '%-.100T' गलत है" + hun "Hibas adatbazisnev: '%-.100T'" + ita "Nome database errato '%-.100T'" + jpn "データベース名 '%-.100T' は不正です。" + kor "'%-.100T' 데이타베이스의 이름이 부정확합니다." + nor "Ugyldig database navn '%-.100T'" + norwegian-ny "Ugyldig database namn '%-.100T'" + pol "Niedozwolona nazwa bazy danych '%-.100T'" + por "Nome de banco de dados '%-.100T' incorreto" + rum "Numele bazei de date este incorect '%-.100T'" + rus "Некорректное имя базы данных '%-.100T'" + serbian "Pogrešno ime baze '%-.100T'" + slo "Neprípustné meno databázy '%-.100T'" + spa "Nombre de base de datos ilegal '%-.100T'" + swe "Felaktigt databasnamn '%-.100T'" + ukr "Невірне ім'я бази данних '%-.100T'" ER_WRONG_TABLE_NAME 42000 cze "Nepřípustné jméno tabulky '%-.100s'" dan "Ugyldigt tabel navn '%-.100s'" @@ -4688,15 +4688,15 @@ ER_NO_DEFAULT 42000 spa "Variable '%-.64s' no tiene un valor patrón" swe "Variabel '%-.64s' har inte ett DEFAULT-värde" ER_WRONG_VALUE_FOR_VAR 42000 - nla "Variabele '%-.64s' kan niet worden gewijzigd naar de waarde '%-.200s'" - eng "Variable '%-.64s' can't be set to the value of '%-.200s'" - ger "Variable '%-.64s' kann nicht auf '%-.200s' gesetzt werden" - ita "Alla variabile '%-.64s' non puo' essere assegato il valore '%-.200s'" - jpn "変数 '%-.64s' に値 '%-.200s' を設定できません。" - por "Variável '%-.64s' não pode ser configurada para o valor de '%-.200s'" - rus "Переменная '%-.64s' не может быть установлена в значение '%-.200s'" - spa "Variable '%-.64s' no puede ser configurada para el valor de '%-.200s'" - swe "Variabel '%-.64s' kan inte sättas till '%-.200s'" + nla "Variabele '%-.64s' kan niet worden gewijzigd naar de waarde '%-.200T'" + eng "Variable '%-.64s' can't be set to the value of '%-.200T'" + ger "Variable '%-.64s' kann nicht auf '%-.200T' gesetzt werden" + ita "Alla variabile '%-.64s' non puo' essere assegato il valore '%-.200T'" + jpn "変数 '%-.64s' に値 '%-.200T' を設定できません。" + por "Variável '%-.64s' não pode ser configurada para o valor de '%-.200T'" + rus "Переменная '%-.64s' не может быть установлена в значение '%-.200T'" + spa "Variable '%-.64s' no puede ser configurada para el valor de '%-.200T'" + swe "Variabel '%-.64s' kan inte sättas till '%-.200T'" ER_WRONG_TYPE_FOR_VAR 42000 nla "Foutief argumenttype voor variabele '%-.64s'" eng "Incorrect argument type to variable '%-.64s'" @@ -5140,11 +5140,11 @@ ER_DUPLICATED_VALUE_IN_TYPE por "Coluna '%-.100s' tem valor duplicado '%-.64s' em %s" spa "Columna '%-.100s' tiene valor doblado '%-.64s' en %s" ER_TRUNCATED_WRONG_VALUE 22007 - eng "Truncated incorrect %-.32s value: '%-.128s'" - ger "Falscher %-.32s-Wert gekürzt: '%-.128s'" - jpn "不正な %-.32s の値が切り捨てられました。: '%-.128s'" - por "Truncado errado %-.32s valor: '%-.128s'" - spa "Equivocado truncado %-.32s valor: '%-.128s'" + eng "Truncated incorrect %-.32T value: '%-.128T'" + ger "Falscher %-.32T-Wert gekürzt: '%-.128T'" + jpn "不正な %-.32T の値が切り捨てられました。: '%-.128T'" + por "Truncado errado %-.32T valor: '%-.128T'" + spa "Equivocado truncado %-.32T valor: '%-.128T'" ER_TOO_MUCH_AUTO_TIMESTAMP_COLS eng "Incorrect table definition; there can be only one TIMESTAMP column with CURRENT_TIMESTAMP in DEFAULT or ON UPDATE clause" ger "Fehlerhafte Tabellendefinition. Es kann nur eine einzige TIMESTAMP-Spalte mit CURRENT_TIMESTAMP als DEFAULT oder in einer ON-UPDATE-Klausel geben" @@ -5181,8 +5181,8 @@ ER_WARN_INVALID_TIMESTAMP eng "Invalid TIMESTAMP value in column '%s' at row %lu" ger "Ungültiger TIMESTAMP-Wert in Feld '%s', Zeile %lu" ER_INVALID_CHARACTER_STRING - eng "Invalid %s character string: '%.64s'" - ger "Ungültiger %s-Zeichen-String: '%.64s'" + eng "Invalid %s character string: '%.64T'" + ger "Ungültiger %s-Zeichen-String: '%.64T'" ER_WARN_ALLOWED_PACKET_OVERFLOWED eng "Result of %s() was larger than max_allowed_packet (%ld) - truncated" ger "Ergebnis von %s() war größer als max_allowed_packet (%ld) Bytes und wurde deshalb gekürzt" @@ -5426,11 +5426,11 @@ ER_DIVISION_BY_ZERO 22012 ger "Division durch 0" hindi "0 से विभाजन" ER_TRUNCATED_WRONG_VALUE_FOR_FIELD 22007 - eng "Incorrect %-.32s value: '%-.128s' for column `%.192s`.`%.192s`.`%.192s` at row %lu" - ger "Falscher %-.32s-Wert: '%-.128s' für Feld '`%.192s`.`%.192s`.`%.192s` in Zeile %lu" + eng "Incorrect %-.32s value: '%-.128T' for column `%.192s`.`%.192s`.`%.192s` at row %lu" + ger "Falscher %-.32s-Wert: '%-.128T' für Feld '`%.192s`.`%.192s`.`%.192s` in Zeile %lu" ER_ILLEGAL_VALUE_FOR_TYPE 22007 - eng "Illegal %s '%-.192s' value found during parsing" - ger "Nicht zulässiger %s-Wert '%-.192s' beim Parsen gefunden" + eng "Illegal %s '%-.192T' value found during parsing" + ger "Nicht zulässiger %s-Wert '%-.192T' beim Parsen gefunden" ER_VIEW_NONUPD_CHECK eng "CHECK OPTION on non-updatable view %`-.192s.%`-.192s" ger "CHECK OPTION auf nicht-aktualisierbarem View %`-.192s.%`-.192s" @@ -5576,8 +5576,8 @@ ER_CANT_CREATE_USER_WITH_GRANT 42000 eng "You are not allowed to create a user with GRANT" ger "Sie dürfen keinen Benutzer mit GRANT anlegen" ER_WRONG_VALUE_FOR_TYPE - eng "Incorrect %-.32s value: '%-.128s' for function %-.32s" - ger "Falscher %-.32s-Wert: '%-.128s' für Funktion %-.32s" + eng "Incorrect %-.32s value: '%-.128T' for function %-.32s" + ger "Falscher %-.32s-Wert: '%-.128T' für Funktion %-.32s" ER_TABLE_DEF_CHANGED eng "Table definition has changed, please retry transaction" ger "Tabellendefinition wurde geändert, bitte starten Sie die Transaktion neu" @@ -5756,8 +5756,8 @@ ER_HOSTNAME ger "Hostname" hindi "होस्ट का नाम" ER_WRONG_STRING_LENGTH - eng "String '%-.70s' is too long for %s (should be no longer than %d)" - ger "String '%-.70s' ist zu lang für %s (sollte nicht länger sein als %d)" + eng "String '%-.70T' is too long for %s (should be no longer than %d)" + ger "String '%-.70T' ist zu lang für %s (sollte nicht länger sein als %d)" ER_NON_INSERTABLE_TABLE eng "The target table %-.100s of the %s is not insertable-into" ger "Die Zieltabelle %-.100s von %s ist nicht einfügbar" @@ -5964,8 +5964,8 @@ ER_PLUGIN_IS_NOT_LOADED eng "Plugin '%-.192s' is not loaded" ger "Plugin '%-.192s' ist nicht geladen" ER_WRONG_VALUE - eng "Incorrect %-.32s value: '%-.128s'" - ger "Falscher %-.32s-Wert: '%-.128s'" + eng "Incorrect %-.32s value: '%-.128T'" + ger "Falscher %-.32s-Wert: '%-.128T'" ER_NO_PARTITION_FOR_GIVEN_VALUE eng "Table has no partition for value %-.64s" ger "Tabelle hat für den Wert %-.64s keine Partition" @@ -6108,8 +6108,8 @@ ER_WRONG_PARTITION_NAME ER_CANT_CHANGE_TX_CHARACTERISTICS 25001 eng "Transaction characteristics can't be changed while a transaction is in progress" ER_DUP_ENTRY_AUTOINCREMENT_CASE - eng "ALTER TABLE causes auto_increment resequencing, resulting in duplicate entry '%-.192s' for key '%-.192s'" - ger "ALTER TABLE führt zur Neusequenzierung von auto_increment, wodurch der doppelte Eintrag '%-.192s' für Schlüssel '%-.192s' auftritt" + eng "ALTER TABLE causes auto_increment resequencing, resulting in duplicate entry '%-.192T' for key '%-.192s'" + ger "ALTER TABLE führt zur Neusequenzierung von auto_increment, wodurch der doppelte Eintrag '%-.192T' für Schlüssel '%-.192s' auftritt" ER_EVENT_MODIFY_QUEUE_ERROR eng "Internal scheduler error %d" ger "Interner Scheduler-Fehler %d" @@ -6164,29 +6164,29 @@ ER_NATIVE_FCT_NAME_COLLISION # When using this error message, use the ER_DUP_ENTRY error code. See, for # example, code in handler.cc. ER_DUP_ENTRY_WITH_KEY_NAME 23000 S1009 - cze "Zvojený klíč '%-.64s' (číslo klíče '%-.192s')" - dan "Ens værdier '%-.64s' for indeks '%-.192s'" - nla "Dubbele ingang '%-.64s' voor zoeksleutel '%-.192s'" - eng "Duplicate entry '%-.64s' for key '%-.192s'" - est "Kattuv väärtus '%-.64s' võtmele '%-.192s'" - fre "Duplicata du champ '%-.64s' pour la clef '%-.192s'" - ger "Doppelter Eintrag '%-.64s' für Schlüssel '%-.192s'" - greek "Διπλή εγγραφή '%-.64s' για το κλειδί '%-.192s'" - hun "Duplikalt bejegyzes '%-.64s' a '%-.192s' kulcs szerint" - ita "Valore duplicato '%-.64s' per la chiave '%-.192s'" - jpn "'%-.64s' は索引 '%-.192s' で重複しています。" - kor "중복된 입력 값 '%-.64s': key '%-.192s'" - nor "Like verdier '%-.64s' for nøkkel '%-.192s'" - norwegian-ny "Like verdiar '%-.64s' for nykkel '%-.192s'" - pol "Powtórzone wystąpienie '%-.64s' dla klucza '%-.192s'" - por "Entrada '%-.64s' duplicada para a chave '%-.192s'" - rum "Cimpul '%-.64s' e duplicat pentru cheia '%-.192s'" - rus "Дублирующаяся запись '%-.64s' по ключу '%-.192s'" - serbian "Dupliran unos '%-.64s' za ključ '%-.192s'" - slo "Opakovaný kľúč '%-.64s' (číslo kľúča '%-.192s')" - spa "Entrada duplicada '%-.64s' para la clave '%-.192s'" - swe "Dublett '%-.64s' för nyckel '%-.192s'" - ukr "Дублюючий запис '%-.64s' для ключа '%-.192s'" + cze "Zvojený klíč '%-.64T' (číslo klíče '%-.192s')" + dan "Ens værdier '%-.64T' for indeks '%-.192s'" + nla "Dubbele ingang '%-.64T' voor zoeksleutel '%-.192s'" + eng "Duplicate entry '%-.64T' for key '%-.192s'" + est "Kattuv väärtus '%-.64T' võtmele '%-.192s'" + fre "Duplicata du champ '%-.64T' pour la clef '%-.192s'" + ger "Doppelter Eintrag '%-.64T' für Schlüssel '%-.192s'" + greek "Διπλή εγγραφή '%-.64T' για το κλειδί '%-.192s'" + hun "Duplikalt bejegyzes '%-.64T' a '%-.192s' kulcs szerint" + ita "Valore duplicato '%-.64T' per la chiave '%-.192s'" + jpn "'%-.64T' は索引 '%-.192s' で重複しています。" + kor "중복된 입력 값 '%-.64T': key '%-.192s'" + nor "Like verdier '%-.64T' for nøkkel '%-.192s'" + norwegian-ny "Like verdiar '%-.64T' for nykkel '%-.192s'" + pol "Powtórzone wystąpienie '%-.64T' dla klucza '%-.192s'" + por "Entrada '%-.64T' duplicada para a chave '%-.192s'" + rum "Cimpul '%-.64T' e duplicat pentru cheia '%-.192s'" + rus "Дублирующаяся запись '%-.64T' по ключу '%-.192s'" + serbian "Dupliran unos '%-.64T' za ključ '%-.192s'" + slo "Opakovaný kľúč '%-.64T' (číslo kľúča '%-.192s')" + spa "Entrada duplicada '%-.64T' para la clave '%-.192s'" + swe "Dublett '%-.64T' för nyckel '%-.192s'" + ukr "Дублюючий запис '%-.64T' для ключа '%-.192s'" ER_BINLOG_PURGE_EMFILE eng "Too many files opened, please execute the command again" ger "Zu viele offene Dateien, bitte führen Sie den Befehl noch einmal aus" @@ -6467,8 +6467,8 @@ ER_SLAVE_CANT_CREATE_CONVERSION ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT eng "Cannot modify @@session.binlog_format inside a transaction" ER_PATH_LENGTH - eng "The path specified for %.64s is too long" - hindi "%.64s के लिए निर्दिष्ट पथ बहुत लंबा है" + eng "The path specified for %.64T is too long" + hindi "%.64T के लिए निर्दिष्ट पथ बहुत लंबा है" ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT eng "'%s' is deprecated and will be removed in a future release" ger "'%s' ist veraltet und wird in einer zukünftigen Version entfernt werden" @@ -6573,7 +6573,7 @@ ER_MULTI_UPDATE_KEY_CONFLICT # When translating this error message make sure to include "ALTER TABLE" in the # message as mysqlcheck parses the error message looking for ALTER TABLE. ER_TABLE_NEEDS_REBUILD - eng "Table rebuild required. Please do \"ALTER TABLE `%-.32s` FORCE\" or dump/reload to fix it!" + eng "Table rebuild required. Please do \"ALTER TABLE `%-.32T` FORCE\" or dump/reload to fix it!" WARN_OPTION_BELOW_LIMIT eng "The value of '%s' should be no less than the value of '%s'" @@ -7166,8 +7166,8 @@ ER_UNKNOWN_OPTION eng "Unknown option '%-.64s'" hindi "अज्ञात विकल्प '%-.64s'" ER_BAD_OPTION_VALUE - eng "Incorrect value '%-.64s' for option '%-.64s'" - hindi "गलत मान '%-.64s' विकल्प '%-.64s' के लिए" + eng "Incorrect value '%-.64T' for option '%-.64s'" + hindi "गलत मान '%-.64T' विकल्प '%-.64s' के लिए" ER_UNUSED_6 eng "You should never see it" ER_UNUSED_7 @@ -7324,8 +7324,8 @@ ER_ROLE_DROP_EXISTS ER_CANNOT_CONVERT_CHARACTER eng "Cannot convert '%s' character 0x%-.64s to '%s'" ER_INVALID_DEFAULT_VALUE_FOR_FIELD 22007 - eng "Incorrect default value '%-.128s' for column '%.192s'" - hindi "गलत डिफ़ॉल्ट मान '%-.128s' कॉलम '%.192s' के लिए" + eng "Incorrect default value '%-.128T' for column '%.192s'" + hindi "गलत डिफ़ॉल्ट मान '%-.128T' कॉलम '%.192s' के लिए" ER_KILL_QUERY_DENIED_ERROR eng "You are not owner of query %lu" ger "Sie sind nicht Eigentümer von Abfrage %lu" diff --git a/strings/my_vsnprintf.c b/strings/my_vsnprintf.c index fa2cd494dc0..f9c0ad0a52c 100644 --- a/strings/my_vsnprintf.c +++ b/strings/my_vsnprintf.c @@ -224,7 +224,8 @@ err: */ static char *process_str_arg(CHARSET_INFO *cs, char *to, const char *end, - size_t width, char *par, uint print_type) + size_t width, char *par, uint print_type, + my_bool nice_cut) { int well_formed_error; uint dots= 0; @@ -232,24 +233,34 @@ static char *process_str_arg(CHARSET_INFO *cs, char *to, const char *end, if (!par) par = (char*) "(null)"; - plen= slen= strnlen(par, width + 1); - if (plen > width) - plen= width; - if (left_len <= plen) - plen = left_len - 1; - if ((slen > plen)) + if (nice_cut) { - if (plen < 3) + plen= slen= strnlen(par, width + 1); + if (plen > width) + plen= width; + if (left_len <= plen) + plen = left_len - 1; + if ((slen > plen)) { - dots= (uint) plen; - plen= 0; - } - else - { - dots= 3; - plen-= 3; + if (plen < 3) + { + dots= (uint) plen; + plen= 0; + } + else + { + dots= 3; + plen-= 3; + } } } + else + { + plen= slen= strnlen(par, width); + dots= 0; + if (left_len <= plen) + plen = left_len - 1; + } plen= my_well_formed_length(cs, par, par + plen, width, &well_formed_error); if (print_type & ESCAPED_ARG) @@ -446,6 +457,7 @@ start: switch (args_arr[i].arg_type) { case 's': case 'b': + case 'T': args_arr[i].str_arg= va_arg(ap, char *); break; case 'f': @@ -480,12 +492,14 @@ start: size_t width= 0, length= 0; switch (print_arr[i].arg_type) { case 's': + case 'T': { char *par= args_arr[print_arr[i].arg_idx].str_arg; width= (print_arr[i].flags & WIDTH_ARG) ? (size_t)args_arr[print_arr[i].width].longlong_arg : print_arr[i].width; - to= process_str_arg(cs, to, end, width, par, print_arr[i].flags); + to= process_str_arg(cs, to, end, width, par, print_arr[i].flags, + (print_arr[i].arg_type == 'T')); break; } case 'b': @@ -552,7 +566,7 @@ start: *to++= '"'; my_strerror(errmsg_buff, sizeof(errmsg_buff), (int) larg); to= process_str_arg(cs, to, real_end, width, errmsg_buff, - print_arr[i].flags); + print_arr[i].flags, 1); if (real_end > to) *to++= '"'; } break; @@ -676,10 +690,10 @@ size_t my_vsnprintf_ex(CHARSET_INFO *cs, char *to, size_t n, fmt= check_longlong(fmt, &have_longlong); - if (*fmt == 's') /* String parameter */ + if (*fmt == 's' || *fmt == 'T') /* String parameter */ { reg2 char *par= va_arg(ap, char *); - to= process_str_arg(cs, to, end, width, par, print_type); + to= process_str_arg(cs, to, end, width, par, print_type, (*fmt == 'T')); continue; } else if (*fmt == 'b') /* Buffer parameter */ @@ -731,7 +745,8 @@ size_t my_vsnprintf_ex(CHARSET_INFO *cs, char *to, size_t n, *to++= ' '; *to++= '"'; my_strerror(errmsg_buff, sizeof(errmsg_buff), (int) larg); - to= process_str_arg(cs, to, real_end, width, errmsg_buff, print_type); + to= process_str_arg(cs, to, real_end, width, errmsg_buff, + print_type, 1); if (real_end > to) *to++= '"'; } continue; diff --git a/unittest/mysys/my_vsnprintf-t.c b/unittest/mysys/my_vsnprintf-t.c index 872e88ddd7e..1a0b4080a2d 100644 --- a/unittest/mysys/my_vsnprintf-t.c +++ b/unittest/mysys/my_vsnprintf-t.c @@ -61,7 +61,7 @@ static void test_many(const char **res, const char *fmt, ...) int main(void) { - plan(43); + plan(47); test1("Constant string", "Constant string"); @@ -99,27 +99,33 @@ int main(void) test1("Width is ignored for strings ", "Width is ignored for strings <%04s> <%5s>", "x", "y"); - test1("Precision works for strings ", + test1("Precision works for strings ", "Precision works for strings <%.5s>", "abcdef!"); + test1("Precision works for strings ", + "Precision works for strings <%.5T>", "abcdef!"); + + test1("Flag '`' (backtick) works: `abcd` `op``q` (mysql extension)", + "Flag '`' (backtick) works: %`s %`.4s (mysql extension)", + "abcd", "op`qrst"); test1("Flag '`' (backtick) works: `abcd` `op``q...` (mysql extension)", - "Flag '`' (backtick) works: %`s %`.7s (mysql extension)", + "Flag '`' (backtick) works: %`T %`.7T (mysql extension)", "abcd", "op`qrstuuuuuuuuu"); test1("Flag '`' (backtick) works: `abcd` `.` (mysql extension)", - "Flag '`' (backtick) works: %`s %`.1s (mysql extension)", + "Flag '`' (backtick) works: %`T %`.1T (mysql extension)", "abcd", "op`qrstuuuuuuuuu"); test1("Flag '`' (backtick) works: `abcd` `...` (mysql extension)", - "Flag '`' (backtick) works: %`s %`.3s (mysql extension)", + "Flag '`' (backtick) works: %`T %`.3T (mysql extension)", "abcd", "op`qrstuuuuuuuuu"); test1("Flag '`' (backtick) works: `abcd` `op...` (mysql extension)", - "Flag '`' (backtick) works: %`s %`.5s (mysql extension)", + "Flag '`' (backtick) works: %`T %`.5T (mysql extension)", "abcd", "op`qrstuuuuuuuuu"); test1("Flag '`' (backtick) works: `abcd` `op``...` (mysql extension)", - "Flag '`' (backtick) works: %`s %`.6s (mysql extension)", + "Flag '`' (backtick) works: %`T %`.6T (mysql extension)", "abcd", "op`qrstuuuuuuuuu"); test1("Length modifiers work: 1 * -1 * 2 * 3", @@ -141,15 +147,21 @@ int main(void) test1("Asterisk '*' as a width works: < 4>", "Asterisk '*' as a width works: <%*d>", 5, 4); - test1("Asterisk '*' as a precision works: ", + test1("Asterisk '*' as a precision works: ", "Asterisk '*' as a precision works: <%.*s>", 6, "qwertyuiop"); + test1("Asterisk '*' as a precision works: ", + "Asterisk '*' as a precision works: <%.*T>", 6, "qwertyuiop"); + test1("Positional arguments for a width: < 4>", "Positional arguments for a width: <%1$*2$d>", 4, 5); - test1("Positional arguments for a precision: ", + test1("Positional arguments for a precision: ", "Positional arguments for a precision: <%1$.*2$s>", "qwertyuiop", 6); + test1("Positional arguments for a precision: ", + "Positional arguments for a precision: <%1$.*2$T>", "qwertyuiop", 6); + test1("Positional arguments and a width: <0000ab>", "Positional arguments and a width: <%1$06x>", 0xab); From 4a6b28c7b9c5e2f381f5376178785785c7263146 Mon Sep 17 00:00:00 2001 From: Varun Gupta Date: Tue, 5 May 2020 20:44:43 +0530 Subject: [PATCH 6/8] MDEV-22461: JOIN::make_aggr_tables_info(): Assertion `select_options & (1ULL << 17)' failed. A temporary table is needed for window function computation but if only a NAMED WINDOW SPEC is used and there is no window function, then there is no need to create a temporary table as there is no stage to compute WINDOW FUNCTION --- mysql-test/r/win.result | 19 +++++++++++++++++++ mysql-test/t/win.test | 20 ++++++++++++++++++++ sql/sql_select.cc | 13 +++++++++---- sql/sql_select.h | 4 +++- 4 files changed, 51 insertions(+), 5 deletions(-) diff --git a/mysql-test/r/win.result b/mysql-test/r/win.result index 9e551dd6dd6..35371f49a50 100644 --- a/mysql-test/r/win.result +++ b/mysql-test/r/win.result @@ -3724,5 +3724,24 @@ MAX(1) OVER () COUNT(a) abs(a) 1 0 NULL drop table t1; # +# MDEV-22461: JOIN::make_aggr_tables_info(): Assertion `select_options & (1ULL << 17)' failed. +# +CREATE TEMPORARY TABLE t0 (a INT PRIMARY KEY ) ; +INSERT INTO t0 VALUES (1),(2),(3); +SELECT a FROM t0 +WHERE a < 8 +GROUP BY 1.5 +WINDOW v2 AS ( PARTITION BY a ORDER BY a DESC ); +a +1 +SELECT a, ROW_NUMBER() OVER v2 +FROM t0 +WHERE a < 8 +GROUP BY 1.5 +WINDOW v2 AS ( PARTITION BY a ORDER BY a DESC ); +a ROW_NUMBER() OVER v2 +1 1 +drop table t0; +# # End of 10.2 tests # diff --git a/mysql-test/t/win.test b/mysql-test/t/win.test index d2c0c1c9712..e3025a400d7 100644 --- a/mysql-test/t/win.test +++ b/mysql-test/t/win.test @@ -2426,6 +2426,26 @@ SELECT MAX(1) OVER (), COUNT(a), abs(a) FROM t1 WHERE FALSE; drop table t1; +--echo # +--echo # MDEV-22461: JOIN::make_aggr_tables_info(): Assertion `select_options & (1ULL << 17)' failed. +--echo # + +CREATE TEMPORARY TABLE t0 (a INT PRIMARY KEY ) ; +INSERT INTO t0 VALUES (1),(2),(3); + +SELECT a FROM t0 +WHERE a < 8 +GROUP BY 1.5 +WINDOW v2 AS ( PARTITION BY a ORDER BY a DESC ); + +SELECT a, ROW_NUMBER() OVER v2 +FROM t0 +WHERE a < 8 +GROUP BY 1.5 +WINDOW v2 AS ( PARTITION BY a ORDER BY a DESC ); + +drop table t0; + --echo # --echo # End of 10.2 tests --echo # diff --git a/sql/sql_select.cc b/sql/sql_select.cc index c601946cfa0..b558445540d 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2014,11 +2014,16 @@ JOIN::optimize_inner() } need_tmp= test_if_need_tmp_table(); - //TODO this could probably go in test_if_need_tmp_table. - if (this->select_lex->window_specs.elements > 0) { - need_tmp= TRUE; + + /* + If window functions are present then we can't have simple_order set to + TRUE as the window function needs a temp table for computation. + ORDER BY is computed after the window function computation is done, so + the sort will be done on the temp table. + */ + if (select_lex->have_window_funcs()) simple_order= FALSE; - } + /* If the hint FORCE INDEX FOR ORDER BY/GROUP BY is used for the table diff --git a/sql/sql_select.h b/sql/sql_select.h index 0e011c9267a..92da1355be0 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -1636,6 +1636,7 @@ public: - We are using an ORDER BY or GROUP BY on fields not in the first table - We are using different ORDER BY and GROUP BY orders - The user wants us to buffer the result. + - We are using WINDOW functions. When the WITH ROLLUP modifier is present, we cannot skip temporary table creation for the DISTINCT clause just because there are only const tables. */ @@ -1645,7 +1646,8 @@ public: ((select_distinct || !simple_order || !simple_group) || (group_list && order) || MY_TEST(select_options & OPTION_BUFFER_RESULT))) || - (rollup.state != ROLLUP::STATE_NONE && select_distinct)); + (rollup.state != ROLLUP::STATE_NONE && select_distinct) || + select_lex->have_window_funcs()); } bool choose_subquery_plan(table_map join_tables); void get_partial_cost_and_fanout(int end_tab_idx, From e2c749380bc27a7af741c7eb607b4bccdf9429f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 25 May 2020 17:46:52 +0300 Subject: [PATCH 7/8] MDEV-22545 post-fix: Fix a test result commit cf52dd174ecf0e6055f4443be9c1fea8bc37b65e failed to adjust the result of the test main.mysqldump. --- mysql-test/r/mysqldump.result | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/r/mysqldump.result b/mysql-test/r/mysqldump.result index bbd7163552e..e527dbffe43 100644 --- a/mysql-test/r/mysqldump.result +++ b/mysql-test/r/mysqldump.result @@ -3756,7 +3756,7 @@ DROP TABLE t1; # CREATE TABLE t1(a int); INSERT INTO t1 VALUES (1), (2); -mysqldump: Input filename too long: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa... +mysqldump: Input filename too long: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa DROP TABLE t1; CREATE TABLE t2 (a INT) ENGINE=MyISAM; CREATE TABLE t3 (a INT) ENGINE=MyISAM; From 5530a93f47324b847c799d00a2756729a2869d13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 25 May 2020 18:57:14 +0300 Subject: [PATCH 8/8] MDEV-17092 use-after-poison around lock_trx_handle_wait_low There was a race condition where the connection of the victim of a KILL statement is disconnected while the KILL statement is executing. As a side effect of this fix, we will make XA PREPARE transactions immune to KILL statements. Starting with MariaDB 10.2, we have a pool of trx_t objects. trx_free() would only free memory to the pool. We poison the contents of freed objects in the pool in order to catch misuse. trx_free(): Unpoison also trx->mysql_thd and trx->state. This is to counter the poisoning of *trx in trx_pools->mem_free(). Unpoison only on AddressSanitizer or Valgrind, but not on MemorySanitizer. Pool: Unpoison allocated objects only on AddressSanitizer or Valgrind, but not on MemorySanitizer. innobase_kill_query(): Properly protect trx, acquiring also trx_sys_t::mutex and checking trx_t::mysql_thd and trx_t::state. --- storage/innobase/handler/ha_innodb.cc | 42 +++++++++++++++++++++++---- storage/innobase/include/ut0pool.h | 10 +++++++ storage/innobase/trx/trx0trx.cc | 16 ++++++++-- 3 files changed, 60 insertions(+), 8 deletions(-) diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index ac196f58416..8638ed66538 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -5201,13 +5201,43 @@ static void innobase_kill_query(handlerton*, THD* thd, enum thd_kill_levels) } #endif /* WITH_WSREP */ - if (trx_t* trx = thd_to_trx(thd)) { - ut_ad(trx->mysql_thd == thd); - /* Cancel a pending lock request if there are any */ - lock_trx_handle_wait(trx); - } + if (trx_t* trx= thd_to_trx(thd)) + { + lock_mutex_enter(); + trx_sys_mutex_enter(); + trx_mutex_enter(trx); + /* It is possible that innobase_close_connection() is concurrently + being executed on our victim. In that case, trx->mysql_thd would + be reset before invoking trx_free(). Even if the trx object is later + reused for another client connection or a background transaction, + its trx->mysql_thd will differ from our thd. - DBUG_VOID_RETURN; + If trx never performed any changes, nothing is really protecting + the trx_free() call or the changes of trx_t::state when the + transaction is being rolled back and trx_commit_low() is being + executed. + + The function trx_allocate_for_mysql() acquires + trx_sys_t::mutex, but trx_allocate_for_background() will not. + Luckily, background transactions cannot be read-only, because + for read-only transactions, trx_start_low() will avoid acquiring + any of the trx_sys_t::mutex, lock_sys_t::mutex, trx_t::mutex before + assigning trx_t::state. + + At this point, trx may have been reallocated for another client + connection, or for a background operation. In that case, either + trx_t::state or trx_t::mysql_thd should not match our expectations. */ + bool cancel= trx->mysql_thd == thd && trx->state == TRX_STATE_ACTIVE && + !trx->lock.was_chosen_as_deadlock_victim; + trx_sys_mutex_exit(); + if (!cancel); + else if (lock_t *lock= trx->lock.wait_lock) + lock_cancel_waiting_and_release(lock); + lock_mutex_exit(); + trx_mutex_exit(trx); + } + + DBUG_VOID_RETURN; } diff --git a/storage/innobase/include/ut0pool.h b/storage/innobase/include/ut0pool.h index f7bc4c5ebdf..a7efc4e56a2 100644 --- a/storage/innobase/include/ut0pool.h +++ b/storage/innobase/include/ut0pool.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 2013, 2014, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2018, 2020, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -86,11 +87,15 @@ struct Pool { for (Element* elem = m_start; elem != m_last; ++elem) { ut_ad(elem->m_pool == this); +#ifdef __SANITIZE_ADDRESS__ /* Unpoison the memory for AddressSanitizer */ MEM_UNDEFINED(&elem->m_type, sizeof elem->m_type); +#endif +#ifdef HAVE_valgrind /* Declare the contents as initialized for Valgrind; we checked this in mem_free(). */ UNIV_MEM_VALID(&elem->m_type, sizeof elem->m_type); +#endif Factory::destroy(&elem->m_type); } @@ -127,13 +132,18 @@ struct Pool { #if defined HAVE_valgrind || defined __SANITIZE_ADDRESS__ if (elem) { +# ifdef __SANITIZE_ADDRESS__ /* Unpoison the memory for AddressSanitizer */ MEM_UNDEFINED(&elem->m_type, sizeof elem->m_type); +# endif +# ifdef HAVE_valgrind + /* Declare the memory initialized for Valgrind. The trx_t that are released to the pool are actually initialized; we checked that by UNIV_MEM_ASSERT_RW() in mem_free() below. */ UNIV_MEM_VALID(&elem->m_type, sizeof elem->m_type); +# endif } #endif diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc index 85667e91d13..5768f0fc890 100644 --- a/storage/innobase/trx/trx0trx.cc +++ b/storage/innobase/trx/trx0trx.cc @@ -408,14 +408,26 @@ trx_free(trx_t*& trx) ut_ad(trx->will_lock == 0); trx_pools->mem_free(trx); +#ifdef __SANITIZE_ADDRESS__ /* Unpoison the memory for innodb_monitor_set_option; it is operating also on the freed transaction objects. */ MEM_UNDEFINED(&trx->mutex, sizeof trx->mutex); MEM_UNDEFINED(&trx->undo_mutex, sizeof trx->undo_mutex); - /* Declare the contents as initialized for Valgrind; - we checked that it was initialized in trx_pools->mem_free(trx). */ + /* For innobase_kill_connection() */ + MEM_UNDEFINED(&trx->state, sizeof trx->state); + MEM_UNDEFINED(&trx->mysql_thd, sizeof trx->mysql_thd); +#endif +#ifdef HAVE_valgrind + /* Unpoison the memory for innodb_monitor_set_option; + it is operating also on the freed transaction objects. + We checked that these were initialized in + trx_pools->mem_free(trx). */ UNIV_MEM_VALID(&trx->mutex, sizeof trx->mutex); UNIV_MEM_VALID(&trx->undo_mutex, sizeof trx->undo_mutex); + /* For innobase_kill_connection() */ + UNIV_MEM_VALID(&trx->state, sizeof trx->state); + UNIV_MEM_VALID(&trx->mysql_thd, sizeof trx->mysql_thd); +#endif trx = NULL; }