From de13fccfc6b16915190cd740f7fe1e3445dff0e7 Mon Sep 17 00:00:00 2001 From: Daniel Bartholomew Date: Mon, 11 May 2020 12:50:03 -0400 Subject: [PATCH 01/59] bump the VERSION --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 5048bc29198..bccc9d350b1 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ MYSQL_VERSION_MAJOR=5 MYSQL_VERSION_MINOR=5 -MYSQL_VERSION_PATCH=68 +MYSQL_VERSION_PATCH=69 MYSQL_VERSION_EXTRA= From dc22acfdb62745017226a5c698c1bc3ee3e3563e Mon Sep 17 00:00:00 2001 From: Daniele Sciascia Date: Thu, 21 May 2020 08:34:03 +0200 Subject: [PATCH 02/59] MDEV-22616 CHECK TABLE fails with wsrep_trx_fragment_size > 0 (#1551) Executing CHECK TABLE with streaming replication enabled reports error "Streaming replication not supported with binlog_format=STATEMENT". Administrative commands such as CHECK TABLE, are not replicated and temporarily set binlog format to statement. To avoid the problem, report the error only for active transactions for which streaming replication is enabled. --- mysql-test/suite/galera_sr/r/MDEV-22616.result | 13 +++++++++++++ mysql-test/suite/galera_sr/t/MDEV-22616.test | 18 ++++++++++++++++++ sql/sql_class.cc | 4 +++- 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 mysql-test/suite/galera_sr/r/MDEV-22616.result create mode 100644 mysql-test/suite/galera_sr/t/MDEV-22616.test diff --git a/mysql-test/suite/galera_sr/r/MDEV-22616.result b/mysql-test/suite/galera_sr/r/MDEV-22616.result new file mode 100644 index 00000000000..0c3847bd9b9 --- /dev/null +++ b/mysql-test/suite/galera_sr/r/MDEV-22616.result @@ -0,0 +1,13 @@ +connection node_2; +connection node_1; +CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB; +SET SESSION wsrep_trx_fragment_size = 1; +CHECK TABLE t1; +Table Op Msg_type Msg_text +test.t1 check status OK +START TRANSACTION; +INSERT INTO t1 VALUES (1); +CHECK TABLE t1; +Table Op Msg_type Msg_text +test.t1 check status OK +DROP TABLE t1; diff --git a/mysql-test/suite/galera_sr/t/MDEV-22616.test b/mysql-test/suite/galera_sr/t/MDEV-22616.test new file mode 100644 index 00000000000..818d787703f --- /dev/null +++ b/mysql-test/suite/galera_sr/t/MDEV-22616.test @@ -0,0 +1,18 @@ +# +# MDEV-22616 +# +# CHECK TABLE fails with wsrep_trx_fragment_size > 0 +# + +--source include/galera_cluster.inc + +CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB; +SET SESSION wsrep_trx_fragment_size = 1; +CHECK TABLE t1; + +START TRANSACTION; +INSERT INTO t1 VALUES (1); + +CHECK TABLE t1; + +DROP TABLE t1; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 8f3bfde6da8..fbe73072863 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -5758,7 +5758,9 @@ int THD::decide_logging_format(TABLE_LIST *tables) binlog by filtering rules. */ #ifdef WITH_WSREP - if (WSREP_CLIENT_NNULL(this) && wsrep_thd_is_local(this) && + if (WSREP_CLIENT_NNULL(this) && + wsrep_thd_is_local(this) && + wsrep_is_active(this) && variables.wsrep_trx_fragment_size > 0) { if (!is_current_stmt_binlog_format_row()) From cb9c49a9b20dd8d1eee39641176134e207a4d84f Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Thu, 21 May 2020 18:16:27 +0400 Subject: [PATCH 03/59] 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 04/59] 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 736ca14323fa16e409378f0da8005bce4be6dcf8 Mon Sep 17 00:00:00 2001 From: Monty Date: Fri, 22 May 2020 15:42:11 +0300 Subject: [PATCH 05/59] Don't crash if creating sequence under XA MDEV-22002 Assertion `!is_set() || (m_status == DA_OK_BULK && is_bulk_op())' failed upon CREATE TEMPORARY SEQUENCE under XA --- mysql-test/main/xa.result | 13 +++++++++++++ mysql-test/main/xa.test | 26 ++++++++++++++++++++++++++ sql/sql_sequence.cc | 6 ++++-- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/mysql-test/main/xa.result b/mysql-test/main/xa.result index f77c0afdec5..f37a3c36531 100644 --- a/mysql-test/main/xa.result +++ b/mysql-test/main/xa.result @@ -345,3 +345,16 @@ connection default; XA END 'xid1'; XA ROLLBACK 'xid1'; DROP TABLE t1, t2, t3; +XA BEGIN 'xid'; +CREATE TEMPORARY SEQUENCE s; +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the ACTIVE state +XA END 'xid'; +XA ROLLBACK 'xid'; +XA BEGIN 'xid'; +CREATE SEQUENCE s; +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the ACTIVE state +XA END 'xid'; +XA ROLLBACK 'xid'; +# +# End of 10.3 tests +# diff --git a/mysql-test/main/xa.test b/mysql-test/main/xa.test index 176ef6aa760..ce8f3834b03 100644 --- a/mysql-test/main/xa.test +++ b/mysql-test/main/xa.test @@ -478,3 +478,29 @@ DROP TABLE t1, t2, t3; --source include/wait_until_count_sessions.inc +# +# MDEV-22002 Assertion `!is_set() || (m_status == DA_OK_BULK && is_bulk_op())' +# failed upon CREATE TEMPORARY SEQUENCE under XA +# + +XA BEGIN 'xid'; + +--error ER_XAER_RMFAIL +CREATE TEMPORARY SEQUENCE s; + +XA END 'xid'; + +XA ROLLBACK 'xid'; + +XA BEGIN 'xid'; + +--error ER_XAER_RMFAIL +CREATE SEQUENCE s; + +XA END 'xid'; + +XA ROLLBACK 'xid'; + +--echo # +--echo # End of 10.3 tests +--echo # diff --git a/sql/sql_sequence.cc b/sql/sql_sequence.cc index b69b52505f4..96c1cd19433 100644 --- a/sql/sql_sequence.cc +++ b/sql/sql_sequence.cc @@ -355,8 +355,10 @@ bool sequence_insert(THD *thd, LEX *lex, TABLE_LIST *org_table_list) seq->reserved_until= seq->start; error= seq->write_initial_sequence(table); - trans_commit_stmt(thd); - trans_commit_implicit(thd); + if (trans_commit_stmt(thd)) + error= 1; + if (trans_commit_implicit(thd)) + error= 1; if (!temporary_table) { From ea7830eef48333e28f98a9b91f05a95735b465a3 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Fri, 22 May 2020 16:31:16 +0400 Subject: [PATCH 06/59] MDEV-14221 Assertion `0' failed in Item::field_type_for_temporal_comparison Item_null_result did not override type_handler() because of a wrong merge of d8a9b524f2e4efec675725c4c32acefe1d8dcb15 (MDEV-14221) from 10.1. Overriding type_handler(). Removing the old style field_type() method. It's not relevant any more. --- mysql-test/main/type_datetime_hires.result | 11 +++++++++++ mysql-test/main/type_datetime_hires.test | 10 ++++++++++ sql/item.h | 6 ++++-- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/mysql-test/main/type_datetime_hires.result b/mysql-test/main/type_datetime_hires.result index ebb9c6032b9..5211515ea77 100644 --- a/mysql-test/main/type_datetime_hires.result +++ b/mysql-test/main/type_datetime_hires.result @@ -889,5 +889,16 @@ Warning 1411 Incorrect date value: '0000-02-28' for function round(datetime) Warning 1411 Incorrect date value: '0000-12-31' for function round(datetime) DROP TABLE t1; # +# MDEV-20984 Possibly wrong result or Assertion `args[0]->type_handler()->mysql_timestamp_type() == MYSQL_TIMESTAMP_DATETIME' failed in Item_func_round::date_op +# +CREATE TABLE t1 (a DATETIME); +INSERT INTO t1 VALUES ('1979-01-03 10:33:32'),('2012-12-12 12:12:12'); +SELECT ROUND(a) AS f FROM t1 GROUP BY a WITH ROLLUP; +f +1979-01-03 10:33:32 +2012-12-12 12:12:12 +NULL +DROP TABLE t1; +# # End of 10.4 tests # diff --git a/mysql-test/main/type_datetime_hires.test b/mysql-test/main/type_datetime_hires.test index ed9a85bcda3..7b3b37560a3 100644 --- a/mysql-test/main/type_datetime_hires.test +++ b/mysql-test/main/type_datetime_hires.test @@ -146,6 +146,16 @@ SELECT a, ROUND(a,-6) FROM t1; DROP TABLE t1; +--echo # +--echo # MDEV-20984 Possibly wrong result or Assertion `args[0]->type_handler()->mysql_timestamp_type() == MYSQL_TIMESTAMP_DATETIME' failed in Item_func_round::date_op +--echo # + +CREATE TABLE t1 (a DATETIME); +INSERT INTO t1 VALUES ('1979-01-03 10:33:32'),('2012-12-12 12:12:12'); +SELECT ROUND(a) AS f FROM t1 GROUP BY a WITH ROLLUP; +DROP TABLE t1; + + --echo # --echo # End of 10.4 tests --echo # diff --git a/sql/item.h b/sql/item.h index 278ecc8f7e7..98b61e15c11 100644 --- a/sql/item.h +++ b/sql/item.h @@ -3654,9 +3654,11 @@ public: Field *result_field; Item_null_result(THD *thd): Item_null(thd), result_field(0) {} bool is_result_field() { return result_field != 0; } - enum_field_types field_type() const + const Type_handler *type_handler() const { - return result_field->type(); + if (result_field) + return result_field->type_handler(); + return &type_handler_null; } Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src, const Tmp_field_param *param) From be647ff14d6196af825f05020acee8f18af4773d Mon Sep 17 00:00:00 2001 From: Monty Date: Fri, 22 May 2020 18:02:24 +0300 Subject: [PATCH 07/59] 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 08/59] 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 09/59] 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 10/59] 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 11/59] 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 12/59] 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; } From 1af6e92f0b106c0588f89c51b749c573262e82d1 Mon Sep 17 00:00:00 2001 From: sjaakola Date: Mon, 25 May 2020 14:23:42 +0300 Subject: [PATCH 13/59] MDEV-22666 galera.MW-328A hang The hang can happen between a lock connection issuing KILL CONNECTION for a victim, which is in committing phase. There happens two resource deadlockwhere killer is holding victim's LOCK_thd_data and requires trx mutex for the victim. The victim, otoh, holds his own trx mutex, but requires LOCK_thd_data in wsrep_commit_ordered(). Hence a classic two thread deadlock happens. The fix in this commit changes innodb commit so that wsrep_commit_ordered() is not called while holding trx mutex. With this, wsrep patch commit time mutex locking does not violate the locking protocol of KILL command (i.e. LOCK_thd_data -> trx mutex) Also, a new test case has been added in galera.galera_bf_kill.test for scenario where a client connection is killed in committting phase. --- .../suite/galera/r/galera_bf_kill.result | 17 +++++++ mysql-test/suite/galera/t/galera_bf_kill.test | 44 +++++++++++++++++++ sql/service_wsrep.cc | 1 + storage/innobase/trx/trx0trx.cc | 11 +++-- 4 files changed, 67 insertions(+), 6 deletions(-) diff --git a/mysql-test/suite/galera/r/galera_bf_kill.result b/mysql-test/suite/galera/r/galera_bf_kill.result index 8b620323e35..2a7bc9eac29 100644 --- a/mysql-test/suite/galera/r/galera_bf_kill.result +++ b/mysql-test/suite/galera/r/galera_bf_kill.result @@ -70,3 +70,20 @@ a b 2 1 disconnect node_2a; drop table t1; +connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2; +connection node_2a; +CREATE TABLE t1 (i int primary key); +SET DEBUG_SYNC = "before_wsrep_ordered_commit SIGNAL bwoc_reached WAIT_FOR bwoc_continue"; +INSERT INTO t1 VALUES (1); +connection node_2; +SET DEBUG_SYNC = "now WAIT_FOR bwoc_reached"; +SET DEBUG_SYNC = "now SIGNAL bwoc_continue"; +SET DEBUG_SYNC='RESET'; +connection node_2a; +connection node_2; +select * from t1; +i +1 +disconnect node_2a; +connection node_2; +drop table t1; diff --git a/mysql-test/suite/galera/t/galera_bf_kill.test b/mysql-test/suite/galera/t/galera_bf_kill.test index 0748b732ead..ce8d27c281b 100644 --- a/mysql-test/suite/galera/t/galera_bf_kill.test +++ b/mysql-test/suite/galera/t/galera_bf_kill.test @@ -140,4 +140,48 @@ select * from t1; drop table t1; +# +# Test case 7: +# run a transaction in node 2, and set a sync point to pause the transaction +# in commit phase. +# Through another connection to node 2, kill the committing transaction by +# KILL QUERY command +# + +--connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2 +--connection node_2a +--let $connection_id = `SELECT CONNECTION_ID()` + +CREATE TABLE t1 (i int primary key); + +# Set up sync point +SET DEBUG_SYNC = "before_wsrep_ordered_commit SIGNAL bwoc_reached WAIT_FOR bwoc_continue"; + +# Send insert which will block in the sync point above +--send INSERT INTO t1 VALUES (1) + +--connection node_2 +SET DEBUG_SYNC = "now WAIT_FOR bwoc_reached"; + +--disable_query_log +--disable_result_log +# victim has passed the point of no return, kill is not possible anymore +--eval KILL QUERY $connection_id +--enable_result_log +--enable_query_log + +SET DEBUG_SYNC = "now SIGNAL bwoc_continue"; +SET DEBUG_SYNC='RESET'; +--connection node_2a +--error 0,1213 +--reap + +--connection node_2 +# victim was able to complete the INSERT +select * from t1; + +--disconnect node_2a + +--connection node_2 +drop table t1; diff --git a/sql/service_wsrep.cc b/sql/service_wsrep.cc index 7cac2bf741b..ada0bde803f 100644 --- a/sql/service_wsrep.cc +++ b/sql/service_wsrep.cc @@ -299,6 +299,7 @@ extern "C" void wsrep_commit_ordered(THD *thd) thd->wsrep_trx().state() == wsrep::transaction::s_committing && !wsrep_commit_will_write_binlog(thd)) { + DEBUG_SYNC(thd, "before_wsrep_ordered_commit"); thd->wsrep_cs().ordered_commit(); } } diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc index 276a78d00bf..f926e661be4 100644 --- a/storage/innobase/trx/trx0trx.cc +++ b/storage/innobase/trx/trx0trx.cc @@ -1493,12 +1493,6 @@ inline void trx_t::commit_in_memory(const mtr_t *mtr) if (fts_trx) trx_finalize_for_fts(this, undo_no != 0); - trx_mutex_enter(this); - dict_operation= TRX_DICT_OP_NONE; - - DBUG_LOG("trx", "Commit in memory: " << this); - state= TRX_STATE_NOT_STARTED; - #ifdef WITH_WSREP /* Serialization history has been written and the transaction is committed in memory, which makes this commit ordered. Release commit @@ -1510,6 +1504,11 @@ inline void trx_t::commit_in_memory(const mtr_t *mtr) } lock.was_chosen_as_wsrep_victim= false; #endif /* WITH_WSREP */ + trx_mutex_enter(this); + dict_operation= TRX_DICT_OP_NONE; + + DBUG_LOG("trx", "Commit in memory: " << this); + state= TRX_STATE_NOT_STARTED; assert_freed(); trx_init(this); From 76f4ae8295f63e4f3f7b8b72c2120d5da59b302c Mon Sep 17 00:00:00 2001 From: Varun Gupta Date: Tue, 26 May 2020 01:50:46 +0530 Subject: [PATCH 14/59] MDEV-21495: Conditional jump or move depends on uninitialised value in sel_arg_range_seq_next Initialize the parameter PARAM::max_key_part when we iterate over the ranges to get estimates from EITS. --- mysql-test/r/selectivity.result | 15 +++++++++++++++ mysql-test/r/selectivity_innodb.result | 15 +++++++++++++++ mysql-test/t/selectivity.test | 12 ++++++++++++ sql/opt_range_mrr.cc | 1 + 4 files changed, 43 insertions(+) diff --git a/mysql-test/r/selectivity.result b/mysql-test/r/selectivity.result index dc7e9494946..9bb197ad056 100644 --- a/mysql-test/r/selectivity.result +++ b/mysql-test/r/selectivity.result @@ -1866,4 +1866,19 @@ id select_type table type possible_keys key key_len ref rows Extra set optimizer_switch= @save_optimizer_switch; set optimizer_use_condition_selectivity= @save_optimizer_use_condition_selectivity; drop table t1,t2; +# +# MDEV-21495: Conditional jump or move depends on uninitialised value in sel_arg_range_seq_next +# +CREATE TABLE t1(a INT, b INT); +INSERT INTO t1 SELECT seq, seq from seq_1_to_100; +set optimizer_use_condition_selectivity=4; +ANALYZE TABLE t1 PERSISTENT FOR ALL; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +SELECT * from t1 WHERE a = 5 and b = 5; +a b +5 5 +set optimizer_use_condition_selectivity= @save_optimizer_use_condition_selectivity; +drop table t1; # End of 10.1 tests diff --git a/mysql-test/r/selectivity_innodb.result b/mysql-test/r/selectivity_innodb.result index f0cbf1662aa..d0e31374784 100644 --- a/mysql-test/r/selectivity_innodb.result +++ b/mysql-test/r/selectivity_innodb.result @@ -1876,6 +1876,21 @@ id select_type table type possible_keys key key_len ref rows Extra set optimizer_switch= @save_optimizer_switch; set optimizer_use_condition_selectivity= @save_optimizer_use_condition_selectivity; drop table t1,t2; +# +# MDEV-21495: Conditional jump or move depends on uninitialised value in sel_arg_range_seq_next +# +CREATE TABLE t1(a INT, b INT); +INSERT INTO t1 SELECT seq, seq from seq_1_to_100; +set optimizer_use_condition_selectivity=4; +ANALYZE TABLE t1 PERSISTENT FOR ALL; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +SELECT * from t1 WHERE a = 5 and b = 5; +a b +5 5 +set optimizer_use_condition_selectivity= @save_optimizer_use_condition_selectivity; +drop table t1; # End of 10.1 tests set optimizer_switch=@save_optimizer_switch_for_selectivity_test; set @tmp_ust= @@use_stat_tables; diff --git a/mysql-test/t/selectivity.test b/mysql-test/t/selectivity.test index 7df326edb44..1d0256b1b7e 100644 --- a/mysql-test/t/selectivity.test +++ b/mysql-test/t/selectivity.test @@ -1266,5 +1266,17 @@ set optimizer_switch= @save_optimizer_switch; set optimizer_use_condition_selectivity= @save_optimizer_use_condition_selectivity; drop table t1,t2; +--echo # +--echo # MDEV-21495: Conditional jump or move depends on uninitialised value in sel_arg_range_seq_next +--echo # + +CREATE TABLE t1(a INT, b INT); +INSERT INTO t1 SELECT seq, seq from seq_1_to_100; +set optimizer_use_condition_selectivity=4; +ANALYZE TABLE t1 PERSISTENT FOR ALL; +SELECT * from t1 WHERE a = 5 and b = 5; +set optimizer_use_condition_selectivity= @save_optimizer_use_condition_selectivity; +drop table t1; + --echo # End of 10.1 tests diff --git a/sql/opt_range_mrr.cc b/sql/opt_range_mrr.cc index 5d62ceda7d4..b1894f4b6c4 100644 --- a/sql/opt_range_mrr.cc +++ b/sql/opt_range_mrr.cc @@ -73,6 +73,7 @@ range_seq_t sel_arg_range_seq_init(void *init_param, uint n_ranges, uint flags) { SEL_ARG_RANGE_SEQ *seq= (SEL_ARG_RANGE_SEQ*)init_param; seq->at_start= TRUE; + seq->param->max_key_part= 0; seq->stack[0].key_tree= NULL; seq->stack[0].min_key= seq->param->min_key; seq->stack[0].min_key_flag= 0; From 7476e8c7cdd73d60294126a2840baee97e7644b6 Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Mon, 25 May 2020 21:42:26 +0530 Subject: [PATCH 15/59] MDEV-22637 Rollback of insert fails when column reorder happens - During column reorder table rebuild, rollback of insert fails. Reason is that InnoDB tries to fetch the column position from new clustered index and it exceeds default column value tuple fields. So InnoDB should use the table column position while searching for defaults column value. --- mysql-test/suite/innodb/r/alter_crash.result | 38 ++++++++++++++++++++ mysql-test/suite/innodb/t/alter_crash.test | 27 ++++++++++++++ storage/innobase/row/row0log.cc | 13 ++++--- 3 files changed, 73 insertions(+), 5 deletions(-) diff --git a/mysql-test/suite/innodb/r/alter_crash.result b/mysql-test/suite/innodb/r/alter_crash.result index cb9c17e45ea..70c2102ee0d 100644 --- a/mysql-test/suite/innodb/r/alter_crash.result +++ b/mysql-test/suite/innodb/r/alter_crash.result @@ -148,3 +148,41 @@ SELECT * FROM t1; a b d 1 NULL NULL DROP TABLE t1; +# +# MDEV-22637 Rollback of insert fails when column reorder happens +# +SET @@SQL_MODE = REPLACE(@@SQL_MODE, 'STRICT_TRANS_TABLES', ''); +SET @@SQL_MODE = REPLACE(@@SQL_MODE, 'STRICT_ALL_TABLES', ''); +CREATE TABLE t1(f1 INT NOT NULL, f2 CHAR(100), +f3 CHAR(100), f4 CHAR(100))ENGINE=InnoDB; +INSERT INTO t1 VALUES(1, "This is column2", "This is column3", +"This is column4"); +set DEBUG_SYNC = 'row_log_table_apply1_before SIGNAL scanned WAIT_FOR insert_done'; +ALTER TABLE t1 ADD COLUMN f6 int after f3, add primary key(f6, f4(3), f3(3)); +connect con1,localhost,root,,; +SET DEBUG_SYNC = 'now WAIT_FOR scanned'; +BEGIN; +INSERT INTO t1(f1, f2) VALUES(2, "This is column2 value"); +ROLLBACK; +set DEBUG_SYNC = 'now SIGNAL insert_done'; +connection default; +Warnings: +Warning 1265 Data truncated for column 'f3' at row 3 +Warning 1265 Data truncated for column 'f4' at row 3 +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `f1` int(11) NOT NULL, + `f2` char(100) DEFAULT NULL, + `f3` char(100) NOT NULL, + `f6` int(11) NOT NULL, + `f4` char(100) NOT NULL, + PRIMARY KEY (`f6`,`f4`(3),`f3`(3)) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +SELECT COUNT(*) FROM t1; +COUNT(*) +1 +disconnect con1; +DROP TABLE t1; +SET DEBUG_SYNC = 'RESET'; +SET SQL_MODE=DEFAULT; diff --git a/mysql-test/suite/innodb/t/alter_crash.test b/mysql-test/suite/innodb/t/alter_crash.test index c7cd1bcfe66..72116b0ca9d 100644 --- a/mysql-test/suite/innodb/t/alter_crash.test +++ b/mysql-test/suite/innodb/t/alter_crash.test @@ -196,3 +196,30 @@ SHOW CREATE TABLE t1; UPDATE t1 SET d=NULL; SELECT * FROM t1; DROP TABLE t1; + +--echo # +--echo # MDEV-22637 Rollback of insert fails when column reorder happens +--echo # +SET @@SQL_MODE = REPLACE(@@SQL_MODE, 'STRICT_TRANS_TABLES', ''); +SET @@SQL_MODE = REPLACE(@@SQL_MODE, 'STRICT_ALL_TABLES', ''); +CREATE TABLE t1(f1 INT NOT NULL, f2 CHAR(100), + f3 CHAR(100), f4 CHAR(100))ENGINE=InnoDB; +INSERT INTO t1 VALUES(1, "This is column2", "This is column3", + "This is column4"); +set DEBUG_SYNC = 'row_log_table_apply1_before SIGNAL scanned WAIT_FOR insert_done'; +--send ALTER TABLE t1 ADD COLUMN f6 int after f3, add primary key(f6, f4(3), f3(3)) +connect(con1,localhost,root,,); +SET DEBUG_SYNC = 'now WAIT_FOR scanned'; +BEGIN; +INSERT INTO t1(f1, f2) VALUES(2, "This is column2 value"); +ROLLBACK; +set DEBUG_SYNC = 'now SIGNAL insert_done'; + +connection default; +reap; +SHOW CREATE TABLE t1; +SELECT COUNT(*) FROM t1; +disconnect con1; +DROP TABLE t1; +SET DEBUG_SYNC = 'RESET'; +SET SQL_MODE=DEFAULT; diff --git a/storage/innobase/row/row0log.cc b/storage/innobase/row/row0log.cc index 30cce113d1d..5c64095f5ba 100644 --- a/storage/innobase/row/row0log.cc +++ b/storage/innobase/row/row0log.cc @@ -1173,14 +1173,15 @@ row_log_table_get_pk_col( return(DB_INVALID_NULL); } - ulint n_default_cols = i - DATA_N_SYS_COLS; + unsigned col_no= ifield->col->ind; + ut_ad(col_no < log->defaults->n_fields); field = static_cast( - log->defaults->fields[n_default_cols].data); + log->defaults->fields[col_no].data); if (!field) { return(DB_INVALID_NULL); } - len = log->defaults->fields[i - DATA_N_SYS_COLS].len; + len = log->defaults->fields[col_no].len; } if (rec_offs_nth_extern(offsets, i)) { @@ -1659,10 +1660,12 @@ blob_done: const dfield_t& default_field = log->defaults->fields[col_no]; - Field* field = log->old_table->field[col_no]; + + Field* field = log->old_table->field[col->ind]; field->set_warning(Sql_condition::WARN_LEVEL_WARN, - WARN_DATA_TRUNCATED, 1, ulong(log->n_rows)); + WARN_DATA_TRUNCATED, 1, + ulong(log->n_rows)); if (!log->allow_not_null) { /* We got a NULL value for a NOT NULL column. */ From fbcfbb0e1ce16db6c7debe77119de1141a8c6391 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 26 May 2020 11:43:43 +0300 Subject: [PATCH 16/59] MDEV-19751 Wrong partitioning by KEY() after primary key dropped Default (empty) field list in partitioning by KEY() clause is assigned from primary key. If primary key is changed the partitioning field list is changed as well, so repartitioning required. Not applicable to any non-primary keys as default field list may be taken only from primary key. --- mysql-test/r/partition_alter.result | 43 +++++++++++++++++++++++++++++ mysql-test/t/partition_alter.test | 39 ++++++++++++++++++++++++++ sql/sql_partition.cc | 22 +++++++++++++++ 3 files changed, 104 insertions(+) diff --git a/mysql-test/r/partition_alter.result b/mysql-test/r/partition_alter.result index 1dced95b17a..8de3d4b45ad 100644 --- a/mysql-test/r/partition_alter.result +++ b/mysql-test/r/partition_alter.result @@ -122,3 +122,46 @@ t1 CREATE TABLE `t1` ( PARTITION `p02` ENGINE = MyISAM, PARTITION `p03` ENGINE = MyISAM) drop table t1; +# +# MDEV-19751 Wrong partitioning by KEY() after key dropped +# +create or replace table t1 (pk int, x timestamp(6), primary key (pk, x)) engine innodb +partition by key() partitions 2; +insert into t1 (pk, x) values (1, '2000-01-01 00:00'), (2, '2000-01-01 00:01'); +# Inplace for DROP PRIMARY KEY when partitioned by default field list is denied +alter table t1 drop primary key, drop column x, add primary key (pk), algorithm=inplace; +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY +alter table t1 drop primary key, drop column x, add primary key (pk); +select * from t1 partition (p0); +pk +1 +drop table t1; +create or replace table t1 (pk int not null, x timestamp(6), unique u(pk, x)) engine innodb +partition by key() partitions 2; +insert into t1 (pk, x) values (1, '2000-01-01 00:00'), (2, '2000-01-01 00:01'); +# Same for NOT NULL UNIQUE KEY as this is actually primary key +alter table t1 drop key u, drop column x, add unique (pk), algorithm=inplace; +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY +alter table t1 drop key u, drop column x, add unique (pk); +select * from t1 partition (p0); +pk +1 +drop table t1; +create or replace table t1 (pk int, x timestamp(6), primary key (pk)) engine innodb +partition by key(pk) partitions 2; +insert into t1 (pk, x) values (1, '2000-01-01 00:00'), (2, '2000-01-01 00:01'); +# Inplace for DROP PRIMARY KEY when partitioned by explicit field list is allowed +alter table t1 drop primary key, add primary key (pk, x), algorithm=inplace; +select * from t1 partition (p0); +pk x +1 2000-01-01 00:00:00.000000 +drop table t1; +create or replace table t1 (k int, x timestamp(6), unique key u (x, k)) engine innodb +partition by key(k) partitions 2; +insert into t1 (k, x) values (1, '2000-01-01 00:00'), (2, '2000-01-01 00:01'); +# Inplace for DROP KEY is allowed +alter table t1 drop key u, algorithm=inplace; +select * from t1 partition (p0); +k x +1 2000-01-01 00:00:00.000000 +drop table t1; diff --git a/mysql-test/t/partition_alter.test b/mysql-test/t/partition_alter.test index 1235e5b8dc2..3c783ebe375 100644 --- a/mysql-test/t/partition_alter.test +++ b/mysql-test/t/partition_alter.test @@ -112,3 +112,42 @@ insert into t1 values(0, 1, 1, NULL, now(), now()); alter online table t1 delay_key_write=1; show create table t1; drop table t1; + +--echo # +--echo # MDEV-19751 Wrong partitioning by KEY() after key dropped +--echo # +create or replace table t1 (pk int, x timestamp(6), primary key (pk, x)) engine innodb +partition by key() partitions 2; +insert into t1 (pk, x) values (1, '2000-01-01 00:00'), (2, '2000-01-01 00:01'); +--echo # Inplace for DROP PRIMARY KEY when partitioned by default field list is denied +--error ER_ALTER_OPERATION_NOT_SUPPORTED +alter table t1 drop primary key, drop column x, add primary key (pk), algorithm=inplace; +alter table t1 drop primary key, drop column x, add primary key (pk); +select * from t1 partition (p0); +drop table t1; + +create or replace table t1 (pk int not null, x timestamp(6), unique u(pk, x)) engine innodb +partition by key() partitions 2; +insert into t1 (pk, x) values (1, '2000-01-01 00:00'), (2, '2000-01-01 00:01'); +--echo # Same for NOT NULL UNIQUE KEY as this is actually primary key +--error ER_ALTER_OPERATION_NOT_SUPPORTED +alter table t1 drop key u, drop column x, add unique (pk), algorithm=inplace; +alter table t1 drop key u, drop column x, add unique (pk); +select * from t1 partition (p0); +drop table t1; + +create or replace table t1 (pk int, x timestamp(6), primary key (pk)) engine innodb +partition by key(pk) partitions 2; +insert into t1 (pk, x) values (1, '2000-01-01 00:00'), (2, '2000-01-01 00:01'); +--echo # Inplace for DROP PRIMARY KEY when partitioned by explicit field list is allowed +alter table t1 drop primary key, add primary key (pk, x), algorithm=inplace; +select * from t1 partition (p0); +drop table t1; + +create or replace table t1 (k int, x timestamp(6), unique key u (x, k)) engine innodb +partition by key(k) partitions 2; +insert into t1 (k, x) values (1, '2000-01-01 00:00'), (2, '2000-01-01 00:01'); +--echo # Inplace for DROP KEY is allowed +alter table t1 drop key u, algorithm=inplace; +select * from t1 partition (p0); +drop table t1; diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 071c9c05129..2334286b039 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -5473,6 +5473,28 @@ the generated partition syntax in a correct manner. *partition_changed= TRUE; } } + /* + Prohibit inplace when partitioned by primary key and the primary key is dropped. + */ + if (!*partition_changed && + tab_part_info->part_field_array && + !tab_part_info->part_field_list.elements && + table->s->primary_key != MAX_KEY) + { + KEY *primary_key= table->key_info + table->s->primary_key; + List_iterator_fast drop_it(alter_info->drop_list); + const char *primary_name= primary_key->name; + const Alter_drop *drop; + drop_it.rewind(); + while ((drop= drop_it++)) + { + if (drop->type == Alter_drop::KEY && + 0 == my_strcasecmp(system_charset_info, primary_name, drop->name)) + break; + } + if (drop) + *partition_changed= TRUE; + } } if (thd->work_part_info) { From 9bbd685e8d325c5868a93083addc7a930c1c90de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 26 May 2020 12:23:20 +0300 Subject: [PATCH 17/59] MDEV-22513 main.processlist_notembedded Timeout in wait_until_count_sessions Do not blindly disconnect the connection that is in WAIT_FOR because it could happen that neither the disconnect nor the SIGNAL would be processed before RESET would discard the signal. --- mysql-test/r/processlist_notembedded.result | 1 + mysql-test/t/processlist_notembedded.test | 3 +++ 2 files changed, 4 insertions(+) diff --git a/mysql-test/r/processlist_notembedded.result b/mysql-test/r/processlist_notembedded.result index ab1ced79e25..ed8509ca13c 100644 --- a/mysql-test/r/processlist_notembedded.result +++ b/mysql-test/r/processlist_notembedded.result @@ -4,5 +4,6 @@ SET DEBUG_SYNC= 'before_join_optimize SIGNAL in_sync WAIT_FOR go'; SET DEBUG_SYNC= 'now WAIT_FOR in_sync'; SET DEBUG_SYNC= 'now SIGNAL go'; +user SET DEBUG_SYNC = 'RESET'; End of 5.5 tests diff --git a/mysql-test/t/processlist_notembedded.test b/mysql-test/t/processlist_notembedded.test index b07ac00a44a..90750b40e62 100644 --- a/mysql-test/t/processlist_notembedded.test +++ b/mysql-test/t/processlist_notembedded.test @@ -30,7 +30,10 @@ remove_file $MYSQLTEST_VARDIR/tmp//MDEV-20466.text; SET DEBUG_SYNC= 'now SIGNAL go'; +connection con1; +reap; disconnect con1; +connection default; SET DEBUG_SYNC = 'RESET'; From f1f14c209205b3a7dc375abf0cad19107221d499 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 26 May 2020 13:14:47 +0300 Subject: [PATCH 18/59] MDEV-20015 Assertion `!in_use->is_error()' failed in TABLE::update_virtual_field update_virtual_field() is called as part of index rebuild in ha_myisam::repair() (MDEV-5800) which is done on bulk INSERT finish. Assertion in update_virtual_field() was put as part of MDEV-16222 because update_virtual_field() returns in_use->is_error(). The idea: wrongly mixed semantics of error status before update_virtual_field() and the status returned by update_virtual_field(). The former can falsely influence the latter. --- mysql-test/suite/vcol/r/vcol_keys_myisam.result | 9 +++++++++ mysql-test/suite/vcol/t/vcol_keys_myisam.test | 11 +++++++++++ sql/table.cc | 8 +++++--- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/mysql-test/suite/vcol/r/vcol_keys_myisam.result b/mysql-test/suite/vcol/r/vcol_keys_myisam.result index b7086600ab1..dd6e4c414f0 100644 --- a/mysql-test/suite/vcol/r/vcol_keys_myisam.result +++ b/mysql-test/suite/vcol/r/vcol_keys_myisam.result @@ -408,3 +408,12 @@ SELECT * FROM t1 WHERE d2 < d1; i d1 d2 t 1 2023-03-16 2023-03-15 1 DROP TABLE t1; +# +# MDEV-20015 Assertion `!in_use->is_error()' failed in TABLE::update_virtual_field +# +create or replace table t1 (a int); +insert into t1 (a) values (1), (1); +create or replace table t2 (pk int, b int, c int as (b) virtual, primary key (pk), key(c)); +insert into t2 (pk) select a from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +drop tables t1, t2; diff --git a/mysql-test/suite/vcol/t/vcol_keys_myisam.test b/mysql-test/suite/vcol/t/vcol_keys_myisam.test index 99b1c9a444b..24612f4d55f 100644 --- a/mysql-test/suite/vcol/t/vcol_keys_myisam.test +++ b/mysql-test/suite/vcol/t/vcol_keys_myisam.test @@ -300,3 +300,14 @@ DROP TABLE t1; # Cleanup --let $datadir= `SELECT @@datadir` --remove_file $datadir/test/load_t1 + + +--echo # +--echo # MDEV-20015 Assertion `!in_use->is_error()' failed in TABLE::update_virtual_field +--echo # +create or replace table t1 (a int); +insert into t1 (a) values (1), (1); +create or replace table t2 (pk int, b int, c int as (b) virtual, primary key (pk), key(c)); +--error ER_DUP_ENTRY +insert into t2 (pk) select a from t1; +drop tables t1, t2; diff --git a/sql/table.cc b/sql/table.cc index 3bb321d3306..d2a45032dbb 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -7778,15 +7778,17 @@ int TABLE::update_virtual_fields(handler *h, enum_vcol_update_mode update_mode) int TABLE::update_virtual_field(Field *vf) { - DBUG_ASSERT(!in_use->is_error()); - Query_arena backup_arena; DBUG_ENTER("TABLE::update_virtual_field"); + Query_arena backup_arena; + Counting_error_handler count_errors; + in_use->push_internal_handler(&count_errors); in_use->set_n_backup_active_arena(expr_arena, &backup_arena); bitmap_clear_all(&tmp_set); vf->vcol_info->expr->walk(&Item::update_vcol_processor, 0, &tmp_set); vf->vcol_info->expr->save_in_field(vf, 0); in_use->restore_active_arena(expr_arena, &backup_arena); - DBUG_RETURN(in_use->is_error()); + in_use->pop_internal_handler(); + DBUG_RETURN(count_errors.errors); } From e04999c4600cbfc83ff238028b820cf300487b18 Mon Sep 17 00:00:00 2001 From: Julius Goryavsky Date: Tue, 26 May 2020 14:01:13 +0200 Subject: [PATCH 19/59] Forgotten include files were added to check the necessary conditions for running the test --- mysql-test/suite/galera/t/galera_bf_kill.test | 3 +++ mysql-test/suite/galera/t/galera_bf_lock_wait.test | 1 + 2 files changed, 4 insertions(+) diff --git a/mysql-test/suite/galera/t/galera_bf_kill.test b/mysql-test/suite/galera/t/galera_bf_kill.test index ce8d27c281b..3eb3ddc32b5 100644 --- a/mysql-test/suite/galera/t/galera_bf_kill.test +++ b/mysql-test/suite/galera/t/galera_bf_kill.test @@ -1,4 +1,7 @@ --source include/galera_cluster.inc +--source include/have_innodb.inc +--source include/have_debug.inc +--source include/have_debug_sync.inc # # Test case 1: Start a transaction on node_2a and kill it diff --git a/mysql-test/suite/galera/t/galera_bf_lock_wait.test b/mysql-test/suite/galera/t/galera_bf_lock_wait.test index a3903fd10c0..97d3b8e0710 100644 --- a/mysql-test/suite/galera/t/galera_bf_lock_wait.test +++ b/mysql-test/suite/galera/t/galera_bf_lock_wait.test @@ -1,4 +1,5 @@ --source include/galera_cluster.inc +--source include/have_innodb.inc --source include/big_test.inc --connection node_1 From 170487473e31da4c74a0f2eb915a6b06b19dcb02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 26 May 2020 15:46:09 +0300 Subject: [PATCH 20/59] After-merge fix: main.perror-win --- mysql-test/main/perror-win.result | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mysql-test/main/perror-win.result b/mysql-test/main/perror-win.result index dcd16d09537..03d43abdb97 100644 --- a/mysql-test/main/perror-win.result +++ b/mysql-test/main/perror-win.result @@ -1,7 +1,7 @@ MariaDB 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). -MariaDB error code 1062 (ER_DUP_ENTRY): Duplicate entry '%-.192T' for key %d -Win32 error code 1062: The service has not been started. +Win32 error code 23: Data error (cyclic redundancy check). +MariaDB error code 1062 (ER_DUP_ENTRY): Duplicate entry '%-.192s' for key %d +Win32 error code 1062: The service has not been started. Illegal error code: 30000 From 3ad1af93667cb265e7c509b14e014c72e6c6d453 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 26 May 2020 16:40:41 +0300 Subject: [PATCH 21/59] MDEV-22545: One more fix: main.perror-win In 10.3 and earlier, some lines in the result file were terminated by CR and LF, but in 10.4, all files are expected to be terminated by LF only. Also, my build of 10.4 apparently failed without me noticing it, and the MDEV-22545 fix was not present. The %T should not have been replaced with %s at all. --- mysql-test/main/perror-win.result | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/main/perror-win.result b/mysql-test/main/perror-win.result index 03d43abdb97..a0ea54f9187 100644 --- a/mysql-test/main/perror-win.result +++ b/mysql-test/main/perror-win.result @@ -2,6 +2,6 @@ MariaDB 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). -MariaDB error code 1062 (ER_DUP_ENTRY): Duplicate entry '%-.192s' for key %d +MariaDB 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 From fdb37d084b04be35c080de78ac01823bfaaf02a4 Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Tue, 26 May 2020 20:25:41 +0530 Subject: [PATCH 22/59] MDEV-21787 Alter table failure tries to access uninitialized column - Problem is that failure of inplace DDL tries to access the uninitialized column. This is caused by MDEV-19606 (commit 0274ab1de391920f98f844f6c71bf5b66db189ee). Fix is that InnoDB should use column while freeing the index when index is completely initialized. --- .../suite/innodb/r/instant_alter_limit,32k.rdiff | 13 ++++++++++--- .../suite/innodb/r/instant_alter_limit,4k.rdiff | 15 ++++++++++++--- .../suite/innodb/r/instant_alter_limit,64k.rdiff | 13 ++++++++++--- .../suite/innodb/r/instant_alter_limit,8k.rdiff | 13 ++++++++++--- .../suite/innodb/t/instant_alter_limit.test | 14 ++++++++++++++ storage/innobase/include/dict0mem.h | 2 +- 6 files changed, 57 insertions(+), 13 deletions(-) diff --git a/mysql-test/suite/innodb/r/instant_alter_limit,32k.rdiff b/mysql-test/suite/innodb/r/instant_alter_limit,32k.rdiff index 5e46c66ce73..8f8cf64b7fc 100644 --- a/mysql-test/suite/innodb/r/instant_alter_limit,32k.rdiff +++ b/mysql-test/suite/innodb/r/instant_alter_limit,32k.rdiff @@ -1,9 +1,16 @@ ---- instant_alter_limit.result -+++ instant_alter_limit.result -@@ -42,5 +42,5 @@ +--- instant_alter_limit.result 2020-05-26 18:01:27.377946439 +0530 ++++ instant_alter_limit,32k.reject 2020-05-26 19:59:19.743877366 +0530 +@@ -43,5 +43,12 @@ FROM information_schema.global_status WHERE variable_name = 'innodb_instant_alter_column'; instants -502 +506 DROP TABLE t; ++# ++# MDEV-21787 Alter table failure tries to access uninitialized column ++# ++CREATE TABLE t1(f1 INT PRIMARY KEY, f2 TEXT GENERATED ALWAYS AS (SUBSTR(f4, 1, 400)), f3 VARCHAR(500), f4 TEXT)ENGINE=InnoDB ROW_FORMAT=Compact; ++ALTER TABLE t1 ADD UNIQUE KEY (f2(9)); ++ALTER TABLE t1 ADD COLUMN f5 TEXT, ALGORITHM=INPLACE; ++DROP TABLE t1; diff --git a/mysql-test/suite/innodb/r/instant_alter_limit,4k.rdiff b/mysql-test/suite/innodb/r/instant_alter_limit,4k.rdiff index b40c6482d22..dad28218a02 100644 --- a/mysql-test/suite/innodb/r/instant_alter_limit,4k.rdiff +++ b/mysql-test/suite/innodb/r/instant_alter_limit,4k.rdiff @@ -1,5 +1,5 @@ ---- instant_alter_limit.result -+++ instant_alter_limit.result +--- instant_alter_limit.result 2020-05-26 18:01:27.377946439 +0530 ++++ instant_alter_limit,4k.reject 2020-05-26 20:17:53.314736548 +0530 @@ -5,6 +5,276 @@ ENGINE=InnoDB; INSERT INTO t VALUES(1,2,3,4,5); @@ -295,10 +295,19 @@ ALTER TABLE t ADD COLUMN b INT NOT NULL, ALGORITHM=INSTANT; ERROR 0A000: ALGORITHM=INSTANT is not supported for this operation. Try ALGORITHM=INPLACE SELECT variable_value-@old_instant instants -@@ -43,5 +319,5 @@ +@@ -43,5 +319,14 @@ FROM information_schema.global_status WHERE variable_name = 'innodb_instant_alter_column'; instants -502 +474 DROP TABLE t; ++# ++# MDEV-21787 Alter table failure tries to access uninitialized column ++# ++CREATE TABLE t1(f1 INT PRIMARY KEY, f2 TEXT GENERATED ALWAYS AS (SUBSTR(f4, 1, 400)), f3 VARCHAR(500), f4 TEXT)ENGINE=InnoDB ROW_FORMAT=Compact; ++ALTER TABLE t1 ADD UNIQUE KEY (f2(9)); ++ALTER TABLE t1 ADD COLUMN f5 TEXT, ALGORITHM=INPLACE; ++Warnings: ++Warning 139 Row size too large (> 1982). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSED may help. In current row format, BLOB prefix of 768 bytes is stored inline. ++DROP TABLE t1; diff --git a/mysql-test/suite/innodb/r/instant_alter_limit,64k.rdiff b/mysql-test/suite/innodb/r/instant_alter_limit,64k.rdiff index 5e46c66ce73..d7479dbba40 100644 --- a/mysql-test/suite/innodb/r/instant_alter_limit,64k.rdiff +++ b/mysql-test/suite/innodb/r/instant_alter_limit,64k.rdiff @@ -1,9 +1,16 @@ ---- instant_alter_limit.result -+++ instant_alter_limit.result -@@ -42,5 +42,5 @@ +--- instant_alter_limit.result 2020-05-26 18:01:27.377946439 +0530 ++++ instant_alter_limit,64k.reject 2020-05-26 20:00:22.499711222 +0530 +@@ -43,5 +43,12 @@ FROM information_schema.global_status WHERE variable_name = 'innodb_instant_alter_column'; instants -502 +506 DROP TABLE t; ++# ++# MDEV-21787 Alter table failure tries to access uninitialized column ++# ++CREATE TABLE t1(f1 INT PRIMARY KEY, f2 TEXT GENERATED ALWAYS AS (SUBSTR(f4, 1, 400)), f3 VARCHAR(500), f4 TEXT)ENGINE=InnoDB ROW_FORMAT=Compact; ++ALTER TABLE t1 ADD UNIQUE KEY (f2(9)); ++ALTER TABLE t1 ADD COLUMN f5 TEXT, ALGORITHM=INPLACE; ++DROP TABLE t1; diff --git a/mysql-test/suite/innodb/r/instant_alter_limit,8k.rdiff b/mysql-test/suite/innodb/r/instant_alter_limit,8k.rdiff index f7cb7238ae3..1fe3e1a56eb 100644 --- a/mysql-test/suite/innodb/r/instant_alter_limit,8k.rdiff +++ b/mysql-test/suite/innodb/r/instant_alter_limit,8k.rdiff @@ -1,5 +1,5 @@ ---- instant_alter_limit.result -+++ instant_alter_limit.result +--- instant_alter_limit.result 2020-05-26 18:01:27.377946439 +0530 ++++ instant_alter_limit,8k.reject 2020-05-26 20:19:50.881869095 +0530 @@ -5,6 +5,28 @@ ENGINE=InnoDB; INSERT INTO t VALUES(1,2,3,4,5); @@ -47,10 +47,17 @@ ALTER TABLE t ADD COLUMN b INT NOT NULL, ALGORITHM=INSTANT; ERROR 0A000: ALGORITHM=INSTANT is not supported for this operation. Try ALGORITHM=INPLACE SELECT variable_value-@old_instant instants -@@ -43,5 +71,5 @@ +@@ -43,5 +71,12 @@ FROM information_schema.global_status WHERE variable_name = 'innodb_instant_alter_column'; instants -502 +492 DROP TABLE t; ++# ++# MDEV-21787 Alter table failure tries to access uninitialized column ++# ++CREATE TABLE t1(f1 INT PRIMARY KEY, f2 TEXT GENERATED ALWAYS AS (SUBSTR(f4, 1, 400)), f3 VARCHAR(500), f4 TEXT)ENGINE=InnoDB ROW_FORMAT=Compact; ++ALTER TABLE t1 ADD UNIQUE KEY (f2(9)); ++ALTER TABLE t1 ADD COLUMN f5 TEXT, ALGORITHM=INPLACE; ++DROP TABLE t1; diff --git a/mysql-test/suite/innodb/t/instant_alter_limit.test b/mysql-test/suite/innodb/t/instant_alter_limit.test index e84885f88e7..b7fb3ee55c1 100644 --- a/mysql-test/suite/innodb/t/instant_alter_limit.test +++ b/mysql-test/suite/innodb/t/instant_alter_limit.test @@ -60,3 +60,17 @@ FROM information_schema.global_status WHERE variable_name = 'innodb_instant_alter_column'; DROP TABLE t; + +--echo # +--echo # MDEV-21787 Alter table failure tries to access uninitialized column +--echo # +CREATE TABLE t1(f1 INT PRIMARY KEY, f2 TEXT GENERATED ALWAYS AS (SUBSTR(f4, 1, 400)), f3 VARCHAR(500), f4 TEXT)ENGINE=InnoDB ROW_FORMAT=Compact; +ALTER TABLE t1 ADD UNIQUE KEY (f2(9)); +let $error_code = 0; +let $innodb_page_size = `SELECT @@INNODB_PAGE_SIZE`; +if ($innodb_page_size == 4k) { + let $error_code= ER_TOO_BIG_ROWSIZE; +} +--error $error_code +ALTER TABLE t1 ADD COLUMN f5 TEXT, ALGORITHM=INPLACE; +DROP TABLE t1; diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index 35f05f40e2c..259da23fcd9 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -1203,7 +1203,7 @@ public: @param whether to reset fields[].col */ void detach_columns(bool clear= false) { - if (!has_virtual()) + if (!has_virtual() || !cached) return; for (unsigned i= 0; i < n_fields; i++) { From dbe447a78908214614db53061dccbc6bde52764e Mon Sep 17 00:00:00 2001 From: Andrei Elkin Date: Tue, 5 May 2020 20:32:32 +0300 Subject: [PATCH 23/59] MDEV-15152 Optimistic parallel slave doesnt cope well with START SLAVE UNTIL The immediate bug was caused by a failure to recognize a correct position to stop the slave applier run in optimistic parallel mode. There were the following set of issues that the analysis unveil. 1 incorrect estimate for the event binlog position passed to is_until_satisfied 2 wait for workers to complete by the driver thread did not account non-group events that could be left unprocessed and thus to mix up the last executed binlog group's file and position: the file remained old and the position related to the new rotated file 3 incorrect 'slave reached file:pos' by the parallel slave report in the error log 4 relay log UNTIL missed out the parallel slave branch in is_until_satisfied. The patch addresses all of them to simplify logics of log change notification in either the master and relay-log until case. P.1 is addressed with passing the event into is_until_satisfied() for proper analisis by the function. P.2 is fixed by changes in handle_queued_pos_update(). P.4 required removing relay-log change notification by workers. Instead the driver thread updates the notion of the current relay-log fully itself with aid of introduced bool Relay_log_info::until_relay_log_names_defer. An extra print out of the requested until file:pos is arranged with --log-warning=3. --- .../r/rpl_parallel_optimistic_until.result | 238 +++++++++ .../rpl/t/rpl_parallel_optimistic_until.test | 466 ++++++++++++++++++ sql/rpl_parallel.cc | 6 +- sql/rpl_rli.cc | 65 ++- sql/rpl_rli.h | 19 +- sql/slave.cc | 31 +- 6 files changed, 806 insertions(+), 19 deletions(-) create mode 100644 mysql-test/suite/rpl/r/rpl_parallel_optimistic_until.result create mode 100644 mysql-test/suite/rpl/t/rpl_parallel_optimistic_until.test diff --git a/mysql-test/suite/rpl/r/rpl_parallel_optimistic_until.result b/mysql-test/suite/rpl/r/rpl_parallel_optimistic_until.result new file mode 100644 index 00000000000..d3d1a52d5db --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_parallel_optimistic_until.result @@ -0,0 +1,238 @@ +include/master-slave.inc +[connection master] +include/stop_slave.inc +RESET MASTER; +RESET SLAVE; +RESET MASTER; +CREATE TABLE t1 (a int primary key, b text) ENGINE=InnoDB; +INSERT INTO t1 SET a=25, b='trx0'; +include/start_slave.inc +include/stop_slave.inc +ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB; +SET @old_parallel_threads=@@GLOBAL.slave_parallel_threads; +SET GLOBAL slave_parallel_threads=2; +SET @old_parallel_mode=@@GLOBAL.slave_parallel_mode; +SET GLOBAL slave_parallel_mode='optimistic'; +SET @old_max_relay_log_size = @@global.max_relay_log_size; +SET @@global.max_relay_log_size=4096; +BEGIN; +INSERT INTO t1 SET a=1, b='trx1'; +INSERT INTO t1 SET a=2, b='trx1'; +INSERT INTO t1 SET a=3, b='trx1'; +INSERT INTO t1 SET a=4, b='trx1'; +INSERT INTO t1 SET a=5, b='trx1'; +INSERT INTO t1 SET a=6, b='trx1'; +INSERT INTO t1 SET a=7, b='trx1'; +INSERT INTO t1 SET a=8, b='trx1'; +INSERT INTO t1 SET a=9, b='trx1'; +INSERT INTO t1 SET a=10, b='trx1'; +INSERT INTO t1 SET a=11, b='trx1'; +INSERT INTO t1 SET a=12, b='trx1'; +INSERT INTO t1 SET a=13, b='trx1'; +INSERT INTO t1 SET a=14, b='trx1'; +INSERT INTO t1 SET a=15, b='trx1'; +INSERT INTO t1 SET a=16, b='trx1'; +INSERT INTO t1 SET a=17, b='trx1'; +INSERT INTO t1 SET a=18, b='trx1'; +INSERT INTO t1 SET a=19, b='trx1'; +INSERT INTO t1 SET a=20, b='trx1'; +INSERT INTO t1 SET a=21, b='trx1'; +INSERT INTO t1 SET a=22, b='trx1'; +INSERT INTO t1 SET a=23, b='trx1'; +INSERT INTO t1 SET a=24, b='trx1'; +COMMIT; +FLUSH LOGS; +BEGIN; +UPDATE t1 SET b='trx2_0' WHERE a = 25; +UPDATE t1 SET b='trx2' WHERE a = 25; +COMMIT; +INSERT INTO t1 SET a=26,b='trx3'; +*** case 1 UNTIL inside trx2 +BEGIN; +INSERT INTO t1 SET a= 1; +SELECT <= AND < as "pos_until < trx0 and is within trx2"; +pos_until < trx0 and is within trx2 +1 +CHANGE MASTER TO MASTER_USE_GTID=no; +START SLAVE UNTIL MASTER_LOG_FILE = 'file_2', MASTER_LOG_POS = ; +ROLLBACK; +Proof 1: Correct stop +include/wait_for_slave_sql_to_stop.inc +SELECT count(*) = 1 as 'trx2 is committed' FROM t1 WHERE b = 'trx2'; +trx2 is committed +1 +SELECT count(*) = 0 as 'trx3 is not committed' FROM t1 WHERE b = 'trx3'; +trx3 is not committed +1 +Proof 2: Resume works out +include/start_slave.inc +*** case 2 UNTIL inside trx2 +DELETE FROM t1 WHERE a <> 25; +UPDATE t1 SET b='trx0' WHERE a = 25; +BEGIN; +INSERT INTO t1 SET a= 1; +include/stop_slave.inc +SELECT <= AND < as "pos_until >= trx0 and is within trx2"; +pos_until >= trx0 and is within trx2 +1 +CHANGE MASTER TO MASTER_LOG_FILE = 'file_1', MASTER_LOG_POS = , MASTER_USE_GTID=no; +START SLAVE UNTIL MASTER_LOG_FILE = 'file_2', MASTER_LOG_POS = ; +ROLLBACK; +Proof 1: Correct stop +include/wait_for_slave_sql_to_stop.inc +SELECT count(*) = 1 as 'trx2 is committed' FROM t1 WHERE b = 'trx2'; +trx2 is committed +1 +SELECT count(*) = 0 as 'trx3 is not committed' FROM t1 WHERE b = 'trx3'; +trx3 is not committed +1 +Proof 2: Resume works out +include/start_slave.inc +*** case 3 UNTIL inside trx1 +DELETE FROM t1 WHERE a <> 25; +UPDATE t1 SET b='trx0' WHERE a = 25; +BEGIN; +INSERT INTO t1 SET a= 1; # block trx1; +include/stop_slave.inc +SELECT < as "pos_until before trx2 start position"; +pos_until before trx2 start position +1 +CHANGE MASTER TO MASTER_LOG_FILE = 'file_1', MASTER_LOG_POS = , MASTER_USE_GTID=no; +START SLAVE UNTIL MASTER_LOG_FILE = 'file_2', MASTER_LOG_POS = ; +ROLLBACK; +Proof 1: Correct stop +include/wait_for_slave_sql_to_stop.inc +SELECT count(*) = 25-1 as 'trx1 is committed' FROM t1 WHERE b = 'trx1'; +trx1 is committed +1 +SELECT count(*) = 0 as 'trx2 is not committed' FROM t1 WHERE b = 'trx2'; +trx2 is not committed +1 +Proof 2: Resume works out +include/start_slave.inc +*** case 4 Relay-log UNTIL inside trx1 +DELETE FROM t1 WHERE a <> 25; +UPDATE t1 SET b='trx0' WHERE a = 25; +BEGIN; +INSERT INTO t1 SET a= 1; # block trx1; +include/stop_slave.inc +CHANGE MASTER TO MASTER_LOG_FILE = 'file_1', MASTER_LOG_POS = , MASTER_USE_GTID=no; +START SLAVE IO_THREAD; +include/wait_for_slave_io_to_start.inc +START SLAVE UNTIL RELAY_LOG_FILE = 'file_2', RELAY_LOG_POS = ; +ROLLBACK; +Proof 1: Correct stop +include/wait_for_slave_sql_to_stop.inc +SELECT count(*) = 25-1 as 'trx1 is committed' FROM t1 WHERE b = 'trx1'; +trx1 is committed +1 +SELECT count(*) = 0 as 'trx2 is not committed' FROM t1 WHERE b = 'trx2'; +trx2 is not committed +1 +Proof 2: Resume works out +include/start_slave.inc +*** case 5 Relay-log UNTIL inside a "big" trx that spawns few relay logs +CREATE TABLE t2 (a TEXT) ENGINE=InnoDB; +FLUSH LOGS; +include/stop_slave.inc +BEGIN; +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +COMMIT; +INSERT INTO t2 SET a='a'; +START SLAVE IO_THREAD; +include/wait_for_slave_io_to_start.inc +START SLAVE UNTIL RELAY_LOG_FILE = 'file_2', RELAY_LOG_POS = ; +Proof 1: Correct stop +include/wait_for_slave_sql_to_stop.inc +Proof 2: Resume works out +include/start_slave.inc +include/diff_tables.inc [master:t2,slave:t2] +*** case 6 Relay-log UNTIL inside a small trx inside a sequence of relay logs +include/stop_slave.inc +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +COMMIT; +START SLAVE IO_THREAD; +include/wait_for_slave_io_to_start.inc +include/sync_slave_io_with_master.inc +START SLAVE UNTIL RELAY_LOG_FILE = 'file_2', RELAY_LOG_POS = ; +Proof 1: Correct stop +include/wait_for_slave_sql_to_stop.inc +Proof 2: Resume works out +include/start_slave.inc +include/diff_tables.inc [master:t2,slave:t2] +include/stop_slave.inc +SET GLOBAL max_relay_log_size=@old_max_relay_log_size; +SET GLOBAL slave_parallel_mode=@old_parallel_mode; +SET GLOBAL slave_parallel_threads=@old_parallel_threads; +include/start_slave.inc +DROP TABLE t1, t2; +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_parallel_optimistic_until.test b/mysql-test/suite/rpl/t/rpl_parallel_optimistic_until.test new file mode 100644 index 00000000000..9e42fae8464 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_parallel_optimistic_until.test @@ -0,0 +1,466 @@ +--source include/have_innodb.inc +--source include/have_debug.inc +--source include/have_debug_sync.inc +--source include/master-slave.inc +# Format is restricted because the test expects a specific result of +# relay-logging that splits a transaction into two different files. +--source include/have_binlog_format_row.inc + +# +# MDEV-15152 Optimistic parallel slave doesn't cope well with START SLAVE UNTIL +# +--connection slave +--source include/stop_slave.inc +RESET MASTER; +RESET SLAVE; + +--connection master +RESET MASTER; +CREATE TABLE t1 (a int primary key, b text) ENGINE=InnoDB; +--let $a0 = 25 +--eval INSERT INTO t1 SET a=$a0, b='trx0' +# Memorize the position for replication restart from it +--let $pos_trx0 = query_get_value(SHOW MASTER STATUS, Position, 1) + +--connection slave +--source include/start_slave.inc + +--connection master +# --connection slave +--sync_slave_with_master +--source include/stop_slave.inc +ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB; +SET @old_parallel_threads=@@GLOBAL.slave_parallel_threads; +SET GLOBAL slave_parallel_threads=2; +SET @old_parallel_mode=@@GLOBAL.slave_parallel_mode; +SET GLOBAL slave_parallel_mode='optimistic'; + +# Run the slave in the following modes one by one. +# +# 1. the until position is set in the middle of trx2 +# below $pos_trx0 of the last exec position in the first file +# 2. and above $pos_trx0 +# In either case trx2 must commit before slave stops. +# 3. the until postion is inside trx1 +# 4. RELAY log until inside trx1 +# 5. RELAY log until inside a "big" trx +# 6. RELAY log until inside a trx within a sequence of relay logs +# +# Execution flaw for Until_Master_Pos cases follows as: +# create the transaction trx1, trx2 +# logged at the beginning of two subsequent binlog files. +# Set the until position to at the middle of the 2rd transaction. +# Engage the optimistic scheduler while having trx1 execution blocked. +# Lift the block after trx2 has reached waiting its order to commit. +# *Proof 1* +# Observe that the slave applier stops at a correct position. +# In the bug condition it would stop prematurely having the stop position +# in the first file, therefore trx2 not committed. +# Specifically, an internal transaction position until makes the applier to run +# beyond it to commit commit the current transaction. +# *Proof 2* +# Observe the following START SLAVE resumes OK. +# +# Auxiliary third trx3 on master is just for triggering the actual stop +# (whihc is a legacy UNTIL's property). +# trx0 is to produce a specific value of the last executed binlog file:pos +# to emulate the bug condition. +# +# Intermediate checks via SELECT are supposed to succeed +# with putting out value 1. +# +# NOTE: Relay log until tests have to use explicit log names and position +# which may require to adjust with future changes to event formats etc. +# + +--connection slave +SET @old_max_relay_log_size = @@global.max_relay_log_size; +SET @@global.max_relay_log_size=4096; + +--connection master +# trx1 +--let $a=1 +BEGIN; +while (`SELECT $a < $a0`) +{ + --eval INSERT INTO t1 SET a=$a, b='trx1' +--inc $a +} +COMMIT; +--let $fil_1 = query_get_value(SHOW MASTER STATUS, File, 1) +--let $pos_trx1 = query_get_value(SHOW MASTER STATUS, Position, 1) + +FLUSH LOGS; + +# $pos_0 the offset of the first event of trx2 in new file +--let $pos_0=query_get_value(SHOW MASTER STATUS, Position, 1) +# trx2 +--let $a=$a0 +BEGIN; +--eval UPDATE t1 SET b='trx2_0' WHERE a = $a +--eval UPDATE t1 SET b='trx2' WHERE a = $a +COMMIT; +--let $fil_2=query_get_value(SHOW MASTER STATUS, File, 1) +--let $pos_trx2=query_get_value(SHOW MASTER STATUS, Position, 1) + +# trx3 +--let $a=$a0 +--inc $a +--eval INSERT INTO t1 SET a=$a,b='trx3' +--let $pos_trx3=query_get_value(SHOW MASTER STATUS, Position, 1) +--let $a= + + +--echo *** case 1 UNTIL inside trx2 + +--connection slave1 +# Blocker to hold off EXEC_MASTER_LOG_POS advance +BEGIN; + --eval INSERT INTO t1 SET a= 1 +--connection slave +--let $pos_until=`SELECT $pos_trx0 - 1` +--replace_result $pos_0 $pos_until $pos_trx2 +--eval SELECT $pos_0 <= $pos_until AND $pos_until < $pos_trx2 as "pos_until < trx0 and is within trx2" +CHANGE MASTER TO MASTER_USE_GTID=no; +--replace_result $fil_2 file_2 $pos_until +--eval START SLAVE UNTIL MASTER_LOG_FILE = '$fil_2', MASTER_LOG_POS = $pos_until + +--let $wait_condition= SELECT COUNT(*) > 0 FROM information_schema.processlist WHERE state = "Waiting for prior transaction to commit" +--source include/wait_condition.inc + +--connection slave1 +# unblock to see the slave applier stops at $until +ROLLBACK; + +--echo Proof 1: Correct stop +--connection slave +--source include/wait_for_slave_sql_to_stop.inc +--let $file_stop= query_get_value(SHOW SLAVE STATUS, Relay_Master_Log_File, 1) +--let $pos_stop= query_get_value(SHOW SLAVE STATUS, Exec_Master_Log_Pos, 1) +if (`SELECT "$file_stop" != "$fil_2" OR $pos_stop < $pos_until`) +{ + --echo *** ERROR: Slave stopped at $file_stop:$pos_stop which is not $fil_2:$pos_until. + --die +} +--eval SELECT count(*) = 1 as 'trx2 is committed' FROM t1 WHERE b = 'trx2' +--eval SELECT count(*) = 0 as 'trx3 is not committed' FROM t1 WHERE b = 'trx3' + +--echo Proof 2: Resume works out +--source include/start_slave.inc +--connection master +--sync_slave_with_master + + +--echo *** case 2 UNTIL inside trx2 + +--connection slave +--eval DELETE FROM t1 WHERE a <> $a0 +--eval UPDATE t1 SET b='trx0' WHERE a = $a0 + +--connection slave1 +# Blocker to hold off EXEC_MASTER_LOG_POS advance +BEGIN; + --eval INSERT INTO t1 SET a= 1 + +--connection slave +--source include/stop_slave.inc + +--let $pos_until=`SELECT $pos_trx2 - 1` +--replace_result $pos_trx0 $pos_until $pos_trx2 +--eval SELECT $pos_trx0 <= $pos_until AND $pos_until < $pos_trx2 as "pos_until >= trx0 and is within trx2" +--replace_result $fil_1 file_1 $pos_trx0 +--eval CHANGE MASTER TO MASTER_LOG_FILE = '$fil_1', MASTER_LOG_POS = $pos_trx0, MASTER_USE_GTID=no +--replace_result $fil_2 file_2 $pos_until +--eval START SLAVE UNTIL MASTER_LOG_FILE = '$fil_2', MASTER_LOG_POS = $pos_until + +--let $wait_condition= SELECT COUNT(*) > 0 FROM information_schema.processlist WHERE state = "Waiting for prior transaction to commit" +--source include/wait_condition.inc + +--connection slave1 +# unblock to see the slave applier stops at $until +ROLLBACK; + +--echo Proof 1: Correct stop +--connection slave +--source include/wait_for_slave_sql_to_stop.inc +--let $file_stop= query_get_value(SHOW SLAVE STATUS, Relay_Master_Log_File, 1) +--let $pos_stop= query_get_value(SHOW SLAVE STATUS, Exec_Master_Log_Pos, 1) +if (`SELECT "$file_stop" != "$fil_2" OR $pos_stop < $pos_until`) +{ + --echo *** ERROR: Slave stopped at $file_stop:$pos_stop which is not $fil_2:$pos_until. + --die +} +--eval SELECT count(*) = 1 as 'trx2 is committed' FROM t1 WHERE b = 'trx2' +--eval SELECT count(*) = 0 as 'trx3 is not committed' FROM t1 WHERE b = 'trx3' + +--echo Proof 2: Resume works out +--source include/start_slave.inc +--connection master +--sync_slave_with_master + + +--echo *** case 3 UNTIL inside trx1 + +--connection slave +--eval DELETE FROM t1 WHERE a <> $a0 +--eval UPDATE t1 SET b='trx0' WHERE a = $a0 + + +--connection slave1 +# Blocker to hold off EXEC_MASTER_LOG_POS advance +BEGIN; + --eval INSERT INTO t1 SET a= 1; # block trx1 + +--connection slave +--source include/stop_slave.inc + +--let $pos_until=`SELECT $pos_0 - 1` +--replace_result $pos_0 $pos_until $pos_trx2 +--eval SELECT $pos_until < $pos_0 as "pos_until before trx2 start position" +--replace_result $fil_1 file_1 $pos_trx0 +--eval CHANGE MASTER TO MASTER_LOG_FILE = '$fil_1', MASTER_LOG_POS = $pos_trx0, MASTER_USE_GTID=no +--replace_result $fil_2 file_2 $pos_until +--eval START SLAVE UNTIL MASTER_LOG_FILE = '$fil_2', MASTER_LOG_POS = $pos_until + +--connection slave1 +# unblock to see the slave applier stops at $until +ROLLBACK; + +--echo Proof 1: Correct stop +--connection slave +--source include/wait_for_slave_sql_to_stop.inc +--let $file_stop= query_get_value(SHOW SLAVE STATUS, Relay_Master_Log_File, 1) +--let $pos_stop= query_get_value(SHOW SLAVE STATUS, Exec_Master_Log_Pos, 1) +if (`SELECT "$file_stop" != "$fil_2" OR $pos_stop < $pos_until`) +{ + --echo *** ERROR: Slave stopped at $file_stop:$pos_stop which is not $fil_2:$pos_until. + --die +} +--eval SELECT count(*) = $a0-1 as 'trx1 is committed' FROM t1 WHERE b = 'trx1' +--eval SELECT count(*) = 0 as 'trx2 is not committed' FROM t1 WHERE b = 'trx2' + +--echo Proof 2: Resume works out +--source include/start_slave.inc +--connection master +--sync_slave_with_master + + +--echo *** case 4 Relay-log UNTIL inside trx1 + +--connection slave +--eval DELETE FROM t1 WHERE a <> $a0 +--eval UPDATE t1 SET b='trx0' WHERE a = $a0 + +--connection slave1 +# Blocker to hold off EXEC_MASTER_LOG_POS advance +BEGIN; + --eval INSERT INTO t1 SET a= 1; # block trx1 + +--connection slave +--source include/stop_slave.inc +--replace_result $fil_1 file_1 $pos_trx0 +--eval CHANGE MASTER TO MASTER_LOG_FILE = '$fil_1', MASTER_LOG_POS = $pos_trx0, MASTER_USE_GTID=no +START SLAVE IO_THREAD; +--source include/wait_for_slave_io_to_start.inc + +# The following test sets the stop coordinate is set to inside the first event +# of a relay log that holds events of a transaction started in an earlier log. +# Peek the stop position in the middle of trx1 (Xid event is here) +# whose last event follow it +--let $pos_until=2568 +--let $file_rl=slave-relay-bin.000002 +--let $binlog_file=$file_rl +--let $info= query_get_value(SHOW RELAYLOG EVENTS IN '$file_rl' FROM $pos_until LIMIT 1, Info, 1) + +if (`SELECT "$info" NOT LIKE "COMMIT /* xid=% */"`) +{ + --echo *** Unexpected offset. Refine it to point to the correct XID event! + --die +} + +# Complicate the test to screw the coordinate away from event boundary. +--dec $pos_until +--replace_result $file_rl file_2 $pos_until +--eval START SLAVE UNTIL RELAY_LOG_FILE = '$file_rl', RELAY_LOG_POS = $pos_until + +--connection slave1 +# unblock to see the slave applier stops at $until +ROLLBACK; + +--echo Proof 1: Correct stop +--connection slave +--source include/wait_for_slave_sql_to_stop.inc +--let $file_stop= query_get_value(SHOW SLAVE STATUS, Relay_Log_File, 1) +--let $pos_stop= query_get_value(SHOW SLAVE STATUS, Relay_Log_Pos, 1) +if (`SELECT strcmp("$file_rl","$file_stop") > -1`) +{ + --echo *** ERROR: Slave stopped at $file_stop:$pos_stop which is not $file_rl:$pos_until. + --die +} + +--eval SELECT count(*) = $a0-1 as 'trx1 is committed' FROM t1 WHERE b = 'trx1' +--eval SELECT count(*) = 0 as 'trx2 is not committed' FROM t1 WHERE b = 'trx2' + +--echo Proof 2: Resume works out +--source include/start_slave.inc +--connection master +--sync_slave_with_master + + + +--echo *** case 5 Relay-log UNTIL inside a "big" trx that spawns few relay logs + +--connection master +CREATE TABLE t2 (a TEXT) ENGINE=InnoDB; +FLUSH LOGS; + +--sync_slave_with_master +--let $file_stop= query_get_value(SHOW SLAVE STATUS, Relay_Log_File, 1) +--let $pos_stop= query_get_value(SHOW SLAVE STATUS, Relay_Log_Pos, 1) +--let $records=`SELECT floor(4*@@global.max_relay_log_size / 1024) + 1` + +--connection slave +--source include/stop_slave.inc + +--connection master +# trx4 +BEGIN; +--let $i=$records +while ($i) +{ + INSERT INTO t2 SET a=repeat('a',1024); + +--dec $i +} +COMMIT; + +# slave will stop there: +--let $file_trx4 = query_get_value(SHOW MASTER STATUS, File, 1) +--let $pos_trx4 = query_get_value(SHOW MASTER STATUS, Position, 1) + +# trx5 +INSERT INTO t2 SET a='a'; +--let $pos_trx5 = query_get_value(SHOW MASTER STATUS, Position, 1) + +--connection slave +START SLAVE IO_THREAD; +--source include/wait_for_slave_io_to_start.inc + +# Set position inside the transaction though the value +# specified is beyond that relay log file. +# The coordianate may point to in a different event in future changes +# but should not move away from inside this big group of events. +# So we don't test which event in the transaction it points to. +--let $pos_until= 4500 +--let $file_rl= slave-relay-bin.000010 + +--replace_result $file_rl file_2 $pos_until +--eval START SLAVE UNTIL RELAY_LOG_FILE = '$file_rl', RELAY_LOG_POS = $pos_until + +--echo Proof 1: Correct stop +--connection slave +--source include/wait_for_slave_sql_to_stop.inc +--let $file_stop= query_get_value(SHOW SLAVE STATUS, Relay_Master_Log_File, 1) +--let $pos_stop= query_get_value(SHOW SLAVE STATUS, Exec_Master_Log_Pos, 1) +# It's showed the actual stop occurred before trx5 +if (`SELECT strcmp("$file_trx4", "$file_stop") <> 0 OR $pos_stop >= $pos_trx5 OR count(*) <> $records FROM t2`) +{ + --echo *** ERROR: Slave stopped at *binlog* $file_stop:$pos_stop which is not $file_trx4:$pos_trx4. + --die +} + +--echo Proof 2: Resume works out +--source include/start_slave.inc +--connection master +--sync_slave_with_master + +--let $diff_tables=master:t2,slave:t2 +--source include/diff_tables.inc + + + +--echo *** case 6 Relay-log UNTIL inside a small trx inside a sequence of relay logs + +--connection slave +--source include/stop_slave.inc + +--connection master +# trx6 +--let $records=`SELECT count(*) FROM t2` +while ($records) +{ + BEGIN; + DELETE FROM t2 LIMIT 1; + COMMIT; +--dec $records +} +COMMIT; + +--connection slave +START SLAVE IO_THREAD; +--source include/wait_for_slave_io_to_start.inc + +--connection master +--source include/sync_slave_io_with_master.inc + +--connection slave +# The relay-log coordinate is not at an event boundary and +# also may change across the server version. +# The test makes its best to check its coherance. +--let $pos_until= 4100 +--let $file_rl= slave-relay-bin.000015 + +--let $pos_gtid = 4010 +--let $info= query_get_value(SHOW RELAYLOG EVENTS IN '$file_rl' FROM $pos_gtid LIMIT 1, Info, 1) + +if (`SELECT "$info" != "BEGIN GTID 0-1-23"`) +{ + --echo *** Unexpected offset. Refine it to point to the correct GTID! + --die +} +--let $pos_event = 4090 +--let $type= query_get_value(SHOW RELAYLOG EVENTS IN '$file_rl' FROM $pos_event LIMIT 1, Event_type, 1) +if (`SELECT "$type" != "Delete_rows_v1"`) +{ + --echo *** Unexpected offset. Refine it to point to the expected event! + --die +} + +--replace_result $file_rl file_2 $pos_until +--eval START SLAVE UNTIL RELAY_LOG_FILE = '$file_rl', RELAY_LOG_POS = $pos_until + +--echo Proof 1: Correct stop +--connection slave +--source include/wait_for_slave_sql_to_stop.inc +--let $file_stop= query_get_value(SHOW SLAVE STATUS, Relay_Log_File, 1) +--let $pos_stop= query_get_value(SHOW SLAVE STATUS, Relay_Log_Pos, 1) +if (`SELECT strcmp("$file_stop", "$file_rl") = -1 OR + strcmp("$file_stop", "$file_rl") = 0 AND $pos_stop < $pos_until`) +{ + --echo *** ERROR: Slave stopped at *relay* $file_stop:$pos_stop which is not $file_rl:$pos_until. + --die +} + +--echo Proof 2: Resume works out +--source include/start_slave.inc +--connection master +--sync_slave_with_master + +--let $diff_tables=master:t2,slave:t2 +--source include/diff_tables.inc + +# +# Clean up. +# +--connection slave +--source include/stop_slave.inc +SET GLOBAL max_relay_log_size=@old_max_relay_log_size; +SET GLOBAL slave_parallel_mode=@old_parallel_mode; +SET GLOBAL slave_parallel_threads=@old_parallel_threads; +--source include/start_slave.inc + +--connection master +DROP TABLE t1, t2; + +--sync_slave_with_master +--source include/rpl_end.inc diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc index 574730b96c1..fb6f23af295 100644 --- a/sql/rpl_parallel.cc +++ b/sql/rpl_parallel.cc @@ -75,18 +75,18 @@ handle_queued_pos_update(THD *thd, rpl_parallel_thread::queued_event *qev) /* Do not update position if an earlier event group caused an error abort. */ DBUG_ASSERT(qev->typ == rpl_parallel_thread::queued_event::QUEUED_POS_UPDATE); + rli= qev->rgi->rli; e= qev->entry_for_queued; - if (e->stop_on_error_sub_id < (uint64)ULONGLONG_MAX || e->force_abort) + if (e->stop_on_error_sub_id < (uint64)ULONGLONG_MAX || + (e->force_abort && !rli->stop_for_until)) return; - rli= qev->rgi->rli; mysql_mutex_lock(&rli->data_lock); cmp= strcmp(rli->group_relay_log_name, qev->event_relay_log_name); if (cmp < 0) { rli->group_relay_log_pos= qev->future_event_relay_log_pos; strmake_buf(rli->group_relay_log_name, qev->event_relay_log_name); - rli->notify_group_relay_log_name_update(); } else if (cmp == 0 && rli->group_relay_log_pos < qev->future_event_relay_log_pos) rli->group_relay_log_pos= qev->future_event_relay_log_pos; diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index f3514253874..905462ecfbd 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -65,6 +65,7 @@ Relay_log_info::Relay_log_info(bool is_slave_recovery) gtid_skip_flag(GTID_SKIP_NOT), inited(0), abort_slave(0), stop_for_until(0), slave_running(MYSQL_SLAVE_NOT_RUN), until_condition(UNTIL_NONE), until_log_pos(0), retried_trans(0), executed_entries(0), + until_relay_log_names_defer(false), m_flags(0) { DBUG_ENTER("Relay_log_info::Relay_log_info"); @@ -512,6 +513,8 @@ void Relay_log_info::clear_until_condition() until_condition= Relay_log_info::UNTIL_NONE; until_log_name[0]= 0; until_log_pos= 0; + until_relay_log_names_defer= false; + DBUG_VOID_RETURN; } @@ -997,7 +1000,6 @@ void Relay_log_info::inc_group_relay_log_pos(ulonglong log_pos, { group_relay_log_pos= rgi->future_event_relay_log_pos; strmake_buf(group_relay_log_name, rgi->event_relay_log_name); - notify_group_relay_log_name_update(); } else if (cmp == 0 && group_relay_log_pos < rgi->future_event_relay_log_pos) group_relay_log_pos= rgi->future_event_relay_log_pos; @@ -1258,29 +1260,78 @@ err: autoincrement or if we have transactions). Should be called ONLY if until_condition != UNTIL_NONE ! + + In the parallel execution mode and UNTIL_MASTER_POS the file name is + presented by future_event_master_log_name which may be ahead of + group_master_log_name. Log_event::log_pos does relate to it nevertheless + so the pair comprises a correct binlog coordinate. + Internal group events and events that have zero log_pos also + produce the zero for the local log_pos which may not lead to the + function falsely return true. + In UNTIL_RELAY_POS the original caching and notification are simplified + to straightforward files comparison when the current event can't be + a part of an event group. + RETURN VALUE true - condition met or error happened (condition seems to have bad log file name) false - condition not met */ -bool Relay_log_info::is_until_satisfied(my_off_t master_beg_pos) +bool Relay_log_info::is_until_satisfied(Log_event *ev) { const char *log_name; ulonglong log_pos; + /* Prevents stopping within transaction; needed solely for Relay UNTIL. */ + bool in_trans= false; + DBUG_ENTER("Relay_log_info::is_until_satisfied"); if (until_condition == UNTIL_MASTER_POS) { log_name= (mi->using_parallel() ? future_event_master_log_name : group_master_log_name); - log_pos= master_beg_pos; + log_pos= (get_flag(Relay_log_info::IN_TRANSACTION) || !ev || !ev->log_pos) ? + (mi->using_parallel() ? 0 : group_master_log_pos) : + ev->log_pos - ev->data_written; } else { DBUG_ASSERT(until_condition == UNTIL_RELAY_POS); - log_name= group_relay_log_name; - log_pos= group_relay_log_pos; + if (!mi->using_parallel()) + { + log_name= group_relay_log_name; + log_pos= group_relay_log_pos; + } + else + { + log_name= event_relay_log_name; + log_pos= event_relay_log_pos; + in_trans= get_flag(Relay_log_info::IN_TRANSACTION); + /* + until_log_names_cmp_result is set to UNKNOWN either + - by a non-group event *and* only when it is in the middle of a group + - or by a group event when the preceding group made the above + non-group event to defer the resetting. + */ + if ((ev && !Log_event::is_group_event(ev->get_type_code()))) + { + if (in_trans) + { + until_relay_log_names_defer= true; + } + else + { + until_log_names_cmp_result= UNTIL_LOG_NAMES_CMP_UNKNOWN; + until_relay_log_names_defer= false; + } + } + else if (!in_trans && until_relay_log_names_defer) + { + until_log_names_cmp_result= UNTIL_LOG_NAMES_CMP_UNKNOWN; + until_relay_log_names_defer= false; + } + } } DBUG_PRINT("info", ("group_master_log_name='%s', group_master_log_pos=%llu", @@ -1334,8 +1385,8 @@ bool Relay_log_info::is_until_satisfied(my_off_t master_beg_pos) } DBUG_RETURN(((until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_EQUAL && - log_pos >= until_log_pos) || - until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_GREATER)); + (log_pos >= until_log_pos && !in_trans)) || + until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_GREATER)); } diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h index 3dec5b0f044..29e47d302bc 100644 --- a/sql/rpl_rli.h +++ b/sql/rpl_rli.h @@ -402,7 +402,7 @@ public: void close_temporary_tables(); /* Check if UNTIL condition is satisfied. See slave.cc for more. */ - bool is_until_satisfied(my_off_t); + bool is_until_satisfied(Log_event *ev); inline ulonglong until_pos() { DBUG_ASSERT(until_condition == UNTIL_MASTER_POS || @@ -410,7 +410,13 @@ public: return ((until_condition == UNTIL_MASTER_POS) ? group_master_log_pos : group_relay_log_pos); } - + inline char *until_name() + { + DBUG_ASSERT(until_condition == UNTIL_MASTER_POS || + until_condition == UNTIL_RELAY_POS); + return ((until_condition == UNTIL_MASTER_POS) ? group_master_log_name : + group_relay_log_name); + } /** Helper function to do after statement completion. @@ -483,6 +489,15 @@ public: } private: + /* + Hint for when to stop event distribution by sql driver thread. + The flag is set ON by a non-group event when this event is in the middle + of a group (e.g a transaction group) so it's too early + to refresh the current-relay-log vs until-log cached comparison result. + And it is checked and to decide whether it's a right time to do so + when the being processed group has been fully scheduled. + */ + bool until_relay_log_names_defer; /* Holds the state of the data in the relay log. diff --git a/sql/slave.cc b/sql/slave.cc index 0ce9c4cbbcd..06f2b0d955a 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -3784,12 +3784,8 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli, rli->until_condition == Relay_log_info::UNTIL_RELAY_POS) && (ev->server_id != global_system_variables.server_id || rli->replicate_same_server_id) && - rli->is_until_satisfied((rli->get_flag(Relay_log_info::IN_TRANSACTION) || !ev->log_pos) - ? rli->group_master_log_pos - : ev->log_pos - ev->data_written)) + rli->is_until_satisfied(ev)) { - sql_print_information("Slave SQL thread stopped because it reached its" - " UNTIL position %llu", rli->until_pos()); /* Setting abort_slave flag because we do not want additional message about error in query execution to be printed. @@ -4943,10 +4939,14 @@ pthread_handler_t handle_slave_sql(void *arg) } if ((rli->until_condition == Relay_log_info::UNTIL_MASTER_POS || rli->until_condition == Relay_log_info::UNTIL_RELAY_POS) && - rli->is_until_satisfied(rli->group_master_log_pos)) + rli->is_until_satisfied(NULL)) { sql_print_information("Slave SQL thread stopped because it reached its" - " UNTIL position %llu", rli->until_pos()); + " UNTIL position %llu in %s %s file", + rli->until_pos(), rli->until_name(), + rli->until_condition == + Relay_log_info::UNTIL_MASTER_POS ? + "binlog" : "relaylog"); mysql_mutex_unlock(&rli->data_lock); goto err; } @@ -5011,7 +5011,24 @@ pthread_handler_t handle_slave_sql(void *arg) err: if (mi->using_parallel()) rli->parallel.wait_for_done(thd, rli); + /* Gtid_list_log_event::do_apply_event has already reported the GTID until */ + if (rli->stop_for_until && rli->until_condition != Relay_log_info::UNTIL_GTID) + { + if (global_system_variables.log_warnings > 2) + sql_print_information("Slave SQL thread UNTIL stop was requested at position " + "%llu in %s %s file", + rli->until_log_pos, rli->until_log_name, + rli->until_condition == + Relay_log_info::UNTIL_MASTER_POS ? + "binlog" : "relaylog"); + sql_print_information("Slave SQL thread stopped because it reached its" + " UNTIL position %llu in %s %s file", + rli->until_pos(), rli->until_name(), + rli->until_condition == + Relay_log_info::UNTIL_MASTER_POS ? + "binlog" : "relaylog"); + }; /* Thread stopped. Print the current replication position to the log */ { StringBuffer<100> tmp; From 0c1f97b3ab7befdae522aaa738be00f6700cee8e Mon Sep 17 00:00:00 2001 From: Andrei Elkin Date: Tue, 5 May 2020 20:32:32 +0300 Subject: [PATCH 24/59] MDEV-15152 Optimistic parallel slave doesnt cope well with START SLAVE UNTIL The immediate bug was caused by a failure to recognize a correct position to stop the slave applier run in optimistic parallel mode. There were the following set of issues that the analysis unveil. 1 incorrect estimate for the event binlog position passed to is_until_satisfied 2 wait for workers to complete by the driver thread did not account non-group events that could be left unprocessed and thus to mix up the last executed binlog group's file and position: the file remained old and the position related to the new rotated file 3 incorrect 'slave reached file:pos' by the parallel slave report in the error log 4 relay log UNTIL missed out the parallel slave branch in is_until_satisfied. The patch addresses all of them to simplify logics of log change notification in either the master and relay-log until case. P.1 is addressed with passing the event into is_until_satisfied() for proper analisis by the function. P.2 is fixed by changes in handle_queued_pos_update(). P.4 required removing relay-log change notification by workers. Instead the driver thread updates the notion of the current relay-log fully itself with aid of introduced bool Relay_log_info::until_relay_log_names_defer. An extra print out of the requested until file:pos is arranged with --log-warning=3. --- .../r/rpl_parallel_optimistic_until.result | 291 +++++++++++ .../rpl/t/rpl_parallel_optimistic_until.test | 465 ++++++++++++++++++ sql/rpl_parallel.cc | 6 +- sql/rpl_rli.cc | 65 ++- sql/rpl_rli.h | 21 +- sql/slave.cc | 31 +- 6 files changed, 859 insertions(+), 20 deletions(-) create mode 100644 mysql-test/suite/rpl/r/rpl_parallel_optimistic_until.result create mode 100644 mysql-test/suite/rpl/t/rpl_parallel_optimistic_until.test diff --git a/mysql-test/suite/rpl/r/rpl_parallel_optimistic_until.result b/mysql-test/suite/rpl/r/rpl_parallel_optimistic_until.result new file mode 100644 index 00000000000..a83a9b61b9f --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_parallel_optimistic_until.result @@ -0,0 +1,291 @@ +include/master-slave.inc +[connection master] +connection slave; +include/stop_slave.inc +RESET MASTER; +RESET SLAVE; +connection master; +RESET MASTER; +CREATE TABLE t1 (a int primary key, b text) ENGINE=InnoDB; +INSERT INTO t1 SET a=25, b='trx0'; +connection slave; +include/start_slave.inc +connection master; +connection slave; +include/stop_slave.inc +ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB; +SET @old_parallel_threads=@@GLOBAL.slave_parallel_threads; +SET GLOBAL slave_parallel_threads=2; +SET @old_parallel_mode=@@GLOBAL.slave_parallel_mode; +SET GLOBAL slave_parallel_mode='optimistic'; +connection slave; +SET @old_max_relay_log_size = @@global.max_relay_log_size; +SET @@global.max_relay_log_size=4096; +connection master; +BEGIN; +INSERT INTO t1 SET a=1, b='trx1'; +INSERT INTO t1 SET a=2, b='trx1'; +INSERT INTO t1 SET a=3, b='trx1'; +INSERT INTO t1 SET a=4, b='trx1'; +INSERT INTO t1 SET a=5, b='trx1'; +INSERT INTO t1 SET a=6, b='trx1'; +INSERT INTO t1 SET a=7, b='trx1'; +INSERT INTO t1 SET a=8, b='trx1'; +INSERT INTO t1 SET a=9, b='trx1'; +INSERT INTO t1 SET a=10, b='trx1'; +INSERT INTO t1 SET a=11, b='trx1'; +INSERT INTO t1 SET a=12, b='trx1'; +INSERT INTO t1 SET a=13, b='trx1'; +INSERT INTO t1 SET a=14, b='trx1'; +INSERT INTO t1 SET a=15, b='trx1'; +INSERT INTO t1 SET a=16, b='trx1'; +INSERT INTO t1 SET a=17, b='trx1'; +INSERT INTO t1 SET a=18, b='trx1'; +INSERT INTO t1 SET a=19, b='trx1'; +INSERT INTO t1 SET a=20, b='trx1'; +INSERT INTO t1 SET a=21, b='trx1'; +INSERT INTO t1 SET a=22, b='trx1'; +INSERT INTO t1 SET a=23, b='trx1'; +INSERT INTO t1 SET a=24, b='trx1'; +COMMIT; +FLUSH LOGS; +BEGIN; +UPDATE t1 SET b='trx2_0' WHERE a = 25; +UPDATE t1 SET b='trx2' WHERE a = 25; +COMMIT; +INSERT INTO t1 SET a=26,b='trx3'; +*** case 1 UNTIL inside trx2 +connection slave1; +BEGIN; +INSERT INTO t1 SET a= 1; +connection slave; +SELECT <= AND < as "pos_until < trx0 and is within trx2"; +pos_until < trx0 and is within trx2 +1 +CHANGE MASTER TO MASTER_USE_GTID=no; +START SLAVE UNTIL MASTER_LOG_FILE = 'file_2', MASTER_LOG_POS = ; +connection slave1; +ROLLBACK; +Proof 1: Correct stop +connection slave; +include/wait_for_slave_sql_to_stop.inc +SELECT count(*) = 1 as 'trx2 is committed' FROM t1 WHERE b = 'trx2'; +trx2 is committed +1 +SELECT count(*) = 0 as 'trx3 is not committed' FROM t1 WHERE b = 'trx3'; +trx3 is not committed +1 +Proof 2: Resume works out +include/start_slave.inc +connection master; +connection slave; +*** case 2 UNTIL inside trx2 +connection slave; +DELETE FROM t1 WHERE a <> 25; +UPDATE t1 SET b='trx0' WHERE a = 25; +connection slave1; +BEGIN; +INSERT INTO t1 SET a= 1; +connection slave; +include/stop_slave.inc +SELECT <= AND < as "pos_until >= trx0 and is within trx2"; +pos_until >= trx0 and is within trx2 +1 +CHANGE MASTER TO MASTER_LOG_FILE = 'file_1', MASTER_LOG_POS = , MASTER_USE_GTID=no; +START SLAVE UNTIL MASTER_LOG_FILE = 'file_2', MASTER_LOG_POS = ; +connection slave1; +ROLLBACK; +Proof 1: Correct stop +connection slave; +include/wait_for_slave_sql_to_stop.inc +SELECT count(*) = 1 as 'trx2 is committed' FROM t1 WHERE b = 'trx2'; +trx2 is committed +1 +SELECT count(*) = 0 as 'trx3 is not committed' FROM t1 WHERE b = 'trx3'; +trx3 is not committed +1 +Proof 2: Resume works out +include/start_slave.inc +connection master; +connection slave; +*** case 3 UNTIL inside trx1 +connection slave; +DELETE FROM t1 WHERE a <> 25; +UPDATE t1 SET b='trx0' WHERE a = 25; +connection slave1; +BEGIN; +INSERT INTO t1 SET a= 1; # block trx1; +connection slave; +include/stop_slave.inc +SELECT < as "pos_until before trx2 start position"; +pos_until before trx2 start position +1 +CHANGE MASTER TO MASTER_LOG_FILE = 'file_1', MASTER_LOG_POS = , MASTER_USE_GTID=no; +START SLAVE UNTIL MASTER_LOG_FILE = 'file_2', MASTER_LOG_POS = ; +connection slave1; +ROLLBACK; +Proof 1: Correct stop +connection slave; +include/wait_for_slave_sql_to_stop.inc +SELECT count(*) = 25-1 as 'trx1 is committed' FROM t1 WHERE b = 'trx1'; +trx1 is committed +1 +SELECT count(*) = 0 as 'trx2 is not committed' FROM t1 WHERE b = 'trx2'; +trx2 is not committed +1 +Proof 2: Resume works out +include/start_slave.inc +connection master; +connection slave; +*** case 4 Relay-log UNTIL inside trx1 +connection slave; +DELETE FROM t1 WHERE a <> 25; +UPDATE t1 SET b='trx0' WHERE a = 25; +connection slave1; +BEGIN; +INSERT INTO t1 SET a= 1; # block trx1; +connection slave; +include/stop_slave.inc +CHANGE MASTER TO MASTER_LOG_FILE = 'file_1', MASTER_LOG_POS = , MASTER_USE_GTID=no; +START SLAVE IO_THREAD; +include/wait_for_slave_io_to_start.inc +START SLAVE UNTIL RELAY_LOG_FILE = 'file_2', RELAY_LOG_POS = ; +connection slave1; +ROLLBACK; +Proof 1: Correct stop +connection slave; +include/wait_for_slave_sql_to_stop.inc +SELECT count(*) = 25-1 as 'trx1 is committed' FROM t1 WHERE b = 'trx1'; +trx1 is committed +1 +SELECT count(*) = 0 as 'trx2 is not committed' FROM t1 WHERE b = 'trx2'; +trx2 is not committed +1 +Proof 2: Resume works out +include/start_slave.inc +connection master; +connection slave; +*** case 5 Relay-log UNTIL inside a "big" trx that spawns few relay logs +connection master; +CREATE TABLE t2 (a TEXT) ENGINE=InnoDB; +FLUSH LOGS; +connection slave; +connection slave; +include/stop_slave.inc +connection master; +BEGIN; +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +INSERT INTO t2 SET a=repeat('a',1024); +COMMIT; +INSERT INTO t2 SET a='a'; +connection slave; +START SLAVE IO_THREAD; +include/wait_for_slave_io_to_start.inc +START SLAVE UNTIL RELAY_LOG_FILE = 'file_2', RELAY_LOG_POS = ; +Proof 1: Correct stop +connection slave; +include/wait_for_slave_sql_to_stop.inc +Proof 2: Resume works out +include/start_slave.inc +connection master; +connection slave; +include/diff_tables.inc [master:t2,slave:t2] +*** case 6 Relay-log UNTIL inside a small trx inside a sequence of relay logs +connection slave; +include/stop_slave.inc +connection master; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +BEGIN; +DELETE FROM t2 LIMIT 1; +COMMIT; +COMMIT; +connection slave; +START SLAVE IO_THREAD; +include/wait_for_slave_io_to_start.inc +connection master; +include/sync_slave_io_with_master.inc +connection slave; +START SLAVE UNTIL RELAY_LOG_FILE = 'file_2', RELAY_LOG_POS = ; +Proof 1: Correct stop +connection slave; +include/wait_for_slave_sql_to_stop.inc +Proof 2: Resume works out +include/start_slave.inc +connection master; +connection slave; +include/diff_tables.inc [master:t2,slave:t2] +connection slave; +include/stop_slave.inc +SET GLOBAL max_relay_log_size=@old_max_relay_log_size; +SET GLOBAL slave_parallel_mode=@old_parallel_mode; +SET GLOBAL slave_parallel_threads=@old_parallel_threads; +include/start_slave.inc +connection master; +DROP TABLE t1, t2; +connection slave; +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_parallel_optimistic_until.test b/mysql-test/suite/rpl/t/rpl_parallel_optimistic_until.test new file mode 100644 index 00000000000..508213c9075 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_parallel_optimistic_until.test @@ -0,0 +1,465 @@ +--source include/have_innodb.inc +--source include/have_debug.inc +--source include/have_debug_sync.inc +--source include/master-slave.inc +# Format is restricted because the test expects a specific result of +# relay-logging that splits a transaction into two different files. +--source include/have_binlog_format_row.inc + +# +# MDEV-15152 Optimistic parallel slave doesn't cope well with START SLAVE UNTIL +# +--connection slave +--source include/stop_slave.inc +RESET MASTER; +RESET SLAVE; + +--connection master +RESET MASTER; +CREATE TABLE t1 (a int primary key, b text) ENGINE=InnoDB; +--let $a0 = 25 +--eval INSERT INTO t1 SET a=$a0, b='trx0' +# Memorize the position for replication restart from it +--let $pos_trx0 = query_get_value(SHOW MASTER STATUS, Position, 1) + +--connection slave +--source include/start_slave.inc + +--connection master +# --connection slave +--sync_slave_with_master +--source include/stop_slave.inc +ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB; +SET @old_parallel_threads=@@GLOBAL.slave_parallel_threads; +SET GLOBAL slave_parallel_threads=2; +SET @old_parallel_mode=@@GLOBAL.slave_parallel_mode; +SET GLOBAL slave_parallel_mode='optimistic'; + +# Run the slave in the following modes one by one. +# +# 1. the until position is set in the middle of trx2 +# below $pos_trx0 of the last exec position in the first file +# 2. and above $pos_trx0 +# In either case trx2 must commit before slave stops. +# 3. the until postion is inside trx1 +# 4. RELAY log until inside trx1 +# 5. RELAY log until inside a "big" trx +# 6. RELAY log until inside a trx within a sequence of relay logs +# +# Execution flaw for Until_Master_Pos cases follows as: +# create the transaction trx1, trx2 +# logged at the beginning of two subsequent binlog files. +# Set the until position to at the middle of the 2rd transaction. +# Engage the optimistic scheduler while having trx1 execution blocked. +# Lift the block after trx2 has reached waiting its order to commit. +# *Proof 1* +# Observe that the slave applier stops at a correct position. +# In the bug condition it would stop prematurely having the stop position +# in the first file, therefore trx2 not committed. +# Specifically, an internal transaction position until makes the applier to run +# beyond it to commit commit the current transaction. +# *Proof 2* +# Observe the following START SLAVE resumes OK. +# +# Auxiliary third trx3 on master is just for triggering the actual stop +# (whihc is a legacy UNTIL's property). +# trx0 is to produce a specific value of the last executed binlog file:pos +# to emulate the bug condition. +# +# Intermediate checks via SELECT are supposed to succeed +# with putting out value 1. +# +# NOTE: Relay log until tests have to use explicit log names and position +# which may require to adjust with future changes to event formats etc. +# + +--connection slave +SET @old_max_relay_log_size = @@global.max_relay_log_size; +SET @@global.max_relay_log_size=4096; + +--connection master +# trx1 +--let $a=1 +BEGIN; +while (`SELECT $a < $a0`) +{ + --eval INSERT INTO t1 SET a=$a, b='trx1' +--inc $a +} +COMMIT; +--let $fil_1 = query_get_value(SHOW MASTER STATUS, File, 1) +--let $pos_trx1 = query_get_value(SHOW MASTER STATUS, Position, 1) + +FLUSH LOGS; + +# $pos_0 the offset of the first event of trx2 in new file +--let $pos_0=query_get_value(SHOW MASTER STATUS, Position, 1) +# trx2 +--let $a=$a0 +BEGIN; +--eval UPDATE t1 SET b='trx2_0' WHERE a = $a +--eval UPDATE t1 SET b='trx2' WHERE a = $a +COMMIT; +--let $fil_2=query_get_value(SHOW MASTER STATUS, File, 1) +--let $pos_trx2=query_get_value(SHOW MASTER STATUS, Position, 1) + +# trx3 +--let $a=$a0 +--inc $a +--eval INSERT INTO t1 SET a=$a,b='trx3' +--let $pos_trx3=query_get_value(SHOW MASTER STATUS, Position, 1) +--let $a= + + +--echo *** case 1 UNTIL inside trx2 + +--connection slave1 +# Blocker to hold off EXEC_MASTER_LOG_POS advance +BEGIN; + --eval INSERT INTO t1 SET a= 1 +--connection slave +--let $pos_until=`SELECT $pos_trx0 - 1` +--replace_result $pos_0 $pos_until $pos_trx2 +--eval SELECT $pos_0 <= $pos_until AND $pos_until < $pos_trx2 as "pos_until < trx0 and is within trx2" +CHANGE MASTER TO MASTER_USE_GTID=no; +--replace_result $fil_2 file_2 $pos_until +--eval START SLAVE UNTIL MASTER_LOG_FILE = '$fil_2', MASTER_LOG_POS = $pos_until + +--let $wait_condition= SELECT COUNT(*) > 0 FROM information_schema.processlist WHERE state = "Waiting for prior transaction to commit" +--source include/wait_condition.inc + +--connection slave1 +# unblock to see the slave applier stops at $until +ROLLBACK; + +--echo Proof 1: Correct stop +--connection slave +--source include/wait_for_slave_sql_to_stop.inc +--let $file_stop= query_get_value(SHOW SLAVE STATUS, Relay_Master_Log_File, 1) +--let $pos_stop= query_get_value(SHOW SLAVE STATUS, Exec_Master_Log_Pos, 1) +if (`SELECT "$file_stop" != "$fil_2" OR $pos_stop < $pos_until`) +{ + --echo *** ERROR: Slave stopped at $file_stop:$pos_stop which is not $fil_2:$pos_until. + --die +} +--eval SELECT count(*) = 1 as 'trx2 is committed' FROM t1 WHERE b = 'trx2' +--eval SELECT count(*) = 0 as 'trx3 is not committed' FROM t1 WHERE b = 'trx3' + +--echo Proof 2: Resume works out +--source include/start_slave.inc +--connection master +--sync_slave_with_master + + +--echo *** case 2 UNTIL inside trx2 + +--connection slave +--eval DELETE FROM t1 WHERE a <> $a0 +--eval UPDATE t1 SET b='trx0' WHERE a = $a0 + +--connection slave1 +# Blocker to hold off EXEC_MASTER_LOG_POS advance +BEGIN; + --eval INSERT INTO t1 SET a= 1 + +--connection slave +--source include/stop_slave.inc + +--let $pos_until=`SELECT $pos_trx2 - 1` +--replace_result $pos_trx0 $pos_until $pos_trx2 +--eval SELECT $pos_trx0 <= $pos_until AND $pos_until < $pos_trx2 as "pos_until >= trx0 and is within trx2" +--replace_result $fil_1 file_1 $pos_trx0 +--eval CHANGE MASTER TO MASTER_LOG_FILE = '$fil_1', MASTER_LOG_POS = $pos_trx0, MASTER_USE_GTID=no +--replace_result $fil_2 file_2 $pos_until +--eval START SLAVE UNTIL MASTER_LOG_FILE = '$fil_2', MASTER_LOG_POS = $pos_until + +--let $wait_condition= SELECT COUNT(*) > 0 FROM information_schema.processlist WHERE state = "Waiting for prior transaction to commit" +--source include/wait_condition.inc + +--connection slave1 +# unblock to see the slave applier stops at $until +ROLLBACK; + +--echo Proof 1: Correct stop +--connection slave +--source include/wait_for_slave_sql_to_stop.inc +--let $file_stop= query_get_value(SHOW SLAVE STATUS, Relay_Master_Log_File, 1) +--let $pos_stop= query_get_value(SHOW SLAVE STATUS, Exec_Master_Log_Pos, 1) +if (`SELECT "$file_stop" != "$fil_2" OR $pos_stop < $pos_until`) +{ + --echo *** ERROR: Slave stopped at $file_stop:$pos_stop which is not $fil_2:$pos_until. + --die +} +--eval SELECT count(*) = 1 as 'trx2 is committed' FROM t1 WHERE b = 'trx2' +--eval SELECT count(*) = 0 as 'trx3 is not committed' FROM t1 WHERE b = 'trx3' + +--echo Proof 2: Resume works out +--source include/start_slave.inc +--connection master +--sync_slave_with_master + + +--echo *** case 3 UNTIL inside trx1 + +--connection slave +--eval DELETE FROM t1 WHERE a <> $a0 +--eval UPDATE t1 SET b='trx0' WHERE a = $a0 + + +--connection slave1 +# Blocker to hold off EXEC_MASTER_LOG_POS advance +BEGIN; + --eval INSERT INTO t1 SET a= 1; # block trx1 + +--connection slave +--source include/stop_slave.inc + +--let $pos_until=`SELECT $pos_0 - 1` +--replace_result $pos_0 $pos_until $pos_trx2 +--eval SELECT $pos_until < $pos_0 as "pos_until before trx2 start position" +--replace_result $fil_1 file_1 $pos_trx0 +--eval CHANGE MASTER TO MASTER_LOG_FILE = '$fil_1', MASTER_LOG_POS = $pos_trx0, MASTER_USE_GTID=no +--replace_result $fil_2 file_2 $pos_until +--eval START SLAVE UNTIL MASTER_LOG_FILE = '$fil_2', MASTER_LOG_POS = $pos_until + +--connection slave1 +# unblock to see the slave applier stops at $until +ROLLBACK; + +--echo Proof 1: Correct stop +--connection slave +--source include/wait_for_slave_sql_to_stop.inc +--let $file_stop= query_get_value(SHOW SLAVE STATUS, Relay_Master_Log_File, 1) +--let $pos_stop= query_get_value(SHOW SLAVE STATUS, Exec_Master_Log_Pos, 1) +if (`SELECT "$file_stop" != "$fil_2" OR $pos_stop < $pos_until`) +{ + --echo *** ERROR: Slave stopped at $file_stop:$pos_stop which is not $fil_2:$pos_until. + --die +} +--eval SELECT count(*) = $a0-1 as 'trx1 is committed' FROM t1 WHERE b = 'trx1' +--eval SELECT count(*) = 0 as 'trx2 is not committed' FROM t1 WHERE b = 'trx2' + +--echo Proof 2: Resume works out +--source include/start_slave.inc +--connection master +--sync_slave_with_master + + +--echo *** case 4 Relay-log UNTIL inside trx1 + +--connection slave +--eval DELETE FROM t1 WHERE a <> $a0 +--eval UPDATE t1 SET b='trx0' WHERE a = $a0 + +--connection slave1 +# Blocker to hold off EXEC_MASTER_LOG_POS advance +BEGIN; + --eval INSERT INTO t1 SET a= 1; # block trx1 + +--connection slave +--source include/stop_slave.inc +--replace_result $fil_1 file_1 $pos_trx0 +--eval CHANGE MASTER TO MASTER_LOG_FILE = '$fil_1', MASTER_LOG_POS = $pos_trx0, MASTER_USE_GTID=no +START SLAVE IO_THREAD; +--source include/wait_for_slave_io_to_start.inc + +# The following test sets the stop coordinate is set to inside the first event +# of a relay log that holds events of a transaction started in an earlier log. +# Peek the stop position in the middle of trx1, not even on a event boundary. +--let $pos_until=255 +--let $file_rl=slave-relay-bin.000003 +--let $binlog_file=$file_rl + +--let $pos_xid=508 +--let $info= query_get_value(SHOW RELAYLOG EVENTS IN '$file_rl' FROM $pos_xid LIMIT 1, Info, 1) + +if (`SELECT "$info" NOT LIKE "COMMIT /* xid=% */" OR $pos_xid < $pos_until`) +{ + --echo *** Unexpected offset. Refine it to point to the correct XID event! + --die +} + +--replace_result $file_rl file_2 $pos_until +--eval START SLAVE UNTIL RELAY_LOG_FILE = '$file_rl', RELAY_LOG_POS = $pos_until + +--connection slave1 +# unblock to see the slave applier stops at $until +ROLLBACK; + +--echo Proof 1: Correct stop +--connection slave +--source include/wait_for_slave_sql_to_stop.inc +--let $file_stop= query_get_value(SHOW SLAVE STATUS, Relay_Log_File, 1) +--let $pos_stop= query_get_value(SHOW SLAVE STATUS, Relay_Log_Pos, 1) +if (`SELECT strcmp("$file_rl","$file_stop") > -1`) +{ + --echo *** ERROR: Slave stopped at $file_stop:$pos_stop which is not $file_rl:$pos_until. + --die +} + +--eval SELECT count(*) = $a0-1 as 'trx1 is committed' FROM t1 WHERE b = 'trx1' +--eval SELECT count(*) = 0 as 'trx2 is not committed' FROM t1 WHERE b = 'trx2' + +--echo Proof 2: Resume works out +--source include/start_slave.inc +--connection master +--sync_slave_with_master + + + +--echo *** case 5 Relay-log UNTIL inside a "big" trx that spawns few relay logs + +--connection master +CREATE TABLE t2 (a TEXT) ENGINE=InnoDB; +FLUSH LOGS; + +--sync_slave_with_master +--let $file_stop= query_get_value(SHOW SLAVE STATUS, Relay_Log_File, 1) +--let $pos_stop= query_get_value(SHOW SLAVE STATUS, Relay_Log_Pos, 1) +--let $records=`SELECT floor(4*@@global.max_relay_log_size / 1024) + 1` + +--connection slave +--source include/stop_slave.inc + +--connection master +# trx4 +BEGIN; +--let $i=$records +while ($i) +{ + INSERT INTO t2 SET a=repeat('a',1024); + +--dec $i +} +COMMIT; + +# slave will stop there: +--let $file_trx4 = query_get_value(SHOW MASTER STATUS, File, 1) +--let $pos_trx4 = query_get_value(SHOW MASTER STATUS, Position, 1) + +# trx5 +INSERT INTO t2 SET a='a'; +--let $pos_trx5 = query_get_value(SHOW MASTER STATUS, Position, 1) + +--connection slave +START SLAVE IO_THREAD; +--source include/wait_for_slave_io_to_start.inc + +# Set position inside the transaction though the value +# specified is beyond that relay log file. +# The coordianate may point to in a different event in future changes +# but should not move away from inside this big group of events. +# So we don't test which event in the transaction it points to. +--let $pos_until= 4500 +--let $file_rl= slave-relay-bin.000010 + +--replace_result $file_rl file_2 $pos_until +--eval START SLAVE UNTIL RELAY_LOG_FILE = '$file_rl', RELAY_LOG_POS = $pos_until + +--echo Proof 1: Correct stop +--connection slave +--source include/wait_for_slave_sql_to_stop.inc +--let $file_stop= query_get_value(SHOW SLAVE STATUS, Relay_Master_Log_File, 1) +--let $pos_stop= query_get_value(SHOW SLAVE STATUS, Exec_Master_Log_Pos, 1) +# It's showed the actual stop occurred before trx5 +if (`SELECT strcmp("$file_trx4", "$file_stop") <> 0 OR $pos_stop >= $pos_trx5 OR count(*) <> $records FROM t2`) +{ + --echo *** ERROR: Slave stopped at *binlog* $file_stop:$pos_stop which is not $file_trx4:$pos_trx4. + --die +} + +--echo Proof 2: Resume works out +--source include/start_slave.inc +--connection master +--sync_slave_with_master + +--let $diff_tables=master:t2,slave:t2 +--source include/diff_tables.inc + + + +--echo *** case 6 Relay-log UNTIL inside a small trx inside a sequence of relay logs + +--connection slave +--source include/stop_slave.inc + +--connection master +# trx6 +--let $records=`SELECT count(*) FROM t2` +while ($records) +{ + BEGIN; + DELETE FROM t2 LIMIT 1; + COMMIT; +--dec $records +} +COMMIT; + +--connection slave +START SLAVE IO_THREAD; +--source include/wait_for_slave_io_to_start.inc + +--connection master +--source include/sync_slave_io_with_master.inc + +--connection slave +# The relay-log coordinate is not at an event boundary and +# also may change across the server version. +# The test makes its best to check its coherance. +--let $pos_until= 3130 +--let $file_rl= slave-relay-bin.000018 + +--let $pos_gtid = 2987 +--let $info= query_get_value(SHOW RELAYLOG EVENTS IN '$file_rl' FROM $pos_gtid LIMIT 1, Info, 1) + +if (`SELECT "$info" != "BEGIN GTID 0-1-23"`) +{ + --echo *** Unexpected offset. Refine it to point to the correct GTID! + --die +} +--let $pos_event = 3120 +--let $type= query_get_value(SHOW RELAYLOG EVENTS IN '$file_rl' FROM $pos_event LIMIT 1, Event_type, 1) +if (`SELECT "$type" != "Delete_rows_v1"`) +{ + --echo *** Unexpected offset. Refine it to point to the expected event! + --die +} + +--replace_result $file_rl file_2 $pos_until +--eval START SLAVE UNTIL RELAY_LOG_FILE = '$file_rl', RELAY_LOG_POS = $pos_until + +--echo Proof 1: Correct stop +--connection slave +--source include/wait_for_slave_sql_to_stop.inc +--let $file_stop= query_get_value(SHOW SLAVE STATUS, Relay_Log_File, 1) +--let $pos_stop= query_get_value(SHOW SLAVE STATUS, Relay_Log_Pos, 1) +if (`SELECT strcmp("$file_stop", "$file_rl") = -1 OR + strcmp("$file_stop", "$file_rl") = 0 AND $pos_stop < $pos_until`) +{ + --echo *** ERROR: Slave stopped at *relay* $file_stop:$pos_stop which is not $file_rl:$pos_until. + --die +} + +--echo Proof 2: Resume works out +--source include/start_slave.inc +--connection master +--sync_slave_with_master + +--let $diff_tables=master:t2,slave:t2 +--source include/diff_tables.inc + +# +# Clean up. +# +--connection slave +--source include/stop_slave.inc +SET GLOBAL max_relay_log_size=@old_max_relay_log_size; +SET GLOBAL slave_parallel_mode=@old_parallel_mode; +SET GLOBAL slave_parallel_threads=@old_parallel_threads; +--source include/start_slave.inc + +--connection master +DROP TABLE t1, t2; + +--sync_slave_with_master +--source include/rpl_end.inc diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc index 027aa5f628c..3871ddcf8ef 100644 --- a/sql/rpl_parallel.cc +++ b/sql/rpl_parallel.cc @@ -75,18 +75,18 @@ handle_queued_pos_update(THD *thd, rpl_parallel_thread::queued_event *qev) /* Do not update position if an earlier event group caused an error abort. */ DBUG_ASSERT(qev->typ == rpl_parallel_thread::queued_event::QUEUED_POS_UPDATE); + rli= qev->rgi->rli; e= qev->entry_for_queued; - if (e->stop_on_error_sub_id < (uint64)ULONGLONG_MAX || e->force_abort) + if (e->stop_on_error_sub_id < (uint64)ULONGLONG_MAX || + (e->force_abort && !rli->stop_for_until)) return; - rli= qev->rgi->rli; mysql_mutex_lock(&rli->data_lock); cmp= strcmp(rli->group_relay_log_name, qev->event_relay_log_name); if (cmp < 0) { rli->group_relay_log_pos= qev->future_event_relay_log_pos; strmake_buf(rli->group_relay_log_name, qev->event_relay_log_name); - rli->notify_group_relay_log_name_update(); } else if (cmp == 0 && rli->group_relay_log_pos < qev->future_event_relay_log_pos) rli->group_relay_log_pos= qev->future_event_relay_log_pos; diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 3e1bb28f701..7215cdd4b96 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -60,6 +60,7 @@ Relay_log_info::Relay_log_info(bool is_slave_recovery) slave_running(MYSQL_SLAVE_NOT_RUN), until_condition(UNTIL_NONE), until_log_pos(0), retried_trans(0), executed_entries(0), sql_delay(0), sql_delay_end(0), + until_relay_log_names_defer(false), m_flags(0) { DBUG_ENTER("Relay_log_info::Relay_log_info"); @@ -499,6 +500,8 @@ void Relay_log_info::clear_until_condition() until_condition= Relay_log_info::UNTIL_NONE; until_log_name[0]= 0; until_log_pos= 0; + until_relay_log_names_defer= false; + DBUG_VOID_RETURN; } @@ -989,7 +992,6 @@ void Relay_log_info::inc_group_relay_log_pos(ulonglong log_pos, { group_relay_log_pos= rgi->future_event_relay_log_pos; strmake_buf(group_relay_log_name, rgi->event_relay_log_name); - notify_group_relay_log_name_update(); } else if (cmp == 0 && group_relay_log_pos < rgi->future_event_relay_log_pos) group_relay_log_pos= rgi->future_event_relay_log_pos; @@ -1279,29 +1281,78 @@ err: autoincrement or if we have transactions). Should be called ONLY if until_condition != UNTIL_NONE ! + + In the parallel execution mode and UNTIL_MASTER_POS the file name is + presented by future_event_master_log_name which may be ahead of + group_master_log_name. Log_event::log_pos does relate to it nevertheless + so the pair comprises a correct binlog coordinate. + Internal group events and events that have zero log_pos also + produce the zero for the local log_pos which may not lead to the + function falsely return true. + In UNTIL_RELAY_POS the original caching and notification are simplified + to straightforward files comparison when the current event can't be + a part of an event group. + RETURN VALUE true - condition met or error happened (condition seems to have bad log file name) false - condition not met */ -bool Relay_log_info::is_until_satisfied(my_off_t master_beg_pos) +bool Relay_log_info::is_until_satisfied(Log_event *ev) { const char *log_name; ulonglong log_pos; + /* Prevents stopping within transaction; needed solely for Relay UNTIL. */ + bool in_trans= false; + DBUG_ENTER("Relay_log_info::is_until_satisfied"); if (until_condition == UNTIL_MASTER_POS) { log_name= (mi->using_parallel() ? future_event_master_log_name : group_master_log_name); - log_pos= master_beg_pos; + log_pos= (get_flag(Relay_log_info::IN_TRANSACTION) || !ev || !ev->log_pos) ? + (mi->using_parallel() ? 0 : group_master_log_pos) : + ev->log_pos - ev->data_written; } else { DBUG_ASSERT(until_condition == UNTIL_RELAY_POS); - log_name= group_relay_log_name; - log_pos= group_relay_log_pos; + if (!mi->using_parallel()) + { + log_name= group_relay_log_name; + log_pos= group_relay_log_pos; + } + else + { + log_name= event_relay_log_name; + log_pos= event_relay_log_pos; + in_trans= get_flag(Relay_log_info::IN_TRANSACTION); + /* + until_log_names_cmp_result is set to UNKNOWN either + - by a non-group event *and* only when it is in the middle of a group + - or by a group event when the preceding group made the above + non-group event to defer the resetting. + */ + if ((ev && !Log_event::is_group_event(ev->get_type_code()))) + { + if (in_trans) + { + until_relay_log_names_defer= true; + } + else + { + until_log_names_cmp_result= UNTIL_LOG_NAMES_CMP_UNKNOWN; + until_relay_log_names_defer= false; + } + } + else if (!in_trans && until_relay_log_names_defer) + { + until_log_names_cmp_result= UNTIL_LOG_NAMES_CMP_UNKNOWN; + until_relay_log_names_defer= false; + } + } } DBUG_PRINT("info", ("group_master_log_name='%s', group_master_log_pos=%llu", @@ -1355,8 +1406,8 @@ bool Relay_log_info::is_until_satisfied(my_off_t master_beg_pos) } DBUG_RETURN(((until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_EQUAL && - log_pos >= until_log_pos) || - until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_GREATER)); + (log_pos >= until_log_pos && !in_trans)) || + until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_GREATER)); } diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h index 4ec4821b67d..2bc0a80268a 100644 --- a/sql/rpl_rli.h +++ b/sql/rpl_rli.h @@ -219,7 +219,7 @@ public: */ char future_event_master_log_name[FN_REFLEN]; - /* + /* Original log name and position of the group we're currently executing (whose coordinates are group_relay_log_name/pos in the relay log) in the master's binlog. These concern the *group*, because in the master's @@ -419,7 +419,7 @@ public: void close_temporary_tables(); /* Check if UNTIL condition is satisfied. See slave.cc for more. */ - bool is_until_satisfied(my_off_t); + bool is_until_satisfied(Log_event *ev); inline ulonglong until_pos() { DBUG_ASSERT(until_condition == UNTIL_MASTER_POS || @@ -427,7 +427,13 @@ public: return ((until_condition == UNTIL_MASTER_POS) ? group_master_log_pos : group_relay_log_pos); } - + inline char *until_name() + { + DBUG_ASSERT(until_condition == UNTIL_MASTER_POS || + until_condition == UNTIL_RELAY_POS); + return ((until_condition == UNTIL_MASTER_POS) ? group_master_log_name : + group_relay_log_name); + } /** Helper function to do after statement completion. @@ -564,6 +570,15 @@ private: relay_log.info had 4 lines. Now it has 5 lines. */ static const int LINES_IN_RELAY_LOG_INFO_WITH_DELAY= 5; + /* + Hint for when to stop event distribution by sql driver thread. + The flag is set ON by a non-group event when this event is in the middle + of a group (e.g a transaction group) so it's too early + to refresh the current-relay-log vs until-log cached comparison result. + And it is checked and to decide whether it's a right time to do so + when the being processed group has been fully scheduled. + */ + bool until_relay_log_names_defer; /* Holds the state of the data in the relay log. diff --git a/sql/slave.cc b/sql/slave.cc index eb95afd140b..1bf83aa9652 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -3921,12 +3921,8 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli, rli->until_condition == Relay_log_info::UNTIL_RELAY_POS) && (ev->server_id != global_system_variables.server_id || rli->replicate_same_server_id) && - rli->is_until_satisfied((rli->get_flag(Relay_log_info::IN_TRANSACTION) || !ev->log_pos) - ? rli->group_master_log_pos - : ev->log_pos - ev->data_written)) + rli->is_until_satisfied(ev)) { - sql_print_information("Slave SQL thread stopped because it reached its" - " UNTIL position %llu", rli->until_pos()); /* Setting abort_slave flag because we do not want additional message about error in query execution to be printed. @@ -5136,10 +5132,14 @@ pthread_handler_t handle_slave_sql(void *arg) } if ((rli->until_condition == Relay_log_info::UNTIL_MASTER_POS || rli->until_condition == Relay_log_info::UNTIL_RELAY_POS) && - rli->is_until_satisfied(rli->group_master_log_pos)) + rli->is_until_satisfied(NULL)) { sql_print_information("Slave SQL thread stopped because it reached its" - " UNTIL position %llu", rli->until_pos()); + " UNTIL position %llu in %s %s file", + rli->until_pos(), rli->until_name(), + rli->until_condition == + Relay_log_info::UNTIL_MASTER_POS ? + "binlog" : "relaylog"); mysql_mutex_unlock(&rli->data_lock); goto err; } @@ -5205,7 +5205,24 @@ pthread_handler_t handle_slave_sql(void *arg) err: if (mi->using_parallel()) rli->parallel.wait_for_done(thd, rli); + /* Gtid_list_log_event::do_apply_event has already reported the GTID until */ + if (rli->stop_for_until && rli->until_condition != Relay_log_info::UNTIL_GTID) + { + if (global_system_variables.log_warnings > 2) + sql_print_information("Slave SQL thread UNTIL stop was requested at position " + "%llu in %s %s file", + rli->until_log_pos, rli->until_log_name, + rli->until_condition == + Relay_log_info::UNTIL_MASTER_POS ? + "binlog" : "relaylog"); + sql_print_information("Slave SQL thread stopped because it reached its" + " UNTIL position %llu in %s %s file", + rli->until_pos(), rli->until_name(), + rli->until_condition == + Relay_log_info::UNTIL_MASTER_POS ? + "binlog" : "relaylog"); + }; /* Thread stopped. Print the current replication position to the log */ { StringBuffer<100> tmp; From 403dacf6a9a373c25a808a1be9169ab2cd001448 Mon Sep 17 00:00:00 2001 From: Monty Date: Tue, 26 May 2020 20:04:47 +0300 Subject: [PATCH 25/59] Fixed crash in aria recovery when using bulk insert MDEV-20578 Got error 126 when executing undo undo_key_delete upon Aria crash recovery The crash happens in this scenario: - Table with unique keys and non unique keys - Batch insert (LOAD DATA or INSERT ... SELECT) with REPLACE - Some insert succeeds followed by duplicate key error In the above scenario the table gets corrupted. The bug was that we don't generate any undo entry for the failed insert as the whole insert can be ignored by undo. The code did however not take into account that when bulk insert is used, we would write cached keys to the file on failure and undo would wrongly ignore these. Fixed by moving the writing of the cache keys after we write the aborted-insert event to the log. --- mysql-test/suite/maria/bulk_insert_crash.opt | 2 ++ .../suite/maria/bulk_insert_crash.result | 13 +++++++ mysql-test/suite/maria/bulk_insert_crash.test | 36 +++++++++++++++++++ sql/handler.cc | 13 +++++++ sql/handler.h | 8 +---- storage/maria/ma_recovery.c | 21 +++++++---- storage/maria/ma_write.c | 15 ++++---- 7 files changed, 89 insertions(+), 19 deletions(-) create mode 100644 mysql-test/suite/maria/bulk_insert_crash.opt create mode 100644 mysql-test/suite/maria/bulk_insert_crash.result create mode 100644 mysql-test/suite/maria/bulk_insert_crash.test diff --git a/mysql-test/suite/maria/bulk_insert_crash.opt b/mysql-test/suite/maria/bulk_insert_crash.opt new file mode 100644 index 00000000000..f85a8d9c973 --- /dev/null +++ b/mysql-test/suite/maria/bulk_insert_crash.opt @@ -0,0 +1,2 @@ +--skip-stack-trace --skip-core-file +--default-storage-engine=Aria diff --git a/mysql-test/suite/maria/bulk_insert_crash.result b/mysql-test/suite/maria/bulk_insert_crash.result new file mode 100644 index 00000000000..fc28bf325ba --- /dev/null +++ b/mysql-test/suite/maria/bulk_insert_crash.result @@ -0,0 +1,13 @@ +create table t1 (a int primary key, b int, c int, unique key(b), key(c)) engine=aria transactional=1; +insert into t1 values (1000,1000,1000); +insert into t1 select seq,seq+100, seq+200 from seq_1_to_10; +SET GLOBAL debug_dbug="+d,crash_end_bulk_insert"; +REPLACE into t1 select seq+20,seq+95, seq + 300 from seq_1_to_10; +ERROR HY000: Lost connection to MySQL server during query +check table t1; +Table Op Msg_type Msg_text +test.t1 check status OK +select sum(a),sum(b),sum(c) from t1; +sum(a) sum(b) sum(c) +1055 2055 3055 +drop table t1; diff --git a/mysql-test/suite/maria/bulk_insert_crash.test b/mysql-test/suite/maria/bulk_insert_crash.test new file mode 100644 index 00000000000..d9167c3f0d7 --- /dev/null +++ b/mysql-test/suite/maria/bulk_insert_crash.test @@ -0,0 +1,36 @@ +--source include/not_embedded.inc +--source include/not_valgrind.inc +# Avoid CrashReporter popup on Mac +--source include/not_crashrep.inc +# Binary must be compiled with debug for crash to occur +--source include/have_debug.inc +--source include/have_sequence.inc + +# +# MDEV-20578 Got error 126 when executing undo undo_key_delete upon Aria crash +# recovery +# + +# Write file to make mysql-test-run.pl expect crash and restart +--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect + +create table t1 (a int primary key, b int, c int, unique key(b), key(c)) engine=aria transactional=1; +insert into t1 values (1000,1000,1000); +insert into t1 select seq,seq+100, seq+200 from seq_1_to_10; + +# Insert into t1 with batch insert where we get a rows replaced after +# a few sucessful inserts + +SET GLOBAL debug_dbug="+d,crash_end_bulk_insert"; + +--error 2013 +REPLACE into t1 select seq+20,seq+95, seq + 300 from seq_1_to_10; + +# Wait until restarted +--enable_reconnect +--source include/wait_until_connected_again.inc +--disable_reconnect + +check table t1; +select sum(a),sum(b),sum(c) from t1; +drop table t1; diff --git a/sql/handler.cc b/sql/handler.cc index d739ce7cd5b..d6e1680143c 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -4164,6 +4164,19 @@ int handler::ha_repair(THD* thd, HA_CHECK_OPT* check_opt) } +/** + End bulk insert +*/ + +int handler::ha_end_bulk_insert() +{ + DBUG_ENTER("handler::ha_end_bulk_insert"); + DBUG_EXECUTE_IF("crash_end_bulk_insert", + { extra(HA_EXTRA_FLUSH) ; DBUG_SUICIDE();}); + estimation_rows_to_insert= 0; + DBUG_RETURN(end_bulk_insert()); +} + /** Bulk update row: public interface. diff --git a/sql/handler.h b/sql/handler.h index a0a43daf144..d1c4b79ee48 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -2911,13 +2911,7 @@ public: start_bulk_insert(rows, flags); DBUG_VOID_RETURN; } - int ha_end_bulk_insert() - { - DBUG_ENTER("handler::ha_end_bulk_insert"); - estimation_rows_to_insert= 0; - int ret= end_bulk_insert(); - DBUG_RETURN(ret); - } + int ha_end_bulk_insert(); int ha_bulk_update_row(const uchar *old_data, uchar *new_data, uint *dup_key_found); int ha_delete_all_rows(); diff --git a/storage/maria/ma_recovery.c b/storage/maria/ma_recovery.c index c8031030361..96b678b8695 100644 --- a/storage/maria/ma_recovery.c +++ b/storage/maria/ma_recovery.c @@ -949,6 +949,7 @@ prototype_redo_exec_hook(REDO_RENAME_TABLE) char *old_name, *new_name; int error= 1; MARIA_HA *info= NULL; + my_bool from_table_is_crashed= 0; DBUG_ENTER("exec_REDO_LOGREC_REDO_RENAME_TABLE"); if (skip_DDLs) @@ -1018,15 +1019,15 @@ prototype_redo_exec_hook(REDO_RENAME_TABLE) } if (maria_is_crashed(info)) { - tprint(tracef, ", is crashed, can't rename it"); - ALERT_USER(); - goto end; + tprint(tracef, "is crashed, can't be used for rename ; new-name table "); + from_table_is_crashed= 1; } if (close_one_table(info->s->open_file_name.str, rec->lsn) || maria_close(info)) goto end; info= NULL; - tprint(tracef, ", is ok for renaming; new-name table "); + if (!from_table_is_crashed) + tprint(tracef, "is ok for renaming; new-name table "); } else /* one or two files absent, or header corrupted... */ { @@ -1091,11 +1092,19 @@ prototype_redo_exec_hook(REDO_RENAME_TABLE) goto end; info= NULL; /* abnormal situation */ - tprint(tracef, ", exists but is older than record, can't rename it"); + tprint(tracef, "exists but is older than record, can't rename it"); goto end; } else /* one or two files absent, or header corrupted... */ - tprint(tracef, ", can't be opened, probably does not exist"); + tprint(tracef, "can't be opened, probably does not exist"); + + if (from_table_is_crashed) + { + eprint(tracef, "Aborting rename as old table was crashed"); + ALERT_USER(); + goto end; + } + tprint(tracef, ", renaming '%s'", old_name); if (maria_rename(old_name, new_name)) { diff --git a/storage/maria/ma_write.c b/storage/maria/ma_write.c index 126554fffdb..826ff40baaf 100644 --- a/storage/maria/ma_write.c +++ b/storage/maria/ma_write.c @@ -335,12 +335,6 @@ err: my_errno == HA_ERR_NULL_IN_SPATIAL || my_errno == HA_ERR_OUT_OF_MEM) { - if (info->bulk_insert) - { - uint j; - for (j=0 ; j < share->base.keys ; j++) - maria_flush_bulk_insert(info, j); - } info->errkey= i < share->base.keys ? (int) i : -1; /* We delete keys in the reverse order of insertion. This is the order that @@ -366,6 +360,7 @@ err: { if (_ma_ft_del(info,i,buff,record,filepos)) { + fatal_error= 1; if (local_lock_tree) mysql_rwlock_unlock(&keyinfo->root_lock); break; @@ -380,6 +375,7 @@ err: filepos, info->trn->trid))) { + fatal_error= 1; if (local_lock_tree) mysql_rwlock_unlock(&keyinfo->root_lock); break; @@ -399,6 +395,13 @@ err: fatal_error= 1; } + if (info->bulk_insert) + { + uint j; + for (j=0 ; j < share->base.keys ; j++) + maria_flush_bulk_insert(info, j); + } + if (fatal_error) { maria_print_error(info->s, HA_ERR_CRASHED); From 18d8f06f31cbe3913e1c80c0c5120f23e036bd3e Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Sun, 17 May 2020 16:24:40 +0300 Subject: [PATCH 26/59] intrusive::list fixes namespace intrusive: removed split class into two: ilist and sized_ilist which has a size field. ilist no more NULLify pointers to bring a slignly better performance. As a consequence, fil_space_t::is_in_unflushed_spaces and fil_space_t::is_in_rotation_list boolean members are needed now. --- include/{intrusive_list.h => ilist.h} | 101 ++++++++++++++++++++------ storage/innobase/fil/fil0crypt.cc | 3 +- storage/innobase/fil/fil0fil.cc | 52 +++++-------- storage/innobase/include/dyn0buf.h | 8 +- storage/innobase/include/fil0fil.h | 22 +++--- 5 files changed, 114 insertions(+), 72 deletions(-) rename include/{intrusive_list.h => ilist.h} (68%) diff --git a/include/intrusive_list.h b/include/ilist.h similarity index 68% rename from include/intrusive_list.h rename to include/ilist.h index 23534596b93..a94f878f49b 100644 --- a/include/intrusive_list.h +++ b/include/ilist.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, MariaDB + Copyright (c) 2019, 2020, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -21,26 +21,28 @@ #include #include -namespace intrusive -{ - // Derive your class from this struct to insert to a linked list. -template struct list_node +template struct ilist_node { - list_node(list_node *next= NULL, list_node *prev= NULL) - : next(next), prev(prev) + ilist_node() + : +#ifndef DBUG_OFF + next(NULL), prev(NULL) +#endif { } - list_node *next; - list_node *prev; + ilist_node(ilist_node *next, ilist_node *prev) : next(next), prev(prev) {} + + ilist_node *next; + ilist_node *prev; }; // Modelled after std::list -template class list +template class ilist { public: - typedef list_node ListNode; + typedef ilist_node ListNode; class Iterator; // All containers in C++ should define these types to implement generic @@ -103,10 +105,10 @@ public: private: ListNode *node_; - friend class list; + friend class ilist; }; - list() : sentinel_(&sentinel_, &sentinel_), size_(0) {} + ilist() : sentinel_(&sentinel_, &sentinel_) {} reference front() { return *begin(); } reference back() { return *--end(); } @@ -127,14 +129,18 @@ public: reverse_iterator rend() { return reverse_iterator(begin()); } const_reverse_iterator rend() const { return reverse_iterator(begin()); } - bool empty() const { return size_ == 0; } - size_type size() const { return size_; } + bool empty() const { return sentinel_.next == &sentinel_; } + + // Not implemented because it's O(N) + // size_type size() const + // { + // return static_cast(std::distance(begin(), end())); + // } void clear() { sentinel_.next= &sentinel_; sentinel_.prev= &sentinel_; - size_= 0; } iterator insert(iterator pos, reference value) @@ -148,7 +154,6 @@ public: static_cast(value).prev= prev; static_cast(value).next= curr; - ++size_; return iterator(&value); } @@ -160,13 +165,12 @@ public: prev->next= next; next->prev= prev; - // This is not required for list functioning. But maybe it'll prevent bugs - // and ease debugging. +#ifndef DBUG_OFF ListNode *curr= pos.node_; curr->prev= NULL; curr->next= NULL; +#endif - --size_; return next; } @@ -177,12 +181,63 @@ public: void pop_front() { erase(begin()); } // STL version is O(n) but this is O(1) because an element can't be inserted - // several times in the same intrusive list. + // several times in the same ilist. void remove(reference value) { erase(iterator(&value)); } private: ListNode sentinel_; - size_type size_; }; -} // namespace intrusive +// Similar to ilist but also has O(1) size() method. +template class sized_ilist : public ilist +{ + typedef ilist BASE; + +public: + // All containers in C++ should define these types to implement generic + // container interface. + using typename BASE::const_iterator; + using typename BASE::const_pointer; + using typename BASE::const_reference; + using typename BASE::const_reverse_iterator; + using typename BASE::difference_type; + using typename BASE::iterator; + using typename BASE::pointer; + using typename BASE::reference; + using typename BASE::reverse_iterator; + using typename BASE::size_type; + using typename BASE::value_type; + + sized_ilist() : size_(0) {} + + size_type size() const { return size_; } + + void clear() + { + BASE::clear(); + size_= 0; + } + + iterator insert(iterator pos, reference value) + { + ++size_; + return BASE::insert(pos, value); + } + + iterator erase(iterator pos) + { + --size_; + return BASE::erase(pos); + } + + void push_back(reference value) { insert(BASE::end(), value); } + void pop_back() { erase(BASE::end()); } + + void push_front(reference value) { insert(BASE::begin(), value); } + void pop_front() { erase(BASE::begin()); } + + void remove(reference value) { erase(iterator(&value)); } + +private: + size_t size_; +}; diff --git a/storage/innobase/fil/fil0crypt.cc b/storage/innobase/fil/fil0crypt.cc index a28c9f63797..f06aa7606ee 100644 --- a/storage/innobase/fil/fil0crypt.cc +++ b/storage/innobase/fil/fil0crypt.cc @@ -2261,7 +2261,7 @@ static void fil_crypt_rotation_list_fill() space != NULL; space = UT_LIST_GET_NEXT(space_list, space)) { if (space->purpose != FIL_TYPE_TABLESPACE - || space->is_in_rotation_list() + || space->is_in_rotation_list || space->is_stopping() || UT_LIST_GET_LEN(space->chain) == 0) { continue; @@ -2306,6 +2306,7 @@ static void fil_crypt_rotation_list_fill() } fil_system->rotation_list.push_back(*space); + space->is_in_rotation_list = true; } } diff --git a/storage/innobase/fil/fil0fil.cc b/storage/innobase/fil/fil0fil.cc index d4b0d3f4377..bb7fc0d015f 100644 --- a/storage/innobase/fil/fil0fil.cc +++ b/storage/innobase/fil/fil0fil.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2017, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2014, 2019, MariaDB Corporation. +Copyright (c) 2014, 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 @@ -832,7 +832,7 @@ static void fil_flush_low(fil_space_t* space, bool metadata = false) /* No need to flush. User has explicitly disabled buffering. */ - ut_ad(!space->is_in_unflushed_spaces()); + ut_ad(!space->is_in_unflushed_spaces); ut_ad(fil_space_is_flushed(space)); ut_ad(space->n_pending_flushes == 0); @@ -895,10 +895,11 @@ static void fil_flush_low(fil_space_t* space, bool metadata = false) skip_flush: #endif /* _WIN32 */ if (!node->needs_flush) { - if (space->is_in_unflushed_spaces() + if (space->is_in_unflushed_spaces && fil_space_is_flushed(space)) { fil_system->unflushed_spaces.remove(*space); + space->is_in_unflushed_spaces = false; } } @@ -1193,13 +1194,14 @@ fil_node_close_to_free( if (fil_buffering_disabled(space)) { - ut_ad(!space->is_in_unflushed_spaces()); + ut_ad(!space->is_in_unflushed_spaces); ut_ad(fil_space_is_flushed(space)); - } else if (space->is_in_unflushed_spaces() + } else if (space->is_in_unflushed_spaces && fil_space_is_flushed(space)) { fil_system->unflushed_spaces.remove(*space); + space->is_in_unflushed_spaces = false; } fil_node_close_file(node); @@ -1226,16 +1228,18 @@ fil_space_detach( HASH_DELETE(fil_space_t, name_hash, fil_system->name_hash, ut_fold_string(space->name), space); - if (space->is_in_unflushed_spaces()) { + if (space->is_in_unflushed_spaces) { ut_ad(!fil_buffering_disabled(space)); fil_system->unflushed_spaces.remove(*space); + space->is_in_unflushed_spaces = false; } - if (space->is_in_rotation_list()) { + if (space->is_in_rotation_list) { fil_system->rotation_list.remove(*space); + space->is_in_rotation_list = false; } UT_LIST_REMOVE(fil_system->space_list, space); @@ -1462,6 +1466,7 @@ fil_space_create( /* Key rotation is not enabled, need to inform background encryption threads. */ fil_system->rotation_list.push_back(*space); + space->is_in_rotation_list = true; mutex_exit(&fil_system->mutex); mutex_enter(&fil_crypt_threads_mutex); os_event_set(fil_crypt_threads_event); @@ -4751,16 +4756,17 @@ fil_node_complete_io(fil_node_t* node, const IORequest& type) /* We don't need to keep track of unflushed changes as user has explicitly disabled buffering. */ - ut_ad(!node->space->is_in_unflushed_spaces()); + ut_ad(!node->space->is_in_unflushed_spaces); ut_ad(node->needs_flush == false); } else { node->needs_flush = true; - if (!node->space->is_in_unflushed_spaces()) { + if (!node->space->is_in_unflushed_spaces) { fil_system->unflushed_spaces.push_front( *node->space); + node->space->is_in_unflushed_spaces = true; } } } @@ -5247,7 +5253,7 @@ fil_flush_file_spaces( n_space_ids = 0; - for (intrusive::list::iterator it + for (sized_ilist::iterator it = fil_system->unflushed_spaces.begin(), end = fil_system->unflushed_spaces.end(); it != end; ++it) { @@ -5974,9 +5980,10 @@ fil_space_remove_from_keyrotation(fil_space_t* space) ut_ad(mutex_own(&fil_system->mutex)); ut_ad(space); - if (space->n_pending_ops == 0 && space->is_in_rotation_list()) { + if (space->n_pending_ops == 0 && space->is_in_rotation_list) { ut_a(!fil_system->rotation_list.empty()); fil_system->rotation_list.remove(*space); + space->is_in_rotation_list = false; } } @@ -6005,7 +6012,7 @@ fil_space_t *fil_system_t::keyrotate_next(fil_space_t *prev_space, don't remove the last processed tablespace from the rotation list. */ const bool remove= (!recheck || prev_space->crypt_data) && !key_version == !srv_encrypt_tables; - intrusive::list::iterator it= + sized_ilist::iterator it= prev_space == NULL ? fil_system->rotation_list.end() : prev_space; if (it == fil_system->rotation_list.end()) @@ -6117,24 +6124,3 @@ fil_space_set_punch_hole( { node->space->punch_hole = val; } - -/** Checks that this tablespace in a list of unflushed tablespaces. -@return true if in a list */ -bool fil_space_t::is_in_unflushed_spaces() const -{ - ut_ad(mutex_own(&fil_system->mutex)); - - return static_cast *>( - this) - ->next; -} - -/** Checks that this tablespace needs key rotation. -@return true if in a rotation list */ -bool fil_space_t::is_in_rotation_list() const -{ - ut_ad(mutex_own(&fil_system->mutex)); - - return static_cast *>(this) - ->next; -} diff --git a/storage/innobase/include/dyn0buf.h b/storage/innobase/include/dyn0buf.h index 311f6518943..b3302409e5f 100644 --- a/storage/innobase/include/dyn0buf.h +++ b/storage/innobase/include/dyn0buf.h @@ -29,7 +29,7 @@ Created 2013-03-16 Sunny Bains #include "mem0mem.h" #include "dyn0types.h" -#include "intrusive_list.h" +#include "ilist.h" /** Class that manages dynamic buffers. It uses a UT_LIST of @@ -43,7 +43,7 @@ template class dyn_buf_t { public: - class block_t : public intrusive::list_node<> { + class block_t : public ilist_node<> { public: block_t() @@ -153,7 +153,7 @@ public: /** SIZE - sizeof(m_node) + sizeof(m_used) */ enum { MAX_DATA_SIZE = SIZE - - sizeof(intrusive::list_node<>) + - sizeof(ilist_node<>) + sizeof(ib_uint32_t) }; @@ -167,7 +167,7 @@ public: friend class dyn_buf_t; }; - typedef intrusive::list list_t; + typedef sized_ilist list_t; enum { MAX_DATA_SIZE = block_t::MAX_DATA_SIZE}; diff --git a/storage/innobase/include/fil0fil.h b/storage/innobase/include/fil0fil.h index 9c722944665..62d87ce2c06 100644 --- a/storage/innobase/include/fil0fil.h +++ b/storage/innobase/include/fil0fil.h @@ -33,7 +33,7 @@ Created 10/25/1995 Heikki Tuuri #include "dict0types.h" #include "page0size.h" #include "ibuf0types.h" -#include "intrusive_list.h" +#include "ilist.h" #include @@ -82,8 +82,8 @@ fil_type_is_data( struct fil_node_t; /** Tablespace or log data space */ -struct fil_space_t : intrusive::list_node, - intrusive::list_node +struct fil_space_t : ilist_node, + ilist_node { ulint id; /*!< space id */ hash_node_t hash; /*!< hash chain node */ @@ -165,18 +165,18 @@ struct fil_space_t : intrusive::list_node, UT_LIST_NODE_T(fil_space_t) named_spaces; /*!< list of spaces for which MLOG_FILE_NAME records have been issued */ - /** Checks that this tablespace in a list of unflushed tablespaces. - @return true if in a list */ - bool is_in_unflushed_spaces() const; UT_LIST_NODE_T(fil_space_t) space_list; /*!< list of all spaces */ - /** Checks that this tablespace needs key rotation. - @return true if in a rotation list */ - bool is_in_rotation_list() const; /** MariaDB encryption data */ fil_space_crypt_t* crypt_data; + /** Checks that this tablespace in a list of unflushed tablespaces. */ + bool is_in_unflushed_spaces; + + /** Checks that this tablespace needs key rotation. */ + bool is_in_rotation_list; + /** True if the device this filespace is on supports atomic writes */ bool atomic_write_supported; @@ -507,7 +507,7 @@ struct fil_system_t { not put to this list: they are opened after the startup, and kept open until shutdown */ - intrusive::list unflushed_spaces; + sized_ilist unflushed_spaces; /*!< list of those tablespaces whose files contain unflushed writes; those spaces have @@ -530,7 +530,7 @@ struct fil_system_t { record has been written since the latest redo log checkpoint. Protected only by log_sys->mutex. */ - intrusive::list rotation_list; + ilist rotation_list; /*!< list of all file spaces needing key rotation.*/ From ed1434df88270ebe70ae412b5b723920b6b4e0e8 Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Wed, 27 May 2020 12:16:58 +0530 Subject: [PATCH 27/59] MDEV-21787 Alter table failure tries to access uninitialized column - Forget to record .result for instant_alter_limit 16k test case --- .../suite/innodb/r/instant_alter_limit,16k.rdiff | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 mysql-test/suite/innodb/r/instant_alter_limit,16k.rdiff diff --git a/mysql-test/suite/innodb/r/instant_alter_limit,16k.rdiff b/mysql-test/suite/innodb/r/instant_alter_limit,16k.rdiff new file mode 100644 index 00000000000..57d87f7172f --- /dev/null +++ b/mysql-test/suite/innodb/r/instant_alter_limit,16k.rdiff @@ -0,0 +1,13 @@ +--- instant_alter_limit.result 2020-05-26 18:01:27.377946439 +0530 ++++ instant_alter_limit,16k.reject 2020-05-26 20:14:38.452463919 +0530 +@@ -45,3 +45,10 @@ + instants + 502 + DROP TABLE t; ++# ++# MDEV-21787 Alter table failure tries to access uninitialized column ++# ++CREATE TABLE t1(f1 INT PRIMARY KEY, f2 TEXT GENERATED ALWAYS AS (SUBSTR(f4, 1, 400)), f3 VARCHAR(500), f4 TEXT)ENGINE=InnoDB ROW_FORMAT=Compact; ++ALTER TABLE t1 ADD UNIQUE KEY (f2(9)); ++ALTER TABLE t1 ADD COLUMN f5 TEXT, ALGORITHM=INPLACE; ++DROP TABLE t1; From 67496281eaa11098ad4482c2bb5aa5976b4f5cbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 27 May 2020 09:51:46 +0300 Subject: [PATCH 28/59] Fix the RelWithDebInfo build The build was broken in the parent commit 18d8f06f31cbe3913e1c80c0c5120f23e036bd3e. --- include/ilist.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ilist.h b/include/ilist.h index a94f878f49b..ae87b79d54e 100644 --- a/include/ilist.h +++ b/include/ilist.h @@ -25,8 +25,8 @@ template struct ilist_node { ilist_node() - : #ifndef DBUG_OFF + : next(NULL), prev(NULL) #endif { From bf1aa7569e7a29fae2b1cdf36b840e98130fda4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 27 May 2020 09:53:36 +0300 Subject: [PATCH 29/59] Add an end-of-test marker to ease merges --- mysql-test/r/partition_alter.result | 1 + mysql-test/t/partition_alter.test | 2 ++ 2 files changed, 3 insertions(+) diff --git a/mysql-test/r/partition_alter.result b/mysql-test/r/partition_alter.result index 8de3d4b45ad..699377751d1 100644 --- a/mysql-test/r/partition_alter.result +++ b/mysql-test/r/partition_alter.result @@ -165,3 +165,4 @@ select * from t1 partition (p0); k x 1 2000-01-01 00:00:00.000000 drop table t1; +# End of 10.2 tests diff --git a/mysql-test/t/partition_alter.test b/mysql-test/t/partition_alter.test index 3c783ebe375..b52e92445a8 100644 --- a/mysql-test/t/partition_alter.test +++ b/mysql-test/t/partition_alter.test @@ -151,3 +151,5 @@ insert into t1 (k, x) values (1, '2000-01-01 00:00'), (2, '2000-01-01 00:01'); alter table t1 drop key u, algorithm=inplace; select * from t1 partition (p0); drop table t1; + +--echo # End of 10.2 tests From 1b3adaab25c4f0e4288b261f973906bbf6b394c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 27 May 2020 10:58:28 +0300 Subject: [PATCH 30/59] Fix the build with GCC 4.1.2 The build was broken on CentOS 5 and CentOS 6 as a result of commit 18d8f06f31cbe3913e1c80c0c5120f23e036bd3e introducing some constructs that old GCC versions could not cope with. --- include/ilist.h | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/include/ilist.h b/include/ilist.h index ae87b79d54e..746061c397d 100644 --- a/include/ilist.h +++ b/include/ilist.h @@ -210,7 +210,7 @@ public: sized_ilist() : size_(0) {} - size_type size() const { return size_; } + std::size_t size() const { return size_; } void clear() { @@ -218,25 +218,30 @@ public: size_= 0; } - iterator insert(iterator pos, reference value) + typename ilist::iterator + insert(typename ilist::iterator pos, + typename ilist::reference value) { ++size_; return BASE::insert(pos, value); } - iterator erase(iterator pos) + typename ilist::iterator erase(typename ilist::iterator pos) { --size_; return BASE::erase(pos); } - void push_back(reference value) { insert(BASE::end(), value); } + void push_back(typename ilist::reference value) + { insert(BASE::end(), value); } void pop_back() { erase(BASE::end()); } - void push_front(reference value) { insert(BASE::begin(), value); } + void push_front(typename ilist::reference value) + { insert(BASE::begin(), value); } void pop_front() { erase(BASE::begin()); } - void remove(reference value) { erase(iterator(&value)); } + void remove(typename ilist::reference value) + { erase(iterator(&value)); } private: size_t size_; From 5139cfabb3bfc62be38e651ff176580e0aba09b8 Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Wed, 27 May 2020 12:59:27 +0300 Subject: [PATCH 31/59] fix compilation --- include/ilist.h | 41 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/include/ilist.h b/include/ilist.h index 746061c397d..5d0a425a9cc 100644 --- a/include/ilist.h +++ b/include/ilist.h @@ -196,21 +196,21 @@ template class sized_ilist : public ilist public: // All containers in C++ should define these types to implement generic // container interface. - using typename BASE::const_iterator; - using typename BASE::const_pointer; - using typename BASE::const_reference; - using typename BASE::const_reverse_iterator; - using typename BASE::difference_type; - using typename BASE::iterator; - using typename BASE::pointer; - using typename BASE::reference; - using typename BASE::reverse_iterator; - using typename BASE::size_type; - using typename BASE::value_type; + typedef T value_type; + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef value_type &reference; + typedef const value_type &const_reference; + typedef T *pointer; + typedef const T *const_pointer; + typedef typename BASE::Iterator iterator; + typedef const typename BASE::Iterator const_iterator; + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; sized_ilist() : size_(0) {} - std::size_t size() const { return size_; } + size_type size() const { return size_; } void clear() { @@ -218,31 +218,26 @@ public: size_= 0; } - typename ilist::iterator - insert(typename ilist::iterator pos, - typename ilist::reference value) + iterator insert(iterator pos, reference value) { ++size_; return BASE::insert(pos, value); } - typename ilist::iterator erase(typename ilist::iterator pos) + iterator erase(iterator pos) { --size_; return BASE::erase(pos); } - void push_back(typename ilist::reference value) - { insert(BASE::end(), value); } + void push_back(reference value) { insert(BASE::end(), value); } void pop_back() { erase(BASE::end()); } - void push_front(typename ilist::reference value) - { insert(BASE::begin(), value); } + void push_front(reference value) { insert(BASE::begin(), value); } void pop_front() { erase(BASE::begin()); } - void remove(typename ilist::reference value) - { erase(iterator(&value)); } + void remove(reference value) { erase(iterator(&value)); } private: - size_t size_; + size_type size_; }; From a50e6c9eb126cbb1d46735dade6c9006f1a5a764 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 13 May 2020 13:10:35 +0200 Subject: [PATCH 32/59] MDEV-17153 server crash on repair table ... use_frm data_file_length == 0 in mi_repair() is normal for REPAIR ... USE_FRM. But in-file links (for blocks and deleted chain) must be compared with the real file length to avoid spurious "link points outside datafile" warnings and arbitrary block skipping. --- mysql-test/r/repair.result | 21 +++++++++++++++++++++ mysql-test/t/repair.test | 21 +++++++++++++++++++++ storage/myisam/mi_check.c | 6 ++++++ 3 files changed, 48 insertions(+) diff --git a/mysql-test/r/repair.result b/mysql-test/r/repair.result index 171ef836bda..749b8a74b4f 100644 --- a/mysql-test/r/repair.result +++ b/mysql-test/r/repair.result @@ -233,3 +233,24 @@ i UNLOCK TABLES; DROP TABLE t1; # End of 10.0 tests +create table t1 (a int, b varchar(200)); +insert t1 select seq, repeat(200, seq) from seq_1_to_30; +delete from t1 where a % 13 = 0; +repair table t1 use_frm; +Table Op Msg_type Msg_text +test.t1 repair warning Number of rows changed from 0 to 28 +test.t1 repair status OK +delete from t1 where a % 11 = 0; +repair table t1 extended use_frm; +Table Op Msg_type Msg_text +test.t1 repair warning Number of rows changed from 0 to 26 +test.t1 repair status OK +delete from t1 where a % 7 = 0; +set myisam_repair_threads = 2; +repair table t1 use_frm; +Table Op Msg_type Msg_text +test.t1 repair warning Number of rows changed from 0 to 22 +test.t1 repair status OK +set myisam_repair_threads = default; +drop table t1; +# End of 10.2 tests diff --git a/mysql-test/t/repair.test b/mysql-test/t/repair.test index 75978ac482b..435eb99683a 100644 --- a/mysql-test/t/repair.test +++ b/mysql-test/t/repair.test @@ -1,6 +1,7 @@ # # Test of repair table # +--source include/have_sequence.inc --source include/default_charset.inc call mtr.add_suppression("character set is multi-byte"); @@ -246,3 +247,23 @@ UNLOCK TABLES; DROP TABLE t1; --echo # End of 10.0 tests + +# +# MDEV-17153 server crash on repair table ... use_frm +# +# Note, this test case doesn't crash, but shows spurios warnings +# unless the bug is fixed +# +create table t1 (a int, b varchar(200)); +insert t1 select seq, repeat(200, seq) from seq_1_to_30; +delete from t1 where a % 13 = 0; +repair table t1 use_frm; +delete from t1 where a % 11 = 0; +repair table t1 extended use_frm; +delete from t1 where a % 7 = 0; +set myisam_repair_threads = 2; +repair table t1 use_frm; +set myisam_repair_threads = default; +drop table t1; + +--echo # End of 10.2 tests diff --git a/storage/myisam/mi_check.c b/storage/myisam/mi_check.c index b18ffb99a11..d9b9bb5af4a 100644 --- a/storage/myisam/mi_check.c +++ b/storage/myisam/mi_check.c @@ -1586,6 +1586,8 @@ int mi_repair(HA_CHECK *param, register MI_INFO *info, sort_param.filepos=new_header_length; param->read_cache.end_of_file=sort_info.filelength= mysql_file_seek(info->dfile, 0L, MY_SEEK_END, MYF(0)); + if (info->state->data_file_length == 0) + info->state->data_file_length= sort_info.filelength; sort_info.dupp=0; sort_param.fix_datafile= (my_bool) (! rep_quick); sort_param.master=1; @@ -2290,6 +2292,8 @@ int mi_repair_by_sort(HA_CHECK *param, register MI_INFO *info, sort_info.buff=0; param->read_cache.end_of_file=sort_info.filelength= mysql_file_seek(param->read_cache.file, 0L, MY_SEEK_END, MYF(0)); + if (info->state->data_file_length == 0) + info->state->data_file_length= sort_info.filelength; sort_param.wordlist=NULL; init_alloc_root(&sort_param.wordroot, FTPARSER_MEMROOT_ALLOC_SIZE, 0, @@ -2757,6 +2761,8 @@ int mi_repair_parallel(HA_CHECK *param, register MI_INFO *info, sort_info.buff=0; param->read_cache.end_of_file=sort_info.filelength= mysql_file_seek(param->read_cache.file, 0L, MY_SEEK_END, MYF(0)); + if (info->state->data_file_length == 0) + info->state->data_file_length= sort_info.filelength; if (share->data_file_type == DYNAMIC_RECORD) rec_length=MY_MAX(share->base.min_pack_length+1,share->base.min_block_length); From 39c141b4ae9e6381f9efd4c566e61b91371a3d63 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 13 May 2020 19:48:23 +0200 Subject: [PATCH 33/59] don't include .git files in source packages the existing ".git/" rule only filters out .git directories, but as we have submodules now, we have to filter out .git files too. --- cmake/cpack_source_ignore_files.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/cpack_source_ignore_files.cmake b/cmake/cpack_source_ignore_files.cmake index 5b103501a99..eb1ec191d17 100644 --- a/cmake/cpack_source_ignore_files.cmake +++ b/cmake/cpack_source_ignore_files.cmake @@ -15,6 +15,7 @@ SET(CPACK_SOURCE_IGNORE_FILES \\\\.git/ +\\\\.git$ \\\\.gitignore$ \\\\.gitattributes$ CMakeCache\\\\.txt$ From b01c8a6cc8b361c6c8ecd887f1958c1f0b2e9811 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Thu, 14 May 2020 16:18:01 +0200 Subject: [PATCH 34/59] MDEV-22558 wrong error for invalid utf8 table comment --- mysql-test/r/comment_table.result | 5 ++++- mysql-test/t/comment_table.test | 11 ++++++++--- sql/sql_table.cc | 19 +++++++++++++++++++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/comment_table.result b/mysql-test/r/comment_table.result index 99ecb04a362..d0af71b4540 100644 --- a/mysql-test/r/comment_table.result +++ b/mysql-test/r/comment_table.result @@ -1,4 +1,3 @@ -DROP TABLE IF EXISTS t1; create table t1 (c1 VARCHAR(10) NOT NULL COMMENT 'c1 comment', c2 INTEGER,c3 INTEGER COMMENT '012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789', c4 INTEGER, c5 INTEGER, c6 INTEGER, c7 INTEGER, INDEX i1 (c1) COMMENT 'i1 comment',INDEX i2(c2) ) COMMENT='abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcd'; SELECT table_comment,char_length(table_comment) FROM information_schema.tables WHERE table_name='t1'; @@ -125,3 +124,7 @@ SELECT table_comment,char_length(table_comment) FROM information_schema.tables W table_comment char_length(table_comment) SELECT column_comment,char_length(column_comment) FROM information_schema.columns WHERE table_name='t1'; column_comment char_length(column_comment) +set names utf8; +create table t1 (x int comment 'a'); +ERROR HY000: Invalid utf8 character string: 'a' +set names latin1; diff --git a/mysql-test/t/comment_table.test b/mysql-test/t/comment_table.test index 2a85fbac571..db8bdf54ba6 100644 --- a/mysql-test/t/comment_table.test +++ b/mysql-test/t/comment_table.test @@ -1,6 +1,3 @@ ---disable_warnings -DROP TABLE IF EXISTS t1; ---enable_warnings #1024 bytes create table t1 (c1 VARCHAR(10) NOT NULL COMMENT 'c1 comment', c2 INTEGER,c3 INTEGER COMMENT '012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789', c4 INTEGER, c5 INTEGER, c6 INTEGER, c7 INTEGER, INDEX i1 (c1) COMMENT 'i1 comment',INDEX i2(c2) ) COMMENT='abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcd'; @@ -53,3 +50,11 @@ create table t1 (c1 VARCHAR(10) NOT NULL COMMENT 'c1 comment', c2 INTEGER,c3 INT ) COMMENT='abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcde'; SELECT table_comment,char_length(table_comment) FROM information_schema.tables WHERE table_name='t1'; SELECT column_comment,char_length(column_comment) FROM information_schema.columns WHERE table_name='t1'; + +# +# MDEV-22558 wrong error for invalid utf8 table comment +# +set names utf8; +--error ER_INVALID_CHARACTER_STRING +create table t1 (x int comment 'a'); +set names latin1; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 8d81911bf29..87d84b1abc6 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4294,6 +4294,25 @@ bool validate_comment_length(THD *thd, LEX_STRING *comment, size_t max_len, Well_formed_prefix(system_charset_info, *comment, max_len).length(); if (tmp_len < comment->length) { +#if MARIADB_VERSION_ID < 100500 + if (comment->length <= max_len) + { + if (thd->is_strict_mode()) + { + my_error(ER_INVALID_CHARACTER_STRING, MYF(0), + system_charset_info->csname, comment->str); + DBUG_RETURN(true); + } + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_INVALID_CHARACTER_STRING, + ER_THD(thd, ER_INVALID_CHARACTER_STRING), + system_charset_info->csname, comment->str); + comment->length= tmp_len; + DBUG_RETURN(false); + } +#else +#error do it in TEXT_STRING_sys +#endif if (thd->is_strict_mode()) { my_error(err_code, MYF(0), name, static_cast(max_len)); From 1e951155bddd42b84e1c4ed949a85d62829e696f Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 20 May 2020 17:02:47 +0200 Subject: [PATCH 35/59] bugfix: use THD::main_mem_root for kill error message cannot use the current THD::mem_root, because it can be temporarily reassigned to something with a very different life time (e.g. to TABLE::mem_root or range optimizer mem_root). --- sql/sql_class.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sql/sql_class.h b/sql/sql_class.h index 80aa5710b62..838998af94f 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -3729,7 +3729,8 @@ public: The worst things that can happen is that we get a suboptimal error message. */ - if ((killed_err= (err_info*) alloc(sizeof(*killed_err)))) + killed_err= (err_info*) alloc_root(&main_mem_root, sizeof(*killed_err)); + if (killed_err) { killed_err->no= killed_errno_arg; ::strmake((char*) killed_err->msg, killed_err_msg_arg, From ad772478660a67c1c93c22725e25b168ec512f4d Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 20 May 2020 17:04:22 +0200 Subject: [PATCH 36/59] MDEV-21958 Query having many NOT-IN clauses running forever and causing available free memory to use completely let thd->killed to abort range optimizer --- sql/opt_range.h | 1 + 1 file changed, 1 insertion(+) diff --git a/sql/opt_range.h b/sql/opt_range.h index dff50252e1a..1e6aca540d8 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -656,6 +656,7 @@ public: bool statement_should_be_aborted() const { return + thd->killed || thd->is_fatal_error || thd->is_error() || alloced_sel_args > SEL_ARG::MAX_SEL_ARGS; From 6af37ba881fee7e6f651d5e0730c9374337ad1b4 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Thu, 21 May 2020 01:31:17 +0200 Subject: [PATCH 37/59] fix rocksdb zstd detection WITH_ROCKSDB_ZSTD must use the same capitalization as in the foreach loop --- storage/rocksdb/build_rocksdb.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/rocksdb/build_rocksdb.cmake b/storage/rocksdb/build_rocksdb.cmake index ca61842fbb8..3f3dca7e990 100644 --- a/storage/rocksdb/build_rocksdb.cmake +++ b/storage/rocksdb/build_rocksdb.cmake @@ -67,7 +67,7 @@ if(SNAPPY_FOUND AND (NOT WITH_ROCKSDB_snappy STREQUAL "OFF")) endif() include(CheckFunctionExists) -if(ZSTD_FOUND AND (NOT WITH_ROCKSDB_zstd STREQUAL "OFF")) +if(ZSTD_FOUND AND (NOT WITH_ROCKSDB_ZSTD STREQUAL "OFF")) SET(CMAKE_REQUIRED_LIBRARIES zstd) CHECK_FUNCTION_EXISTS(ZDICT_trainFromBuffer ZSTD_VALID) UNSET(CMAKE_REQUIRED_LIBRARIES) From cceb965a7918c1e3a29965fe0b440cf051764358 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Fri, 22 May 2020 12:05:53 +0200 Subject: [PATCH 38/59] Revert "MDEV-12445 : Rocksdb does not shutdown worker threads and aborts in memleak check on server shutdown" This reverts commit 6f1f9114971c3c158e9ac97313c92a24f6554415. because it doesn't do anything now (the server doesn't check my_disable_leak_check) and it never did anything before (because without `extern` it simply created a local instance of my_disable_leak_check, did not affect server's my_disable_leak_check). --- sql/mysqld.cc | 1 - storage/rocksdb/ha_rocksdb.cc | 8 -------- 2 files changed, 9 deletions(-) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 5878f4a2286..e40d5c38233 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -365,7 +365,6 @@ static bool volatile select_thread_in_use, signal_thread_in_use; static volatile bool ready_to_exit; static my_bool opt_debugging= 0, opt_external_locking= 0, opt_console= 0; static my_bool opt_short_log_format= 0, opt_silent_startup= 0; -bool my_disable_leak_check= false; uint kill_cached_threads; static uint wake_thread; diff --git a/storage/rocksdb/ha_rocksdb.cc b/storage/rocksdb/ha_rocksdb.cc index bf9183d7dea..5cbe4c162a8 100644 --- a/storage/rocksdb/ha_rocksdb.cc +++ b/storage/rocksdb/ha_rocksdb.cc @@ -114,7 +114,6 @@ int thd_binlog_format(const MYSQL_THD thd); bool thd_binlog_filter_ok(const MYSQL_THD thd); } -MYSQL_PLUGIN_IMPORT bool my_disable_leak_check; extern my_bool opt_core_file; // Needed in rocksdb_init_func @@ -5688,13 +5687,6 @@ static int rocksdb_init_func(void *const p) { } #endif - /** - Rocksdb does not always shutdown its threads, when - plugin is shut down. Disable server's leak check - at exit to avoid crash. - */ - my_disable_leak_check = true; - err = my_error_register(rdb_get_error_messages, HA_ERR_ROCKSDB_FIRST, HA_ERR_ROCKSDB_LAST); if (err != 0) { From 8cf589218f1b6ac5f8775068873357bd0dc8acbb Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Fri, 22 May 2020 19:50:22 +0200 Subject: [PATCH 39/59] optimize performance of the build in a fresh clone don't fetch the complete history of all submodules, it's rarely needed --- cmake/submodules.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/submodules.cmake b/cmake/submodules.cmake index 34d1f37c956..6e039bfeae3 100644 --- a/cmake/submodules.cmake +++ b/cmake/submodules.cmake @@ -19,11 +19,11 @@ IF(GIT_EXECUTABLE AND EXISTS "${CMAKE_SOURCE_DIR}/.git") SET(update_result 0) ELSEIF (cmake_update_submodules MATCHES force) MESSAGE(STATUS "Updating submodules (forced)") - EXECUTE_PROCESS(COMMAND "${GIT_EXECUTABLE}" submodule update --init --force + EXECUTE_PROCESS(COMMAND "${GIT_EXECUTABLE}" submodule update --init --force --depth=1 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" RESULT_VARIABLE update_result) ELSEIF (cmake_update_submodules MATCHES yes) - EXECUTE_PROCESS(COMMAND "${GIT_EXECUTABLE}" submodule update --init + EXECUTE_PROCESS(COMMAND "${GIT_EXECUTABLE}" submodule update --init --depth=1 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" RESULT_VARIABLE update_result) ELSE() From 04726f292088cf00c1ac9ed7543535e7a091810a Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Sat, 23 May 2020 18:35:42 +0200 Subject: [PATCH 40/59] get rid of cmake warning --- cmake/build_configurations/mysql_release.cmake | 2 +- .../tokudb/PerconaFT/cmake_modules/TokuFeatureDetection.cmake | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/build_configurations/mysql_release.cmake b/cmake/build_configurations/mysql_release.cmake index b19a9437495..1609c45a632 100644 --- a/cmake/build_configurations/mysql_release.cmake +++ b/cmake/build_configurations/mysql_release.cmake @@ -136,7 +136,7 @@ IF(UNIX) RedHat/Fedora/Oracle Linux: yum install libaio-devel SuSE: zypper install libaio-devel - If you really do not want it, pass -DIGNORE_AIO_CHECK to cmake. + If you really do not want it, pass -DIGNORE_AIO_CHECK=ON to cmake. ") ENDIF() diff --git a/storage/tokudb/PerconaFT/cmake_modules/TokuFeatureDetection.cmake b/storage/tokudb/PerconaFT/cmake_modules/TokuFeatureDetection.cmake index 2f04a33558a..88210868eda 100644 --- a/storage/tokudb/PerconaFT/cmake_modules/TokuFeatureDetection.cmake +++ b/storage/tokudb/PerconaFT/cmake_modules/TokuFeatureDetection.cmake @@ -134,4 +134,4 @@ static __thread int tlsvar = 0; int main(void) { return tlsvar; }" HAVE_GNU_TLS) ## set TOKUDB_REVISION -set(CMAKE_TOKUDB_REVISION 0 CACHE INTEGER "Revision of tokudb.") +set(CMAKE_TOKUDB_REVISION 0 CACHE STRING "Revision of tokudb.") From e64dc07125c8143e27565947e71db76d597dfaf8 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Sun, 24 May 2020 21:10:41 +0200 Subject: [PATCH 41/59] assert(a && b); -> assert(a); assert(b); --- mysys/my_bitmap.c | 56 +++++++++++++++++++++++++++------------------ sql/ha_partition.cc | 17 ++++++++------ 2 files changed, 44 insertions(+), 29 deletions(-) diff --git a/mysys/my_bitmap.c b/mysys/my_bitmap.c index c79c5a2d324..f857101af02 100644 --- a/mysys/my_bitmap.c +++ b/mysys/my_bitmap.c @@ -255,7 +255,8 @@ my_bool bitmap_fast_test_and_set(MY_BITMAP *map, uint bitmap_bit) my_bool bitmap_test_and_set(MY_BITMAP *map, uint bitmap_bit) { my_bool res; - DBUG_ASSERT(map->bitmap && bitmap_bit < map->n_bits); + DBUG_ASSERT(map->bitmap); + DBUG_ASSERT(bitmap_bit < map->n_bits); bitmap_lock(map); res= bitmap_fast_test_and_set(map, bitmap_bit); bitmap_unlock(map); @@ -288,7 +289,8 @@ my_bool bitmap_fast_test_and_clear(MY_BITMAP *map, uint bitmap_bit) my_bool bitmap_test_and_clear(MY_BITMAP *map, uint bitmap_bit) { my_bool res; - DBUG_ASSERT(map->bitmap && bitmap_bit < map->n_bits); + DBUG_ASSERT(map->bitmap); + DBUG_ASSERT(bitmap_bit < map->n_bits); bitmap_lock(map); res= bitmap_fast_test_and_clear(map, bitmap_bit); bitmap_unlock(map); @@ -317,8 +319,8 @@ void bitmap_set_prefix(MY_BITMAP *map, uint prefix_size) uint prefix_bytes, prefix_bits, d; uchar *m= (uchar *)map->bitmap; - DBUG_ASSERT(map->bitmap && - (prefix_size <= map->n_bits || prefix_size == (uint) ~0)); + DBUG_ASSERT(map->bitmap); + DBUG_ASSERT(prefix_size <= map->n_bits || prefix_size == (uint) ~0); set_if_smaller(prefix_size, map->n_bits); if ((prefix_bytes= prefix_size / 8)) memset(m, 0xff, prefix_bytes); @@ -340,7 +342,8 @@ my_bool bitmap_is_prefix(const MY_BITMAP *map, uint prefix_size) uchar *m= (uchar*) map->bitmap; uchar *end_prefix= m+(prefix_size-1)/8; uchar *end; - DBUG_ASSERT(m && prefix_size <= map->n_bits); + DBUG_ASSERT(m); + DBUG_ASSERT(prefix_size <= map->n_bits); /* Empty prefix is always true */ if (!prefix_size) @@ -393,8 +396,8 @@ my_bool bitmap_is_subset(const MY_BITMAP *map1, const MY_BITMAP *map2) { my_bitmap_map *m1= map1->bitmap, *m2= map2->bitmap, *end; - DBUG_ASSERT(map1->bitmap && map2->bitmap && - map1->n_bits==map2->n_bits); + DBUG_ASSERT(map1->bitmap && map2->bitmap); + DBUG_ASSERT(map1->n_bits==map2->n_bits); end= map1->last_word_ptr; while (m1 < end) @@ -412,8 +415,9 @@ my_bool bitmap_is_overlapping(const MY_BITMAP *map1, const MY_BITMAP *map2) { my_bitmap_map *m1= map1->bitmap, *m2= map2->bitmap, *end; - DBUG_ASSERT(map1->bitmap && map2->bitmap && - map1->n_bits==map2->n_bits); + DBUG_ASSERT(map1->bitmap); + DBUG_ASSERT(map2->bitmap); + DBUG_ASSERT(map1->n_bits==map2->n_bits); end= map1->last_word_ptr; while (m1 < end) @@ -431,7 +435,8 @@ void bitmap_intersect(MY_BITMAP *map, const MY_BITMAP *map2) my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end; uint len= no_words_in_map(map), len2 = no_words_in_map(map2); - DBUG_ASSERT(map->bitmap && map2->bitmap); + DBUG_ASSERT(map->bitmap); + DBUG_ASSERT(map2->bitmap); end= to+MY_MIN(len,len2); while (to < end) @@ -476,7 +481,8 @@ my_bool bitmap_exists_intersection(const MY_BITMAP **bitmap_array, uint i, j, start_idx, end_idx; my_bitmap_map cur_res; - DBUG_ASSERT(bitmap_count && end_bit >= start_bit); + DBUG_ASSERT(bitmap_count); + DBUG_ASSERT(end_bit >= start_bit); for (j= 0; j < bitmap_count; j++) DBUG_ASSERT(end_bit < bitmap_array[j]->n_bits); @@ -504,8 +510,9 @@ my_bool bitmap_union_is_set_all(const MY_BITMAP *map1, const MY_BITMAP *map2) { my_bitmap_map *m1= map1->bitmap, *m2= map2->bitmap, *end; - DBUG_ASSERT(map1->bitmap && map2->bitmap && - map1->n_bits==map2->n_bits); + DBUG_ASSERT(map1->bitmap); + DBUG_ASSERT(map2->bitmap); + DBUG_ASSERT(map1->n_bits==map2->n_bits); end= map1->last_word_ptr; while ( m1 < end) if ((*m1++ | *m2++) != 0xFFFFFFFF) @@ -550,8 +557,9 @@ void bitmap_set_above(MY_BITMAP *map, uint from_byte, uint use_bit) void bitmap_subtract(MY_BITMAP *map, const MY_BITMAP *map2) { my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end; - DBUG_ASSERT(map->bitmap && map2->bitmap && - map->n_bits==map2->n_bits); + DBUG_ASSERT(map->bitmap); + DBUG_ASSERT(map2->bitmap); + DBUG_ASSERT(map->n_bits==map2->n_bits); end= map->last_word_ptr; @@ -564,8 +572,9 @@ void bitmap_union(MY_BITMAP *map, const MY_BITMAP *map2) { my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end; - DBUG_ASSERT(map->bitmap && map2->bitmap && - map->n_bits==map2->n_bits); + DBUG_ASSERT(map->bitmap); + DBUG_ASSERT(map2->bitmap); + DBUG_ASSERT(map->n_bits == map2->n_bits); end= map->last_word_ptr; while (to <= end) @@ -576,8 +585,9 @@ void bitmap_union(MY_BITMAP *map, const MY_BITMAP *map2) void bitmap_xor(MY_BITMAP *map, const MY_BITMAP *map2) { my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end= map->last_word_ptr; - DBUG_ASSERT(map->bitmap && map2->bitmap && - map->n_bits==map2->n_bits); + DBUG_ASSERT(map->bitmap); + DBUG_ASSERT(map2->bitmap); + DBUG_ASSERT(map->n_bits == map2->n_bits); while (to <= end) *to++ ^= *from++; } @@ -614,8 +624,9 @@ void bitmap_copy(MY_BITMAP *map, const MY_BITMAP *map2) { my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end; - DBUG_ASSERT(map->bitmap && map2->bitmap && - map->n_bits==map2->n_bits); + DBUG_ASSERT(map->bitmap); + DBUG_ASSERT(map2->bitmap); + DBUG_ASSERT(map->n_bits == map2->n_bits); end= map->last_word_ptr; while (to <= end) @@ -744,7 +755,8 @@ uint bitmap_lock_set_next(MY_BITMAP *map) void bitmap_lock_clear_bit(MY_BITMAP *map, uint bitmap_bit) { bitmap_lock(map); - DBUG_ASSERT(map->bitmap && bitmap_bit < map->n_bits); + DBUG_ASSERT(map->bitmap); + DBUG_ASSERT(bitmap_bit < map->n_bits); bitmap_clear_bit(map, bitmap_bit); bitmap_unlock(map); } diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 15e3a765075..453a1c9ff89 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -2183,7 +2183,8 @@ void ha_partition::update_create_info(HA_CREATE_INFO *create_info) sub_elem= subpart_it++; DBUG_ASSERT(sub_elem); part= i * num_subparts + j; - DBUG_ASSERT(part < m_file_tot_parts && m_file[part]); + DBUG_ASSERT(part < m_file_tot_parts); + DBUG_ASSERT(m_file[part]); dummy_info.data_file_name= dummy_info.index_file_name = NULL; m_file[part]->update_create_info(&dummy_info); sub_elem->data_file_name = (char*) dummy_info.data_file_name; @@ -3770,7 +3771,8 @@ int ha_partition::external_lock(THD *thd, int lock_type) MY_BITMAP *used_partitions; DBUG_ENTER("ha_partition::external_lock"); - DBUG_ASSERT(!auto_increment_lock && !auto_increment_safe_stmt_log_lock); + DBUG_ASSERT(!auto_increment_lock); + DBUG_ASSERT(!auto_increment_safe_stmt_log_lock); if (lock_type == F_UNLCK) used_partitions= &m_locked_partitions; @@ -4034,8 +4036,8 @@ void ha_partition::unlock_row() bool ha_partition::was_semi_consistent_read() { DBUG_ENTER("ha_partition::was_semi_consistent_read"); - DBUG_ASSERT(m_last_part < m_tot_parts && - bitmap_is_set(&(m_part_info->read_partitions), m_last_part)); + DBUG_ASSERT(m_last_part < m_tot_parts); + DBUG_ASSERT(bitmap_is_set(&(m_part_info->read_partitions), m_last_part)); DBUG_RETURN(m_file[m_last_part]->was_semi_consistent_read()); } @@ -5910,8 +5912,8 @@ int ha_partition::partition_scan_set_up(uchar * buf, bool idx_read_flag) DBUG_ASSERT(m_part_spec.start_part < m_tot_parts); m_ordered_scan_ongoing= m_ordered; } - DBUG_ASSERT(m_part_spec.start_part < m_tot_parts && - m_part_spec.end_part < m_tot_parts); + DBUG_ASSERT(m_part_spec.start_part < m_tot_parts); + DBUG_ASSERT(m_part_spec.end_part < m_tot_parts); DBUG_RETURN(0); } @@ -8653,7 +8655,8 @@ void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment, DBUG_PRINT("info", ("offset: %lu inc: %lu desired_values: %lu " "first_value: %lu", (ulong) offset, (ulong) increment, (ulong) nb_desired_values, (ulong) *first_value)); - DBUG_ASSERT(increment && nb_desired_values); + DBUG_ASSERT(increment); + DBUG_ASSERT(nb_desired_values); *first_value= 0; if (table->s->next_number_keypart) { From 9fd8f1b26406747f82913cf97034d9a4eb3d0a2e Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Sun, 24 May 2020 17:30:57 +0200 Subject: [PATCH 42/59] mtr: update titlebar when the test ends, not when it starts otherwise it reaches "0 tests left" state and then waits for a few minutes for all workers to complete their tests. show failures. account for retries. --- mysql-test/lib/mtr_report.pm | 60 +++++++++++++++++++++++++++--------- mysql-test/mysql-test-run.pl | 50 +----------------------------- 2 files changed, 46 insertions(+), 64 deletions(-) diff --git a/mysql-test/lib/mtr_report.pm b/mysql-test/lib/mtr_report.pm index e553b0305b4..a58fb369a13 100644 --- a/mysql-test/lib/mtr_report.pm +++ b/mysql-test/lib/mtr_report.pm @@ -37,8 +37,20 @@ use My::Platform; use POSIX qw[ _exit ]; use IO::Handle qw[ flush ]; use mtr_results; - use Term::ANSIColor; +use English; + +my $tot_real_time= 0; +my $tests_done= 0; +my $tests_failed= 0; + +our $timestamp= 0; +our $timediff= 0; +our $name; +our $verbose; +our $verbose_restart= 0; +our $timer= 1; +our $tests_total; my %color_map = qw/pass green retry-pass green @@ -47,20 +59,39 @@ my %color_map = qw/pass green disabled bright_black skipped yellow reset reset/; -sub xterm_color { - if (-t STDOUT and defined $ENV{TERM} and $ENV{TERM} =~ /xterm/) { - syswrite STDOUT, color($color_map{$_[0]}); + +my $set_titlebar; +my $set_color= sub { }; + +if (-t STDOUT) { + if (IS_WINDOWS) { + eval { + require Win32::Console; + $set_titlebar = sub { &Win32::Console::Title($_[0]);}; + } + } elsif ($ENV{TERM} =~ /xterm/) { + $set_titlebar = sub { syswrite STDOUT, "\e]0;$_[0]\a"; }; + $set_color = sub { syswrite STDOUT, color($color_map{$_[0]}); } } } -my $tot_real_time= 0; +sub titlebar_stat($) { -our $timestamp= 0; -our $timediff= 0; -our $name; -our $verbose; -our $verbose_restart= 0; -our $timer= 1; + sub time_format($) { + sprintf '%d:%02d:%02d', $_[0]/3600, ($_[0]/60)%60, $_[0]%60; + } + + $tests_done++; + $tests_failed++ if $_[0] =~ /fail/; + $tests_total++ if $_[0] =~ /retry/; + + my $spent = time - $BASETIME; + my $left = $tests_total - $tests_done; + + &$set_titlebar(sprintf "mtr: spent %s on %d tests. %s (%d tests) left, %d failed", + time_format($spent), $tests_done, + time_format($spent/$tests_done * $left), $left, $tests_failed); +} sub report_option { my ($opt, $value)= @_; @@ -321,8 +352,6 @@ sub mtr_report_stats ($$$$) { if ( $timer ) { - use English; - mtr_report("Spent", sprintf("%.3f", $tot_real_time),"of", time - $BASETIME, "seconds executing testcases"); } @@ -620,10 +649,11 @@ sub mtr_report (@) { my @s = split /\[ (\S+) \]/, _name() . "@_\n"; if (@s > 1) { print $s[0]; - xterm_color($s[1]); + &$set_color($s[1]); print "[ $s[1] ]"; - xterm_color('reset'); + &$set_color('reset'); print $s[2]; + titlebar_stat($s[1]) if $set_titlebar; } else { print $s[0]; } diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index d229390cbb5..29294590ae3 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -369,32 +369,6 @@ my $opt_stop_keep_alive= $ENV{MTR_STOP_KEEP_ALIVE}; select(STDOUT); $| = 1; # Automatically flush STDOUT -my $set_titlebar; - - - BEGIN { - if (IS_WINDOWS) { - my $have_win32_console= 0; - eval { - require Win32::Console; - Win32::Console->import(); - $have_win32_console = 1; - }; - eval 'sub HAVE_WIN32_CONSOLE { $have_win32_console }'; - } else { - eval 'sub HAVE_WIN32_CONSOLE { 0 }'; - } -} - -if (-t STDOUT) { - if (IS_WINDOWS and HAVE_WIN32_CONSOLE) { - $set_titlebar = sub {Win32::Console::Title $_[0];}; - } elsif (defined $ENV{TERM} and $ENV{TERM} =~ /xterm/) { - $set_titlebar = sub { syswrite STDOUT, "\e];$_[0]\a"; }; - } -} - - main(); sub have_wsrep() { @@ -632,7 +606,7 @@ sub main { } ####################################################################### - my $num_tests= @$tests; + my $num_tests= $mtr_report::tests_total= @$tests; if ( $opt_parallel eq "auto" ) { # Try to find a suitable value for number of workers if (IS_WINDOWS) @@ -1073,8 +1047,6 @@ sub run_test_server ($$$) { delete $next->{reserved}; } - titlebar_stat(scalar(@$tests)) if $set_titlebar; - if ($next) { # We don't need this any more delete $next->{criteria}; @@ -6666,23 +6638,3 @@ sub list_options ($) { exit(1); } - -sub time_format($) { - sprintf '%d:%02d:%02d', $_[0]/3600, ($_[0]/60)%60, $_[0]%60; -} - -our $num_tests; - -sub titlebar_stat { - my ($left) = @_; - - # 2.5 -> best by test - $num_tests = $left + 2.5 unless $num_tests; - - my $done = $num_tests - $left; - my $spent = time - $^T; - - &$set_titlebar(sprintf "mtr: spent %s on %d tests. %s (%d tests) left", - time_format($spent), $done, - time_format($spent/$done * $left), $left); -} From 2c9c9acbfc8740dfacc2c0ce88717dfa203b479c Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 26 May 2020 12:11:24 +0200 Subject: [PATCH 43/59] bintars should use bundled PCRE --- cmake/build_configurations/mysql_release.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/build_configurations/mysql_release.cmake b/cmake/build_configurations/mysql_release.cmake index 1609c45a632..9d3785e152b 100644 --- a/cmake/build_configurations/mysql_release.cmake +++ b/cmake/build_configurations/mysql_release.cmake @@ -104,6 +104,7 @@ ELSEIF(DEB) SET(HAVE_EMBEDDED_PRIVILEGE_CONTROL ON) ELSE() SET(WITH_SSL bundled CACHE STRING "") + SET(WITH_PCRE bundled CACHE STRING "") SET(WITH_ZLIB bundled CACHE STRING "") SET(WITH_JEMALLOC static CACHE STRING "") ENDIF() From 8afcc37c685a0b78c3da2b8aa2e5fc70e33e9787 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 26 May 2020 14:11:41 +0200 Subject: [PATCH 44/59] update C/C --- libmariadb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libmariadb b/libmariadb index cdfecebc993..ce74fd0c400 160000 --- a/libmariadb +++ b/libmariadb @@ -1 +1 @@ -Subproject commit cdfecebc9932a0dd5516c10505bfe78d79132e7d +Subproject commit ce74fd0c4009ed9f4bcbdb4a01e96c823e961dc3 From 278facee7c7a028d642f3b5f23a42bcf099d86f3 Mon Sep 17 00:00:00 2001 From: Monty Date: Thu, 28 May 2020 16:56:37 +0300 Subject: [PATCH 45/59] Added test case for query that was crashing in 10.4.13 --- mysql-test/main/alter_table.result | 5 +++++ mysql-test/main/alter_table.test | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/mysql-test/main/alter_table.result b/mysql-test/main/alter_table.result index 8e8b4362ed8..4b817ca11b5 100644 --- a/mysql-test/main/alter_table.result +++ b/mysql-test/main/alter_table.result @@ -2594,3 +2594,8 @@ alter table person_principal add column if not exists date_mask tinyint null; update person_principal set date_mask = 0; alter table person_principal modify column date_mask tinyint not null; drop tables person_principal_hist, person_principal; +CREATE OR REPLACE TABLE `t1` ( `id` varchar(64) NOT NULL, `name` varchar(255) NOT NULL, `extra` text DEFAULT NULL, `password` varchar(128) DEFAULT NULL, `enabled` tinyint(1) DEFAULT NULL, `domain_id` varchar(64) NOT NULL, `default_project_id` varchar(64) DEFAULT NULL, PRIMARY KEY (`id`), CONSTRAINT `CONSTRAINT_1` CHECK (`enabled` in (0,1)) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +insert into t1 (id,name,enabled,domain_id) values (1,"Monty",1,"domain_id"); +insert into t1 (id,name,enabled,domain_id) values (2,"Monty2",1,"domain_id2"); +ALTER TABLE t1 ADD CONSTRAINT ixu_user2_name_domain_id UNIQUE (domain_id, name); +DROP TABLE t1; diff --git a/mysql-test/main/alter_table.test b/mysql-test/main/alter_table.test index f090df56e5f..25a2a6a7a00 100644 --- a/mysql-test/main/alter_table.test +++ b/mysql-test/main/alter_table.test @@ -2111,3 +2111,12 @@ update person_principal set date_mask = 0; alter table person_principal modify column date_mask tinyint not null; drop tables person_principal_hist, person_principal; +# +# The following ALTER TABLE caused crash in 10.4.13 (Reported on freenode) +# + +CREATE OR REPLACE TABLE `t1` ( `id` varchar(64) NOT NULL, `name` varchar(255) NOT NULL, `extra` text DEFAULT NULL, `password` varchar(128) DEFAULT NULL, `enabled` tinyint(1) DEFAULT NULL, `domain_id` varchar(64) NOT NULL, `default_project_id` varchar(64) DEFAULT NULL, PRIMARY KEY (`id`), CONSTRAINT `CONSTRAINT_1` CHECK (`enabled` in (0,1)) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +insert into t1 (id,name,enabled,domain_id) values (1,"Monty",1,"domain_id"); +insert into t1 (id,name,enabled,domain_id) values (2,"Monty2",1,"domain_id2"); +ALTER TABLE t1 ADD CONSTRAINT ixu_user2_name_domain_id UNIQUE (domain_id, name); +DROP TABLE t1; From dac1280a65a97ac49c588d344a33fd7bb49c6737 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Thu, 28 May 2020 20:54:38 +0300 Subject: [PATCH 46/59] MDEV-18794 Assertion `!m_innodb' failed in ha_partition::cmp_ref upon SELECT from partitioned table System versioning assertion fix. Since DROP SYSTEM VERSIONING does not change list of dropped keys we should handle a special case. Caused by MDEV-19751. This fix deprecates MDEV-17091. --- .../versioning/r/partition_innodb.result | 12 +++++ .../suite/versioning/t/partition_innodb.test | 11 ++++ sql/sql_partition.cc | 50 ++++++++----------- 3 files changed, 44 insertions(+), 29 deletions(-) diff --git a/mysql-test/suite/versioning/r/partition_innodb.result b/mysql-test/suite/versioning/r/partition_innodb.result index 3c4adce099a..2489af91b7f 100644 --- a/mysql-test/suite/versioning/r/partition_innodb.result +++ b/mysql-test/suite/versioning/r/partition_innodb.result @@ -65,3 +65,15 @@ partition by range columns (a, row_start) ( partition p1 values less than (100, 100) ); ERROR HY000: Transaction-precise system versioned tables do not support partitioning by ROW START or ROW END +# +# MDEV-18794 Assertion `!m_innodb' failed in ha_partition::cmp_ref upon SELECT from partitioned table +# +create or replace table t1 (pk int auto_increment, i int, c char(1), primary key (pk), key(i)) +engine=innodb with system versioning partition by key() partitions 2; +insert into t1 (i, c) values (1, 'a'), (2, 'b'), (null, 'c'), (null, 'b'); +alter table t1 drop system versioning; +replace into t1 select * from t1; +select * from t1 where i > 0 or pk = 1000 limit 1; +pk i c +1 1 a +drop table t1; diff --git a/mysql-test/suite/versioning/t/partition_innodb.test b/mysql-test/suite/versioning/t/partition_innodb.test index aaaf7001644..79130dbbccf 100644 --- a/mysql-test/suite/versioning/t/partition_innodb.test +++ b/mysql-test/suite/versioning/t/partition_innodb.test @@ -78,4 +78,15 @@ partition by range columns (a, row_start) ( partition p1 values less than (100, 100) ); +--echo # +--echo # MDEV-18794 Assertion `!m_innodb' failed in ha_partition::cmp_ref upon SELECT from partitioned table +--echo # +create or replace table t1 (pk int auto_increment, i int, c char(1), primary key (pk), key(i)) +engine=innodb with system versioning partition by key() partitions 2; +insert into t1 (i, c) values (1, 'a'), (2, 'b'), (null, 'c'), (null, 'b'); +alter table t1 drop system versioning; +replace into t1 select * from t1; +select * from t1 where i > 0 or pk = 1000 limit 1; +drop table t1; + --source suite/versioning/common_finish.inc diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index b7b400f490a..f7158a2b1ee 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -5986,26 +5986,35 @@ the generated partition syntax in a correct manner. } } /* - Prohibit inplace when partitioned by primary key and the primary key is dropped. + Prohibit inplace when partitioned by primary key and the primary key is changed. */ if (!*partition_changed && tab_part_info->part_field_array && !tab_part_info->part_field_list.elements && table->s->primary_key != MAX_KEY) { - KEY *primary_key= table->key_info + table->s->primary_key; - List_iterator_fast drop_it(alter_info->drop_list); - const char *primary_name= primary_key->name.str; - const Alter_drop *drop; - drop_it.rewind(); - while ((drop= drop_it++)) + + if (alter_info->flags & (ALTER_DROP_SYSTEM_VERSIONING | + ALTER_ADD_SYSTEM_VERSIONING)) { - if (drop->type == Alter_drop::KEY && - 0 == my_strcasecmp(system_charset_info, primary_name, drop->name)) - break; + *partition_changed= true; + } + else + { + KEY *primary_key= table->key_info + table->s->primary_key; + List_iterator_fast drop_it(alter_info->drop_list); + const char *primary_name= primary_key->name.str; + const Alter_drop *drop; + drop_it.rewind(); + while ((drop= drop_it++)) + { + if (drop->type == Alter_drop::KEY && + 0 == my_strcasecmp(system_charset_info, primary_name, drop->name)) + break; + } + if (drop) + *partition_changed= TRUE; } - if (drop) - *partition_changed= TRUE; } } if (thd->work_part_info) @@ -6039,23 +6048,6 @@ the generated partition syntax in a correct manner. } } - // In case of PARTITION BY KEY(), check if primary key has changed - // System versioning also implicitly adds/removes primary key parts - if (alter_info->partition_flags == 0 && part_info->list_of_part_fields - && part_info->part_field_list.elements == 0) - { - if (alter_info->flags & (ALTER_DROP_SYSTEM_VERSIONING | - ALTER_ADD_SYSTEM_VERSIONING)) - *partition_changed= true; - - List_iterator it(alter_info->key_list); - Key *key; - while((key= it++) && !*partition_changed) - { - if (key->type == Key::PRIMARY) - *partition_changed= true; - } - } /* Set up partition default_engine_type either from the create_info or from the previus table From 3e9b96b6ff925f83427600956b86a984b726d3af Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Thu, 13 Jun 2019 19:29:02 +0300 Subject: [PATCH 47/59] MDEV-18794 append_drop_column() small refactoring Bogus if() logic inside the func. --- sql/sql_table.cc | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 2d9e07b9f3e..30b728adf69 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -7824,16 +7824,13 @@ blob_length_by_type(enum_field_types type) } -static void append_drop_column(THD *thd, bool dont, String *str, - Field *field) +static inline +void append_drop_column(THD *thd, String *str, Field *field) { - if (!dont) - { - if (str->length()) - str->append(STRING_WITH_LEN(", ")); - str->append(STRING_WITH_LEN("DROP COLUMN ")); - append_identifier(thd, str, &field->field_name); - } + if (str->length()) + str->append(STRING_WITH_LEN(", ")); + str->append(STRING_WITH_LEN("DROP COLUMN ")); + append_identifier(thd, str, &field->field_name); } @@ -8088,7 +8085,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, field->invisible < INVISIBLE_SYSTEM) { StringBuffer tmp; - append_drop_column(thd, false, &tmp, field); + append_drop_column(thd, &tmp, field); my_error(ER_MISSING, MYF(0), table->s->table_name.str, tmp.c_ptr()); goto err; } @@ -8141,10 +8138,10 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, !vers_system_invisible) { StringBuffer tmp; - append_drop_column(thd, dropped_sys_vers_fields & VERS_SYS_START_FLAG, - &tmp, table->vers_start_field()); - append_drop_column(thd, dropped_sys_vers_fields & VERS_SYS_END_FLAG, - &tmp, table->vers_end_field()); + if (!(dropped_sys_vers_fields & VERS_SYS_START_FLAG)) + append_drop_column(thd, &tmp, table->vers_start_field()); + if (!(dropped_sys_vers_fields & VERS_SYS_END_FLAG)) + append_drop_column(thd, &tmp, table->vers_end_field()); my_error(ER_MISSING, MYF(0), table->s->table_name.str, tmp.c_ptr()); goto err; } From dd9773b72380eeb98b9dabba219452f29ee5603b Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Thu, 28 May 2020 22:22:19 +0300 Subject: [PATCH 48/59] MDEV-22413 Server hangs upon UPDATE on a view reading from versioned partitioned table UPDATE gets access to history records because versioning conditions are not set for VIEW. This leads to endless loop of inserting history records when clustered index is rebuilt and ha_rnd_next() returns newly inserted history record. Return back original behavior of failing on write-locked table in historical query. 35b679b9 assumed that SELECT_LEX::lock_type influences anything, but actually at this point table is already locked. Original bug report was tempesta-tech/mariadb#102 --- .../suite/versioning/r/partition.result | 26 ++++++++++++-- mysql-test/suite/versioning/r/select.result | 3 +- mysql-test/suite/versioning/r/select2.result | 3 +- mysql-test/suite/versioning/t/partition.test | 36 ++++++++++++++++--- mysql-test/suite/versioning/t/select.test | 1 + mysql-test/suite/versioning/t/select2.test | 1 + sql/sql_select.cc | 10 ++++-- 7 files changed, 67 insertions(+), 13 deletions(-) diff --git a/mysql-test/suite/versioning/r/partition.result b/mysql-test/suite/versioning/r/partition.result index c4fedb08ad6..4d0426512e6 100644 --- a/mysql-test/suite/versioning/r/partition.result +++ b/mysql-test/suite/versioning/r/partition.result @@ -650,6 +650,26 @@ pk f update t1 set f=pk; delete from t1; drop table t1; -# Test cleanup -drop database test; -create database test; +# +# MDEV-22413 Server hangs upon UPDATE/DELETE on a view reading from versioned partitioned table +# +create or replace table t1 (f char(6)) engine innodb with system versioning; +insert into t1 values (null); +update t1 set f= 'foo'; +update t1 set f= 'bar'; +create or replace view v1 as select * from t1 for system_time all; +update v1 set f = ''; +ERROR HY000: Table 't1' was locked with a READ lock and can't be updated +create or replace table t1 (f char(6)) engine innodb with system versioning +partition by system_time limit 1 +(partition p1 history, partition p2 history, partition pn current); +insert into t1 values (null); +update t1 set f= 'foo'; +update t1 set f= 'bar'; +create or replace view v1 as select * from t1 for system_time all; +update v1 set f= ''; +ERROR HY000: Table 't1' was locked with a READ lock and can't be updated +delete from v1; +ERROR HY000: Table 't1' was locked with a READ lock and can't be updated +drop view v1; +drop table t1; diff --git a/mysql-test/suite/versioning/r/select.result b/mysql-test/suite/versioning/r/select.result index f1bdcccce59..74df243cf7c 100644 --- a/mysql-test/suite/versioning/r/select.result +++ b/mysql-test/suite/versioning/r/select.result @@ -206,8 +206,7 @@ ERROR HY000: Table `t1` is not system-versioned create or replace table t1 (x int) with system versioning; insert into t1 values (1); select * from t1 for system_time all for update; -x -1 +ERROR HY000: Table 't1' was locked with a READ lock and can't be updated create or replace table t1 (a int not null auto_increment primary key) with system versioning; select * from (t1 as t2 left join t1 as t3 using (a)) natural left join t1; a diff --git a/mysql-test/suite/versioning/r/select2.result b/mysql-test/suite/versioning/r/select2.result index bb5c82ee444..7b20d68343b 100644 --- a/mysql-test/suite/versioning/r/select2.result +++ b/mysql-test/suite/versioning/r/select2.result @@ -229,8 +229,7 @@ ERROR HY000: Table `t1` is not system-versioned create or replace table t1 (x int) with system versioning; insert into t1 values (1); select * from t1 for system_time as of now() for update; -x -1 +ERROR HY000: Table 't1' was locked with a READ lock and can't be updated create or replace table t1 (a int not null auto_increment primary key) with system versioning; select * from (t1 as t2 left join t1 as t3 using (a)) natural left join t1; a diff --git a/mysql-test/suite/versioning/t/partition.test b/mysql-test/suite/versioning/t/partition.test index 6fee6f43847..6cd88caf1a9 100644 --- a/mysql-test/suite/versioning/t/partition.test +++ b/mysql-test/suite/versioning/t/partition.test @@ -583,7 +583,6 @@ update t1 left join t2 on a > b set b= 2 order by b; # cleanup drop table t1, t2; ---source suite/versioning/common_finish.inc --echo # --echo # MDEV-17091 Assertion `old_part_id == m_last_part' failed in --echo # ha_partition::update_row or `part_id == m_last_part' in @@ -605,6 +604,35 @@ update t1 set f=pk; delete from t1; drop table t1; ---echo # Test cleanup -drop database test; -create database test; +--echo # +--echo # MDEV-22413 Server hangs upon UPDATE/DELETE on a view reading from versioned partitioned table +--echo # +create or replace table t1 (f char(6)) engine innodb with system versioning; + +insert into t1 values (null); +update t1 set f= 'foo'; +update t1 set f= 'bar'; + +create or replace view v1 as select * from t1 for system_time all; +--error ER_TABLE_NOT_LOCKED_FOR_WRITE +update v1 set f = ''; + +create or replace table t1 (f char(6)) engine innodb with system versioning +partition by system_time limit 1 +(partition p1 history, partition p2 history, partition pn current); + +insert into t1 values (null); +update t1 set f= 'foo'; +update t1 set f= 'bar'; + +create or replace view v1 as select * from t1 for system_time all; +--error ER_TABLE_NOT_LOCKED_FOR_WRITE +update v1 set f= ''; +--error ER_TABLE_NOT_LOCKED_FOR_WRITE +delete from v1; + +# cleanup +drop view v1; +drop table t1; + +--source suite/versioning/common_finish.inc diff --git a/mysql-test/suite/versioning/t/select.test b/mysql-test/suite/versioning/t/select.test index 069fe7a9a8d..16c2f34ab35 100644 --- a/mysql-test/suite/versioning/t/select.test +++ b/mysql-test/suite/versioning/t/select.test @@ -123,6 +123,7 @@ select * from t1 for system_time all; create or replace table t1 (x int) with system versioning; insert into t1 values (1); +--error ER_TABLE_NOT_LOCKED_FOR_WRITE select * from t1 for system_time all for update; create or replace table t1 (a int not null auto_increment primary key) with system versioning; diff --git a/mysql-test/suite/versioning/t/select2.test b/mysql-test/suite/versioning/t/select2.test index d1b73fa799b..fa5d52c5143 100644 --- a/mysql-test/suite/versioning/t/select2.test +++ b/mysql-test/suite/versioning/t/select2.test @@ -128,6 +128,7 @@ select * from t1 for system_time all; create or replace table t1 (x int) with system versioning; insert into t1 values (1); +--error ER_TABLE_NOT_LOCKED_FOR_WRITE select * from t1 for system_time as of now() for update; create or replace table t1 (a int not null auto_increment primary key) with system versioning; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 5694085d7da..05da4a1e750 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -832,10 +832,16 @@ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables) if (vers_conditions.is_set()) { + if (vers_conditions.was_set() && + table->lock_type > TL_READ_NO_INSERT && + !vers_conditions.delete_history) + { + my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), table->alias.str); + DBUG_RETURN(-1); + } + if (vers_conditions.type == SYSTEM_TIME_ALL) continue; - - lock_type= TL_READ; // ignore TL_WRITE, history is immutable anyway } const LEX_CSTRING *fstart= From 19da9a51ae174785806c87bcc8fa47406af9ed96 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Thu, 28 May 2020 22:22:20 +0300 Subject: [PATCH 49/59] MDEV-16937 Strict SQL with system versioned tables causes issues Respect system fields in NO_ZERO_DATE mode. This is the subject for refactoring in MDEV-19597 --- .../federated/federatedx_versioning.result | 4 ++-- .../suite/federated/federatedx_versioning.test | 2 +- mysql-test/suite/versioning/common.inc | 4 ++++ .../suite/versioning/engines.combinations | 5 +++++ mysql-test/suite/versioning/r/select.result | 6 +++--- .../suite/versioning/r/select2,trx_id.rdiff | 4 ++-- mysql-test/suite/versioning/r/select2.result | 10 +++++----- mysql-test/suite/versioning/r/sysvars.result | 4 ++-- mysql-test/suite/versioning/r/view.result | 2 +- mysql-test/suite/versioning/t/select.test | 10 +++++----- mysql-test/suite/versioning/t/select2.test | 10 +++++----- mysql-test/suite/versioning/t/sysvars.test | 4 ++-- mysql-test/suite/versioning/t/view.test | 2 +- sql/field.cc | 7 +++++++ sql/sql_table.cc | 1 + sql/table.cc | 2 +- sql/unireg.cc | 2 ++ storage/federatedx/ha_federatedx.cc | 17 +++++++++++++++++ 18 files changed, 66 insertions(+), 30 deletions(-) diff --git a/mysql-test/suite/federated/federatedx_versioning.result b/mysql-test/suite/federated/federatedx_versioning.result index c708b5f7932..d9e8a421989 100644 --- a/mysql-test/suite/federated/federatedx_versioning.result +++ b/mysql-test/suite/federated/federatedx_versioning.result @@ -9,8 +9,8 @@ show create table tf; Table Create Table tf CREATE TABLE `tf` ( `x` int(11) DEFAULT NULL, - `row_start` SYS_TYPE NOT NULL INVISIBLE DEFAULT 0, - `row_end` SYS_TYPE NOT NULL INVISIBLE DEFAULT 0 + `row_start` SYS_TYPE INVISIBLE DEFAULT '1971-01-01 00:00:00.000000', + `row_end` SYS_TYPE INVISIBLE DEFAULT '1971-01-01 00:00:00.000000' ) ENGINE=FEDERATED DEFAULT CHARSET=latin1 CONNECTION='mysql://root@127.0.0.1:MASTER_MYPORT/test/t1' # INSERT insert into t1 values (1); diff --git a/mysql-test/suite/federated/federatedx_versioning.test b/mysql-test/suite/federated/federatedx_versioning.test index 40d0bc5018a..44999522a4d 100644 --- a/mysql-test/suite/federated/federatedx_versioning.test +++ b/mysql-test/suite/federated/federatedx_versioning.test @@ -12,7 +12,7 @@ eval create or replace table t1 ( with system versioning; --replace_result $MASTER_MYPORT MASTER_MYPORT eval create or replace table tf engine=FEDERATED connection='mysql://root@127.0.0.1:$MASTER_MYPORT/test/t1'; ---replace_result $MASTER_MYPORT MASTER_MYPORT $sys_datatype_expl SYS_TYPE "'0000-00-00 00:00:00.000000'" 0 +--replace_result $MASTER_MYPORT MASTER_MYPORT $sys_datatype_expl SYS_TYPE 19710101000000 "'1971-01-01 00:00:00.000000'" " NOT NULL" "" show create table tf; --echo # INSERT insert into t1 values (1); diff --git a/mysql-test/suite/versioning/common.inc b/mysql-test/suite/versioning/common.inc index 355b571e5a0..efb081a02e4 100644 --- a/mysql-test/suite/versioning/common.inc +++ b/mysql-test/suite/versioning/common.inc @@ -50,6 +50,10 @@ if ($MTR_COMBINATION_MYISAM) { --let $MTR_COMBINATION_TIMESTAMP= 1 } +if ($MTR_COMBINATION_TRADITIONAL) +{ + --let $MTR_COMBINATION_TIMESTAMP= 1 +} if ($MTR_COMBINATION_HEAP) { --let $MTR_COMBINATION_TIMESTAMP= 1 diff --git a/mysql-test/suite/versioning/engines.combinations b/mysql-test/suite/versioning/engines.combinations index 26b5bab23f1..57e2af6cd06 100644 --- a/mysql-test/suite/versioning/engines.combinations +++ b/mysql-test/suite/versioning/engines.combinations @@ -7,5 +7,10 @@ default-storage-engine=innodb [myisam] default-storage-engine=myisam +[traditional] +default-storage-engine=myisam +sql-mode=traditional + [heap] default-storage-engine=memory + diff --git a/mysql-test/suite/versioning/r/select.result b/mysql-test/suite/versioning/r/select.result index 74df243cf7c..23c6e2d74af 100644 --- a/mysql-test/suite/versioning/r/select.result +++ b/mysql-test/suite/versioning/r/select.result @@ -43,7 +43,7 @@ ASOF_x y 7 107 8 108 9 109 -select x as FROMTO_x, y from t1 for system_time from timestamp '0-0-0 0:0:0' to timestamp @t1; +select x as FROMTO_x, y from t1 for system_time from timestamp '1970-01-01 00:00:00' to timestamp @t1; FROMTO_x y 0 100 1 101 @@ -55,7 +55,7 @@ FROMTO_x y 7 107 8 108 9 109 -select x as BETWAND_x, y from t1 for system_time between timestamp '0-0-0 0:0:0' and timestamp @t1; +select x as BETWAND_x, y from t1 for system_time between timestamp '1970-01-01 00:00:00' and timestamp @t1; BETWAND_x y 0 100 1 101 @@ -247,7 +247,7 @@ a b select * from (select * from (select * from t1 cross join t2) as tmp1) as tmp2; a b 1 2 -select * from (select * from t1 cross join t2 for system_time as of timestamp ('0-0-0')) as tmp; +select * from (select * from t1 cross join t2 for system_time as of timestamp ('1970-01-01 00:00:00')) as tmp; a b create or replace table t1(a1 int) with system versioning; create or replace table t2(a2 int) with system versioning; diff --git a/mysql-test/suite/versioning/r/select2,trx_id.rdiff b/mysql-test/suite/versioning/r/select2,trx_id.rdiff index a657b94c031..d4b35c45568 100644 --- a/mysql-test/suite/versioning/r/select2,trx_id.rdiff +++ b/mysql-test/suite/versioning/r/select2,trx_id.rdiff @@ -22,7 +22,7 @@ 7 107 8 108 9 109 --select x as FROMTO2_x, y from t1 for system_time from '0-0-0 0:0:0' to @t1; +-select x as FROMTO2_x, y from t1 for system_time from '1970-01-01 00:00' to @t1; +select x as FROMTO2_x, y from t1 for system_time from @x0 to @x1; FROMTO2_x y 0 100 @@ -31,7 +31,7 @@ 7 107 8 108 9 109 --select x as BETWAND2_x, y from t1 for system_time between timestamp '0-0-0 0:0:0' and timestamp @t1; +-select x as BETWAND2_x, y from t1 for system_time between timestamp '1970-01-01 00:00' and timestamp @t1; +select x as BETWAND2_x, y from t1 for system_time between transaction @x0 and transaction @x1; BETWAND2_x y 0 100 diff --git a/mysql-test/suite/versioning/r/select2.result b/mysql-test/suite/versioning/r/select2.result index 7b20d68343b..90607335fc1 100644 --- a/mysql-test/suite/versioning/r/select2.result +++ b/mysql-test/suite/versioning/r/select2.result @@ -44,7 +44,7 @@ ASOF_x y 7 107 8 108 9 109 -select x as FROMTO_x, y from t1 for system_time from '0-0-0 0:0:0' to timestamp @t1; +select x as FROMTO_x, y from t1 for system_time from '1970-01-01 00:00' to timestamp @t1; FROMTO_x y 0 100 1 101 @@ -56,7 +56,7 @@ FROMTO_x y 7 107 8 108 9 109 -select x as BETWAND_x, y from t1 for system_time between '0-0-0 0:0:0' and timestamp @t1; +select x as BETWAND_x, y from t1 for system_time between '1970-01-01 00:00' and timestamp @t1; BETWAND_x y 0 100 1 101 @@ -94,7 +94,7 @@ ASOF2_x y 7 107 8 108 9 109 -select x as FROMTO2_x, y from t1 for system_time from '0-0-0 0:0:0' to @t1; +select x as FROMTO2_x, y from t1 for system_time from '1970-01-01 00:00' to @t1; FROMTO2_x y 0 100 1 101 @@ -106,7 +106,7 @@ FROMTO2_x y 7 107 8 108 9 109 -select x as BETWAND2_x, y from t1 for system_time between timestamp '0-0-0 0:0:0' and timestamp @t1; +select x as BETWAND2_x, y from t1 for system_time between timestamp '1970-01-01 00:00' and timestamp @t1; BETWAND2_x y 0 100 1 101 @@ -270,7 +270,7 @@ a b select * from (select * from (select * from t1 cross join t2) as tmp1) as tmp2; a b 1 2 -select * from (select * from t1 cross join t2 for system_time as of timestamp ('0-0-0')) as tmp; +select * from (select * from t1 cross join t2 for system_time as of timestamp ('1970-01-01 00:00')) as tmp; a b create or replace table t1(a1 int) with system versioning; create or replace table t2(a2 int) with system versioning; diff --git a/mysql-test/suite/versioning/r/sysvars.result b/mysql-test/suite/versioning/r/sysvars.result index 899eda91a81..9cb4911b321 100644 --- a/mysql-test/suite/versioning/r/sysvars.result +++ b/mysql-test/suite/versioning/r/sysvars.result @@ -134,11 +134,11 @@ select * from t for system_time all; a 2 1 -select * from t for system_time from '0-0-0' to current_timestamp(6); +select * from t for system_time from '1970-01-01 00:00' to current_timestamp(6); a 2 1 -select * from t for system_time between '0-0-0' and current_timestamp(6); +select * from t for system_time between '1970-01-01 00:00' and current_timestamp(6); a 2 1 diff --git a/mysql-test/suite/versioning/r/view.result b/mysql-test/suite/versioning/r/view.result index 3b3fe580af4..e9e74899790 100644 --- a/mysql-test/suite/versioning/r/view.result +++ b/mysql-test/suite/versioning/r/view.result @@ -79,7 +79,7 @@ create or replace view vt12 as select * from t1 cross join t2; select * from vt12; a b 1 2 -create or replace view vt12 as select * from t1 for system_time as of timestamp ('0-0-0') cross join t2; +create or replace view vt12 as select * from t1 for system_time as of timestamp ('1970-01-01 00:00') cross join t2; select * from vt12; a b # VIEW improvements [tempesta-tech/mariadb#183] diff --git a/mysql-test/suite/versioning/t/select.test b/mysql-test/suite/versioning/t/select.test index 16c2f34ab35..c70f2714fff 100644 --- a/mysql-test/suite/versioning/t/select.test +++ b/mysql-test/suite/versioning/t/select.test @@ -48,8 +48,8 @@ if ($MTR_COMBINATION_TRX_ID) select x, y from t1; select x as ASOF_x, y from t1 for system_time as of timestamp @t0; -select x as FROMTO_x, y from t1 for system_time from timestamp '0-0-0 0:0:0' to timestamp @t1; -select x as BETWAND_x, y from t1 for system_time between timestamp '0-0-0 0:0:0' and timestamp @t1; +select x as FROMTO_x, y from t1 for system_time from timestamp '1970-01-01 00:00:00' to timestamp @t1; +select x as BETWAND_x, y from t1 for system_time between timestamp '1970-01-01 00:00:00' and timestamp @t1; select x as ALL_x, y from t1 for system_time all; --disable_query_log @@ -62,8 +62,8 @@ if ($MTR_COMBINATION_TRX_ID) if ($MTR_COMBINATION_TIMESTAMP) { select x as ASOF2_x, y from t1 for system_time as of @t0; - select x as FROMTO2_x, y from t1 for system_time from timestamp '0-0-0 0:0:0' to timestamp @t1; - select x as BETWAND2_x, y from t1 for system_time between timestamp '0-0-0 0:0:0' and timestamp @t1; + select x as FROMTO2_x, y from t1 for system_time from timestamp '1970-01-01 00:00:00' to timestamp @t1; + select x as BETWAND2_x, y from t1 for system_time between timestamp '1970-01-01 00:00:00' and timestamp @t1; } --enable_query_log @@ -161,7 +161,7 @@ insert into t1 values (1); insert into t2 values (2); select * from (select * from t1 cross join t2) as tmp; select * from (select * from (select * from t1 cross join t2) as tmp1) as tmp2; -select * from (select * from t1 cross join t2 for system_time as of timestamp ('0-0-0')) as tmp; +select * from (select * from t1 cross join t2 for system_time as of timestamp ('1970-01-01 00:00:00')) as tmp; create or replace table t1(a1 int) with system versioning; create or replace table t2(a2 int) with system versioning; diff --git a/mysql-test/suite/versioning/t/select2.test b/mysql-test/suite/versioning/t/select2.test index fa5d52c5143..7ede218cabf 100644 --- a/mysql-test/suite/versioning/t/select2.test +++ b/mysql-test/suite/versioning/t/select2.test @@ -36,8 +36,8 @@ if($MTR_COMBINATION_TRX_ID) { select x, y from t1; select x as ASOF_x, y from t1 for system_time as of timestamp @t0; -select x as FROMTO_x, y from t1 for system_time from '0-0-0 0:0:0' to timestamp @t1; -select x as BETWAND_x, y from t1 for system_time between '0-0-0 0:0:0' and timestamp @t1; +select x as FROMTO_x, y from t1 for system_time from '1970-01-01 00:00' to timestamp @t1; +select x as BETWAND_x, y from t1 for system_time between '1970-01-01 00:00' and timestamp @t1; select x as ALL_x, y from t1 for system_time all; if($MTR_COMBINATION_TRX_ID) { @@ -47,8 +47,8 @@ if($MTR_COMBINATION_TRX_ID) { } if(!$MTR_COMBINATION_TRX_ID) { select x as ASOF2_x, y from t1 for system_time as of @t0; - select x as FROMTO2_x, y from t1 for system_time from '0-0-0 0:0:0' to @t1; - select x as BETWAND2_x, y from t1 for system_time between timestamp '0-0-0 0:0:0' and timestamp @t1; + select x as FROMTO2_x, y from t1 for system_time from '1970-01-01 00:00' to @t1; + select x as BETWAND2_x, y from t1 for system_time between timestamp '1970-01-01 00:00' and timestamp @t1; } drop table t1; @@ -166,7 +166,7 @@ insert into t1 values (1); insert into t2 values (2); select * from (select * from t1 cross join t2) as tmp; select * from (select * from (select * from t1 cross join t2) as tmp1) as tmp2; -select * from (select * from t1 cross join t2 for system_time as of timestamp ('0-0-0')) as tmp; +select * from (select * from t1 cross join t2 for system_time as of timestamp ('1970-01-01 00:00')) as tmp; create or replace table t1(a1 int) with system versioning; create or replace table t2(a2 int) with system versioning; diff --git a/mysql-test/suite/versioning/t/sysvars.test b/mysql-test/suite/versioning/t/sysvars.test index 8c8311d66fc..1999ddd13fc 100644 --- a/mysql-test/suite/versioning/t/sysvars.test +++ b/mysql-test/suite/versioning/t/sysvars.test @@ -97,8 +97,8 @@ select * from t for system_time all; select * from t; select * from t for system_time as of timestamp current_timestamp(6); select * from t for system_time all; -select * from t for system_time from '0-0-0' to current_timestamp(6); -select * from t for system_time between '0-0-0' and current_timestamp(6); +select * from t for system_time from '1970-01-01 00:00' to current_timestamp(6); +select * from t for system_time between '1970-01-01 00:00' and current_timestamp(6); show status like "Feature_system_versioning"; diff --git a/mysql-test/suite/versioning/t/view.test b/mysql-test/suite/versioning/t/view.test index 288f1eb6e21..37676a7fb50 100644 --- a/mysql-test/suite/versioning/t/view.test +++ b/mysql-test/suite/versioning/t/view.test @@ -65,7 +65,7 @@ insert into t1 values (1); insert into t2 values (2); create or replace view vt12 as select * from t1 cross join t2; select * from vt12; -create or replace view vt12 as select * from t1 for system_time as of timestamp ('0-0-0') cross join t2; +create or replace view vt12 as select * from t1 for system_time as of timestamp ('1970-01-01 00:00') cross join t2; select * from vt12; --echo # VIEW improvements [tempesta-tech/mariadb#183] diff --git a/sql/field.cc b/sql/field.cc index a9cf4589a16..519a00677ec 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -11456,6 +11456,13 @@ bool Field::save_in_field_default_value(bool view_error_processing) { THD *thd= table->in_use; + /* + TODO: MDEV-19597 Refactor TABLE::vers_update_fields() via stored virtual columns + This condition will go away as well as other conditions with vers_sys_field(). + */ + if (vers_sys_field()) + return false; + if (unlikely(flags & NO_DEFAULT_VALUE_FLAG && real_type() != MYSQL_TYPE_ENUM)) { diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 30b728adf69..c6b22650d5c 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4195,6 +4195,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (thd->variables.sql_mode & MODE_NO_ZERO_DATE && !sql_field->default_value && !sql_field->vcol_info && + !sql_field->vers_sys_field() && sql_field->is_timestamp_type() && !opt_explicit_defaults_for_timestamp && (sql_field->flags & NOT_NULL_FLAG) && diff --git a/sql/table.cc b/sql/table.cc index c91e9d84a59..7d09985094c 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -8222,7 +8222,7 @@ bool TABLE::validate_default_values_of_unset_fields(THD *thd) const for (Field **fld= field; *fld; fld++) { if (!bitmap_is_set(write_set, (*fld)->field_index) && - !((*fld)->flags & NO_DEFAULT_VALUE_FLAG)) + !((*fld)->flags & (NO_DEFAULT_VALUE_FLAG | VERS_SYSTEM_FIELD))) { if (!(*fld)->is_null_in_record(s->default_values) && (*fld)->validate_value_in_record_with_warn(thd, s->default_values) && diff --git a/sql/unireg.cc b/sql/unireg.cc index fa30bab15a2..b7eb1cd3457 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -1086,6 +1086,7 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options, null_count+= field->length & 7; if (field->default_value && !field->default_value->flags && + !field->vers_sys_field() && (!(field->flags & BLOB_FLAG) || field->real_field_type() == MYSQL_TYPE_GEOMETRY)) { @@ -1108,6 +1109,7 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options, delete regfield; //To avoid memory leak } else if (regfield->real_type() == MYSQL_TYPE_ENUM && + !field->vers_sys_field() && (field->flags & NOT_NULL_FLAG)) { regfield->set_notnull(); diff --git a/storage/federatedx/ha_federatedx.cc b/storage/federatedx/ha_federatedx.cc index 4a717eead20..1adb7b214f0 100644 --- a/storage/federatedx/ha_federatedx.cc +++ b/storage/federatedx/ha_federatedx.cc @@ -3611,6 +3611,9 @@ int ha_federatedx::discover_assisted(handlerton *hton, THD* thd, char buf[1024]; String query(buf, sizeof(buf), cs); static LEX_CSTRING cut_clause={STRING_WITH_LEN(" WITH SYSTEM VERSIONING")}; + static LEX_CSTRING cut_start={STRING_WITH_LEN("GENERATED ALWAYS AS ROW START")}; + static LEX_CSTRING cut_end={STRING_WITH_LEN("GENERATED ALWAYS AS ROW END")}; + static LEX_CSTRING set_ts={STRING_WITH_LEN("DEFAULT TIMESTAMP'1971-01-01 00:00:00'")}; int cut_offset; MYSQL_RES *res; MYSQL_ROW rdata; @@ -3649,7 +3652,21 @@ int ha_federatedx::discover_assisted(handlerton *hton, THD* thd, cut_offset= (int)query.length() - (int)cut_clause.length; if (cut_offset > 0 && !memcmp(query.ptr() + cut_offset, cut_clause.str, cut_clause.length)) + { query.length(cut_offset); + const char *ptr= strstr(query.ptr(), cut_start.str); + if (ptr) + { + query.replace((uint32) (ptr - query.ptr()), (uint32) cut_start.length, + set_ts.str, (uint32) set_ts.length); + } + ptr= strstr(query.ptr(), cut_end.str); + if (ptr) + { + query.replace((uint32) (ptr - query.ptr()), (uint32) cut_end.length, + set_ts.str, (uint32) set_ts.length); + } + } query.append(STRING_WITH_LEN(" CONNECTION='"), cs); query.append_for_single_quote(table_s->connect_string.str, table_s->connect_string.length); From 57f7b4866f1347ebf3ee67fe61de3871e56e23cc Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Fri, 29 May 2020 11:45:19 +0300 Subject: [PATCH 50/59] MDEV-16937 Strict SQL with system versioned tables causes issues (10.4) Respect system fields in NO_ZERO_DATE mode. This is the subject for refactoring in MDEV-19597 Conflict resolution from 7d5223310789f967106d86ce193ef31b315ecff0 --- .../federated/federatedx_versioning.result | 4 ++-- .../suite/federated/federatedx_versioning.test | 2 +- mysql-test/suite/versioning/common.inc | 4 ++++ .../suite/versioning/engines.combinations | 5 +++++ mysql-test/suite/versioning/r/select.result | 6 +++--- .../suite/versioning/r/select2,trx_id.rdiff | 4 ++-- mysql-test/suite/versioning/r/select2.result | 10 +++++----- mysql-test/suite/versioning/r/sysvars.result | 4 ++-- mysql-test/suite/versioning/r/view.result | 2 +- mysql-test/suite/versioning/t/select.test | 10 +++++----- mysql-test/suite/versioning/t/select2.test | 10 +++++----- mysql-test/suite/versioning/t/sysvars.test | 4 ++-- mysql-test/suite/versioning/t/view.test | 2 +- sql/field.cc | 7 +++++++ sql/sql_table.cc | 1 + sql/table.cc | 2 +- sql/unireg.cc | 7 ++++--- storage/federatedx/ha_federatedx.cc | 17 +++++++++++++++++ 18 files changed, 68 insertions(+), 33 deletions(-) diff --git a/mysql-test/suite/federated/federatedx_versioning.result b/mysql-test/suite/federated/federatedx_versioning.result index abf2658d2e2..be3b578e839 100644 --- a/mysql-test/suite/federated/federatedx_versioning.result +++ b/mysql-test/suite/federated/federatedx_versioning.result @@ -9,8 +9,8 @@ show create table tf; Table Create Table tf CREATE TABLE `tf` ( `x` int(11) DEFAULT NULL, - `row_start` SYS_TYPE NOT NULL INVISIBLE DEFAULT 0, - `row_end` SYS_TYPE NOT NULL INVISIBLE DEFAULT 0 + `row_start` SYS_TYPE INVISIBLE DEFAULT '1971-01-01 00:00:00.000000', + `row_end` SYS_TYPE INVISIBLE DEFAULT '1971-01-01 00:00:00.000000' ) ENGINE=FEDERATED DEFAULT CHARSET=latin1 CONNECTION='mysql://root@127.0.0.1:MASTER_MYPORT/test/t1' # INSERT insert into t1 values (1); diff --git a/mysql-test/suite/federated/federatedx_versioning.test b/mysql-test/suite/federated/federatedx_versioning.test index e8db8ef9e2a..6c3247cea55 100644 --- a/mysql-test/suite/federated/federatedx_versioning.test +++ b/mysql-test/suite/federated/federatedx_versioning.test @@ -12,7 +12,7 @@ eval create or replace table t1 ( with system versioning; --replace_result $MASTER_MYPORT MASTER_MYPORT eval create or replace table tf engine=FEDERATED connection='mysql://root@127.0.0.1:$MASTER_MYPORT/test/t1'; ---replace_result $MASTER_MYPORT MASTER_MYPORT $sys_datatype_expl SYS_TYPE "'0000-00-00 00:00:00.000000'" 0 +--replace_result $MASTER_MYPORT MASTER_MYPORT $sys_datatype_expl SYS_TYPE 19710101000000 "'1971-01-01 00:00:00.000000'" " NOT NULL" "" show create table tf; --echo # INSERT insert into t1 values (1); diff --git a/mysql-test/suite/versioning/common.inc b/mysql-test/suite/versioning/common.inc index 355b571e5a0..efb081a02e4 100644 --- a/mysql-test/suite/versioning/common.inc +++ b/mysql-test/suite/versioning/common.inc @@ -50,6 +50,10 @@ if ($MTR_COMBINATION_MYISAM) { --let $MTR_COMBINATION_TIMESTAMP= 1 } +if ($MTR_COMBINATION_TRADITIONAL) +{ + --let $MTR_COMBINATION_TIMESTAMP= 1 +} if ($MTR_COMBINATION_HEAP) { --let $MTR_COMBINATION_TIMESTAMP= 1 diff --git a/mysql-test/suite/versioning/engines.combinations b/mysql-test/suite/versioning/engines.combinations index 26b5bab23f1..57e2af6cd06 100644 --- a/mysql-test/suite/versioning/engines.combinations +++ b/mysql-test/suite/versioning/engines.combinations @@ -7,5 +7,10 @@ default-storage-engine=innodb [myisam] default-storage-engine=myisam +[traditional] +default-storage-engine=myisam +sql-mode=traditional + [heap] default-storage-engine=memory + diff --git a/mysql-test/suite/versioning/r/select.result b/mysql-test/suite/versioning/r/select.result index 36d738f57b7..a969c16bad4 100644 --- a/mysql-test/suite/versioning/r/select.result +++ b/mysql-test/suite/versioning/r/select.result @@ -45,7 +45,7 @@ ASOF_x y 7 107 8 108 9 109 -select x as FROMTO_x, y from t1 for system_time from timestamp '0-0-0 0:0:0' to timestamp @t1; +select x as FROMTO_x, y from t1 for system_time from timestamp '1970-01-01 00:00:00' to timestamp @t1; FROMTO_x y 0 100 1 101 @@ -57,7 +57,7 @@ FROMTO_x y 7 107 8 108 9 109 -select x as BETWAND_x, y from t1 for system_time between timestamp '0-0-0 0:0:0' and timestamp @t1; +select x as BETWAND_x, y from t1 for system_time between timestamp '1970-01-01 00:00:00' and timestamp @t1; BETWAND_x y 0 100 1 101 @@ -284,7 +284,7 @@ a b select * from (select * from (select * from t1 cross join t2) as tmp1) as tmp2; a b 1 2 -select * from (select * from t1 cross join t2 for system_time as of timestamp ('0-0-0')) as tmp; +select * from (select * from t1 cross join t2 for system_time as of timestamp ('1970-01-01 00:00:00')) as tmp; a b create or replace table t1(a1 int) with system versioning; create or replace table t2(a2 int) with system versioning; diff --git a/mysql-test/suite/versioning/r/select2,trx_id.rdiff b/mysql-test/suite/versioning/r/select2,trx_id.rdiff index d23eb5afbc0..4075b1b0f28 100644 --- a/mysql-test/suite/versioning/r/select2,trx_id.rdiff +++ b/mysql-test/suite/versioning/r/select2,trx_id.rdiff @@ -22,7 +22,7 @@ 7 107 8 108 9 109 --select x as FROMTO2_x, y from t1 for system_time from '0-0-0 0:0:0' to @t1; +-select x as FROMTO2_x, y from t1 for system_time from '1970-01-01 00:00' to @t1; +select x as FROMTO2_x, y from t1 for system_time from @x0 to @x1; FROMTO2_x y 0 100 @@ -31,7 +31,7 @@ 7 107 8 108 9 109 --select x as BETWAND2_x, y from t1 for system_time between timestamp '0-0-0 0:0:0' and timestamp @t1; +-select x as BETWAND2_x, y from t1 for system_time between timestamp '1970-01-01 00:00' and timestamp @t1; +select x as BETWAND2_x, y from t1 for system_time between transaction @x0 and transaction @x1; BETWAND2_x y 0 100 diff --git a/mysql-test/suite/versioning/r/select2.result b/mysql-test/suite/versioning/r/select2.result index 22388359885..a2642b319ee 100644 --- a/mysql-test/suite/versioning/r/select2.result +++ b/mysql-test/suite/versioning/r/select2.result @@ -48,7 +48,7 @@ ASOF_x y 7 107 8 108 9 109 -select x as FROMTO_x, y from t1 for system_time from '0-0-0 0:0:0' to timestamp @t1; +select x as FROMTO_x, y from t1 for system_time from '1970-01-01 00:00' to timestamp @t1; FROMTO_x y 0 100 1 101 @@ -60,7 +60,7 @@ FROMTO_x y 7 107 8 108 9 109 -select x as BETWAND_x, y from t1 for system_time between '0-0-0 0:0:0' and timestamp @t1; +select x as BETWAND_x, y from t1 for system_time between '1970-01-01 00:00' and timestamp @t1; BETWAND_x y 0 100 1 101 @@ -98,7 +98,7 @@ ASOF2_x y 7 107 8 108 9 109 -select x as FROMTO2_x, y from t1 for system_time from '0-0-0 0:0:0' to @t1; +select x as FROMTO2_x, y from t1 for system_time from '1970-01-01 00:00' to @t1; FROMTO2_x y 0 100 1 101 @@ -110,7 +110,7 @@ FROMTO2_x y 7 107 8 108 9 109 -select x as BETWAND2_x, y from t1 for system_time between timestamp '0-0-0 0:0:0' and timestamp @t1; +select x as BETWAND2_x, y from t1 for system_time between timestamp '1970-01-01 00:00' and timestamp @t1; BETWAND2_x y 0 100 1 101 @@ -275,7 +275,7 @@ a b select * from (select * from (select * from t1 cross join t2) as tmp1) as tmp2; a b 1 2 -select * from (select * from t1 cross join t2 for system_time as of timestamp ('0-0-0')) as tmp; +select * from (select * from t1 cross join t2 for system_time as of timestamp ('1970-01-01 00:00')) as tmp; a b create or replace table t1(a1 int) with system versioning; create or replace table t2(a2 int) with system versioning; diff --git a/mysql-test/suite/versioning/r/sysvars.result b/mysql-test/suite/versioning/r/sysvars.result index 165458ef170..93d99273a40 100644 --- a/mysql-test/suite/versioning/r/sysvars.result +++ b/mysql-test/suite/versioning/r/sysvars.result @@ -134,11 +134,11 @@ select * from t for system_time all; a 2 1 -select * from t for system_time from '0-0-0' to current_timestamp(6); +select * from t for system_time from '1970-01-01 00:00' to current_timestamp(6); a 2 1 -select * from t for system_time between '0-0-0' and current_timestamp(6); +select * from t for system_time between '1970-01-01 00:00' and current_timestamp(6); a 2 1 diff --git a/mysql-test/suite/versioning/r/view.result b/mysql-test/suite/versioning/r/view.result index 3b3fe580af4..e9e74899790 100644 --- a/mysql-test/suite/versioning/r/view.result +++ b/mysql-test/suite/versioning/r/view.result @@ -79,7 +79,7 @@ create or replace view vt12 as select * from t1 cross join t2; select * from vt12; a b 1 2 -create or replace view vt12 as select * from t1 for system_time as of timestamp ('0-0-0') cross join t2; +create or replace view vt12 as select * from t1 for system_time as of timestamp ('1970-01-01 00:00') cross join t2; select * from vt12; a b # VIEW improvements [tempesta-tech/mariadb#183] diff --git a/mysql-test/suite/versioning/t/select.test b/mysql-test/suite/versioning/t/select.test index 00e67ed85a6..a250a60770e 100644 --- a/mysql-test/suite/versioning/t/select.test +++ b/mysql-test/suite/versioning/t/select.test @@ -48,8 +48,8 @@ if ($MTR_COMBINATION_TRX_ID) select x, y from t1; select x as ASOF_x, y from t1 for system_time as of timestamp @t0; -select x as FROMTO_x, y from t1 for system_time from timestamp '0-0-0 0:0:0' to timestamp @t1; -select x as BETWAND_x, y from t1 for system_time between timestamp '0-0-0 0:0:0' and timestamp @t1; +select x as FROMTO_x, y from t1 for system_time from timestamp '1970-01-01 00:00:00' to timestamp @t1; +select x as BETWAND_x, y from t1 for system_time between timestamp '1970-01-01 00:00:00' and timestamp @t1; select x as ALL_x, y from t1 for system_time all; --disable_query_log @@ -62,8 +62,8 @@ if ($MTR_COMBINATION_TRX_ID) if ($MTR_COMBINATION_TIMESTAMP) { select x as ASOF2_x, y from t1 for system_time as of @t0; - select x as FROMTO2_x, y from t1 for system_time from timestamp '0-0-0 0:0:0' to timestamp @t1; - select x as BETWAND2_x, y from t1 for system_time between timestamp '0-0-0 0:0:0' and timestamp @t1; + select x as FROMTO2_x, y from t1 for system_time from timestamp '1970-01-01 00:00:00' to timestamp @t1; + select x as BETWAND2_x, y from t1 for system_time between timestamp '1970-01-01 00:00:00' and timestamp @t1; } --enable_query_log @@ -186,7 +186,7 @@ insert into t1 values (1); insert into t2 values (2); select * from (select * from t1 cross join t2) as tmp; select * from (select * from (select * from t1 cross join t2) as tmp1) as tmp2; -select * from (select * from t1 cross join t2 for system_time as of timestamp ('0-0-0')) as tmp; +select * from (select * from t1 cross join t2 for system_time as of timestamp ('1970-01-01 00:00:00')) as tmp; create or replace table t1(a1 int) with system versioning; create or replace table t2(a2 int) with system versioning; diff --git a/mysql-test/suite/versioning/t/select2.test b/mysql-test/suite/versioning/t/select2.test index d1b73fa799b..75c8165886f 100644 --- a/mysql-test/suite/versioning/t/select2.test +++ b/mysql-test/suite/versioning/t/select2.test @@ -36,8 +36,8 @@ if($MTR_COMBINATION_TRX_ID) { select x, y from t1; select x as ASOF_x, y from t1 for system_time as of timestamp @t0; -select x as FROMTO_x, y from t1 for system_time from '0-0-0 0:0:0' to timestamp @t1; -select x as BETWAND_x, y from t1 for system_time between '0-0-0 0:0:0' and timestamp @t1; +select x as FROMTO_x, y from t1 for system_time from '1970-01-01 00:00' to timestamp @t1; +select x as BETWAND_x, y from t1 for system_time between '1970-01-01 00:00' and timestamp @t1; select x as ALL_x, y from t1 for system_time all; if($MTR_COMBINATION_TRX_ID) { @@ -47,8 +47,8 @@ if($MTR_COMBINATION_TRX_ID) { } if(!$MTR_COMBINATION_TRX_ID) { select x as ASOF2_x, y from t1 for system_time as of @t0; - select x as FROMTO2_x, y from t1 for system_time from '0-0-0 0:0:0' to @t1; - select x as BETWAND2_x, y from t1 for system_time between timestamp '0-0-0 0:0:0' and timestamp @t1; + select x as FROMTO2_x, y from t1 for system_time from '1970-01-01 00:00' to @t1; + select x as BETWAND2_x, y from t1 for system_time between timestamp '1970-01-01 00:00' and timestamp @t1; } drop table t1; @@ -165,7 +165,7 @@ insert into t1 values (1); insert into t2 values (2); select * from (select * from t1 cross join t2) as tmp; select * from (select * from (select * from t1 cross join t2) as tmp1) as tmp2; -select * from (select * from t1 cross join t2 for system_time as of timestamp ('0-0-0')) as tmp; +select * from (select * from t1 cross join t2 for system_time as of timestamp ('1970-01-01 00:00')) as tmp; create or replace table t1(a1 int) with system versioning; create or replace table t2(a2 int) with system versioning; diff --git a/mysql-test/suite/versioning/t/sysvars.test b/mysql-test/suite/versioning/t/sysvars.test index e82a116f30e..ef5d97ad262 100644 --- a/mysql-test/suite/versioning/t/sysvars.test +++ b/mysql-test/suite/versioning/t/sysvars.test @@ -97,8 +97,8 @@ select * from t for system_time all; select * from t; select * from t for system_time as of timestamp current_timestamp(6); select * from t for system_time all; -select * from t for system_time from '0-0-0' to current_timestamp(6); -select * from t for system_time between '0-0-0' and current_timestamp(6); +select * from t for system_time from '1970-01-01 00:00' to current_timestamp(6); +select * from t for system_time between '1970-01-01 00:00' and current_timestamp(6); show status like "Feature_system_versioning"; diff --git a/mysql-test/suite/versioning/t/view.test b/mysql-test/suite/versioning/t/view.test index 288f1eb6e21..37676a7fb50 100644 --- a/mysql-test/suite/versioning/t/view.test +++ b/mysql-test/suite/versioning/t/view.test @@ -65,7 +65,7 @@ insert into t1 values (1); insert into t2 values (2); create or replace view vt12 as select * from t1 cross join t2; select * from vt12; -create or replace view vt12 as select * from t1 for system_time as of timestamp ('0-0-0') cross join t2; +create or replace view vt12 as select * from t1 for system_time as of timestamp ('1970-01-01 00:00') cross join t2; select * from vt12; --echo # VIEW improvements [tempesta-tech/mariadb#183] diff --git a/sql/field.cc b/sql/field.cc index 0a8fdc3d3f5..a7cfca6d819 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -11265,6 +11265,13 @@ bool Field::save_in_field_default_value(bool view_error_processing) { THD *thd= table->in_use; + /* + TODO: MDEV-19597 Refactor TABLE::vers_update_fields() via stored virtual columns + This condition will go away as well as other conditions with vers_sys_field(). + */ + if (vers_sys_field()) + return false; + if (unlikely(flags & NO_DEFAULT_VALUE_FLAG && real_type() != MYSQL_TYPE_ENUM)) { diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 7c59c37734e..ff6c2873dfc 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4262,6 +4262,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (thd->variables.sql_mode & MODE_NO_ZERO_DATE && !sql_field->default_value && !sql_field->vcol_info && + !sql_field->vers_sys_field() && sql_field->is_timestamp_type() && !opt_explicit_defaults_for_timestamp && (sql_field->flags & NOT_NULL_FLAG) && diff --git a/sql/table.cc b/sql/table.cc index d90099835c0..92dfa59e648 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -8686,7 +8686,7 @@ bool TABLE::validate_default_values_of_unset_fields(THD *thd) const for (Field **fld= field; *fld; fld++) { if (!bitmap_is_set(write_set, (*fld)->field_index) && - !((*fld)->flags & NO_DEFAULT_VALUE_FLAG)) + !((*fld)->flags & (NO_DEFAULT_VALUE_FLAG | VERS_SYSTEM_FIELD))) { if (!(*fld)->is_null_in_record(s->default_values) && (*fld)->validate_value_in_record_with_warn(thd, s->default_values) && diff --git a/sql/unireg.cc b/sql/unireg.cc index f646d62d221..2b162e9a374 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -991,9 +991,10 @@ static bool pack_fields(uchar **buff_arg, List &create_fields, static bool make_empty_rec_store_default(THD *thd, Field *regfield, - Virtual_column_info *default_value) + Create_field *field) { - if (default_value && !default_value->flags) + Virtual_column_info *default_value= field->default_value; + if (!field->vers_sys_field() && default_value && !default_value->flags) { Item *expr= default_value->expr; // may be already fixed if ALTER TABLE @@ -1075,7 +1076,7 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options, !f_bit_as_char(field->pack_flag)) null_count+= field->length & 7; - error= make_empty_rec_store_default(thd, regfield, field->default_value); + error= make_empty_rec_store_default(thd, regfield, field); delete regfield; // Avoid memory leaks if (error) goto err; diff --git a/storage/federatedx/ha_federatedx.cc b/storage/federatedx/ha_federatedx.cc index 49101447dda..3c2b4cdc25b 100644 --- a/storage/federatedx/ha_federatedx.cc +++ b/storage/federatedx/ha_federatedx.cc @@ -3620,6 +3620,9 @@ int ha_federatedx::discover_assisted(handlerton *hton, THD* thd, char buf[1024]; String query(buf, sizeof(buf), cs); static LEX_CSTRING cut_clause={STRING_WITH_LEN(" WITH SYSTEM VERSIONING")}; + static LEX_CSTRING cut_start={STRING_WITH_LEN("GENERATED ALWAYS AS ROW START")}; + static LEX_CSTRING cut_end={STRING_WITH_LEN("GENERATED ALWAYS AS ROW END")}; + static LEX_CSTRING set_ts={STRING_WITH_LEN("DEFAULT TIMESTAMP'1971-01-01 00:00:00'")}; int cut_offset; MYSQL_RES *res; MYSQL_ROW rdata; @@ -3658,7 +3661,21 @@ int ha_federatedx::discover_assisted(handlerton *hton, THD* thd, cut_offset= (int)query.length() - (int)cut_clause.length; if (cut_offset > 0 && !memcmp(query.ptr() + cut_offset, cut_clause.str, cut_clause.length)) + { query.length(cut_offset); + const char *ptr= strstr(query.ptr(), cut_start.str); + if (ptr) + { + query.replace((uint32) (ptr - query.ptr()), (uint32) cut_start.length, + set_ts.str, (uint32) set_ts.length); + } + ptr= strstr(query.ptr(), cut_end.str); + if (ptr) + { + query.replace((uint32) (ptr - query.ptr()), (uint32) cut_end.length, + set_ts.str, (uint32) set_ts.length); + } + } query.append(STRING_WITH_LEN(" CONNECTION='"), cs); query.append_for_single_quote(table_s->connect_string.str, table_s->connect_string.length); From 2fbf7514078c4d0171d96a309174d8dedcc9e0e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 29 May 2020 12:00:31 +0300 Subject: [PATCH 51/59] MDEV-22456 after-merge fix: Avoid functional change to rw_lock_s_unlock() In the merge 9e6e43551fc61bc34152f8d60f5d72f0d3814787 we replaced direct use of std::atomic with a wrapper class, so that dict_index_t::lock will support the default assignment operator. As part of that change, one occurrence of std::memory_order_release was accidentally replaced with std::memory_order_relaxed. Thanks to Sergey Vojtovich for noticing this. --- storage/innobase/include/sync0rw.ic | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/storage/innobase/include/sync0rw.ic b/storage/innobase/include/sync0rw.ic index 70723b05944..603e902d01c 100644 --- a/storage/innobase/include/sync0rw.ic +++ b/storage/innobase/include/sync0rw.ic @@ -355,7 +355,8 @@ rw_lock_s_unlock_func( ut_d(rw_lock_remove_debug_info(lock, pass, RW_LOCK_S)); /* Increment lock_word to indicate 1 less reader */ - int32_t lock_word = lock->lock_word.fetch_add(1); + int32_t lock_word = lock->lock_word.fetch_add( + 1, std::memory_order_release); if (lock_word == -1 || lock_word == -X_LOCK_HALF_DECR - 1) { /* wait_ex waiter exists. It may not be asleep, but we signal From e2d7da498231a303854a45455b0b9753af54e3be Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 28 May 2020 01:43:21 +0200 Subject: [PATCH 52/59] Remove unused WiX source file --- win/packaging/custom_ui.wxs | 183 ------------------------------------ 1 file changed, 183 deletions(-) delete mode 100644 win/packaging/custom_ui.wxs diff --git a/win/packaging/custom_ui.wxs b/win/packaging/custom_ui.wxs deleted file mode 100644 index 70fa3ba3abd..00000000000 --- a/win/packaging/custom_ui.wxs +++ /dev/null @@ -1,183 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1 - - - - - - - 1 - - - - Create default [ProductName] instance - - - - {\WixUI_Font_Title}Default instance properties - - - - - - - Service '[SERVICENAME]' will be removed - - - - Remove default database directory '[DATABASELOCATION]' - - - - - 1 - - - 1 - - - 1 - - - - Remove default [ProductName] database - - - - {\WixUI_Font_Title}Default instance properties - - - - - - - - - - 1 - 1 - - - - WixUI_InstallMode = "Change" - !DBInstance=3 - WixUI_InstallMode = "Remove" - - - - - - - - - - - - - - - - - - SERVICENAME - - - - - - - - - - - - Running mysql_install_db.exe - - - - - - - - - - - - - - - - - - - - - - - - CMDLINE_SERVICENAME - - CMDLINE_DATABASELOCATION - - - - CMDLINE_SERVICENAME - - CMDLINE_DATABASELOCATION - - - \ No newline at end of file From ff72f36948985a0dcd2811e4d8dd66e600badc0e Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 28 May 2020 02:06:23 +0200 Subject: [PATCH 53/59] MSI installer : Use CAQuietExec64 on Win64 , not CAQuietExec It works, but irritates people who look into the log and see traces of 32bit custom action server. --- win/packaging/create_msi.cmake.in | 2 ++ win/packaging/extra.wxs.in | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/win/packaging/create_msi.cmake.in b/win/packaging/create_msi.cmake.in index 21c59dd4f38..c589f98becc 100644 --- a/win/packaging/create_msi.cmake.in +++ b/win/packaging/create_msi.cmake.in @@ -70,10 +70,12 @@ IF(CMAKE_SIZEOF_VOID_P EQUAL 8) SET(Win64 " Win64='yes'") SET(Platform x64) SET(PlatformProgramFilesFolder ProgramFiles64Folder) + SET(CA_QUIET_EXEC CAQuietExec64) ELSE() SET(CANDLE_ARCH -arch x86) SET(Platform x86) SET(PlatformProgramFilesFolder ProgramFilesFolder) + SET(CA_QUIET_EXEC CAQuietExec) SET(Win64) ENDIF() diff --git a/win/packaging/extra.wxs.in b/win/packaging/extra.wxs.in index 3aba129fdf3..4c8e1a5a973 100644 --- a/win/packaging/extra.wxs.in +++ b/win/packaging/extra.wxs.in @@ -666,7 +666,7 @@ - From b00cd3e453f9986d0290b4a7c0ca48c509bdb0ef Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 28 May 2020 15:02:10 +0200 Subject: [PATCH 54/59] MDEV-22743 Windows 10 MSI installer : port in use is not determined when checking for free port, use the same logic (IPv6 socket address / dual socket), like the server would. Previous solution for testing whether port is free was trying to bind IPv4 socket on INADDR_ANY. This not work now on some reason, that attempt succeeds, even if there is an existing IPv6-dual socket listening on 0.0.0.0:3306 --- win/packaging/ca/CustomAction.cpp | 97 ++++++++++++++++++++++++------- 1 file changed, 76 insertions(+), 21 deletions(-) diff --git a/win/packaging/ca/CustomAction.cpp b/win/packaging/ca/CustomAction.cpp index 4005bd6e73b..48d56ba1183 100644 --- a/win/packaging/ca/CustomAction.cpp +++ b/win/packaging/ca/CustomAction.cpp @@ -20,6 +20,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ #undef NOMINMAX #include +#include #include #include #include @@ -33,6 +34,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ #include #include + #define ONE_MB 1048576 UINT ExecRemoveDataDirectory(wchar_t *dir) { @@ -255,36 +257,89 @@ bool ExecRemoveService(const wchar_t *name) return ret; } -/* - Check if port is free by trying to bind to the port -*/ -bool IsPortFree(short port) +/* Find whether TCP port is in use by trying to bind to the port. */ +static bool IsPortInUse(unsigned short port) { - WORD wVersionRequested; - WSADATA wsaData; + struct addrinfo* ai, * a; + struct addrinfo hints {}; - wVersionRequested = MAKEWORD(2, 2); + char port_buf[NI_MAXSERV]; + SOCKET ip_sock = INVALID_SOCKET; + hints.ai_flags = AI_PASSIVE; + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = AF_UNSPEC; + snprintf(port_buf, NI_MAXSERV, "%u", (unsigned)port); - WSAStartup(wVersionRequested, &wsaData); - - struct sockaddr_in sin; - SOCKET sock; - sock = socket(AF_INET, SOCK_STREAM, 0); - if(sock == -1) + if (getaddrinfo(NULL, port_buf, &hints, &ai)) { return false; } - sin.sin_port = htons(port); - sin.sin_addr.s_addr = 0; - sin.sin_addr.s_addr = INADDR_ANY; - sin.sin_family = AF_INET; - if(bind(sock, (struct sockaddr *)&sin,sizeof(struct sockaddr_in) ) == -1) + + /* + Prefer IPv6 socket to IPv4, since we'll use IPv6 dual socket, + which coveres both IP versions. + */ + for (a = ai; a; a = a->ai_next) + { + if (a->ai_family == AF_INET6 && + (ip_sock = socket(a->ai_family, a->ai_socktype, a->ai_protocol)) != INVALID_SOCKET) + { + break; + } + } + + if (ip_sock == INVALID_SOCKET) + { + for (a = ai; a; a = a->ai_next) + { + if (ai->ai_family == AF_INET && + (ip_sock = socket(a->ai_family, a->ai_socktype, a->ai_protocol)) != INVALID_SOCKET) + { + break; + } + } + } + + if (ip_sock == INVALID_SOCKET) { return false; } - closesocket(sock); + + /* Use SO_EXCLUSIVEADDRUSE to prevent multiple binding. */ + int arg = 1; + setsockopt(ip_sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&arg, sizeof(arg)); + + /* Allow dual socket, so that IPv4 and IPv6 are both covered.*/ + if (a->ai_family == AF_INET6) + { + arg = 0; + setsockopt(ip_sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&arg, sizeof(arg)); + } + + bool in_use = false; + if (bind(ip_sock, a->ai_addr, a->ai_addrlen) == SOCKET_ERROR) + { + DWORD last_error = WSAGetLastError(); + in_use = (last_error == WSAEADDRINUSE || last_error == WSAEACCES); + } + + freeaddrinfo(ai); + closesocket(ip_sock); + return in_use; +} + + +/* + Check if TCP port is free +*/ +bool IsPortFree(unsigned short port) +{ + WORD wVersionRequested = MAKEWORD(2, 2); + WSADATA wsaData; + WSAStartup(wVersionRequested, &wsaData); + bool in_use = IsPortInUse(port); WSACleanup(); - return true; + return !in_use; } @@ -634,7 +689,7 @@ extern "C" UINT __stdcall CheckDatabaseProperties (MSIHANDLE hInstall) goto LExit; } - short port = (short)_wtoi(Port); + unsigned short port = (unsigned short)_wtoi(Port); if (!IsPortFree(port)) { ErrorMsg = From 069200267db4cb79bbd55d06cd7ea9e1acd86ce8 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 28 May 2020 15:03:28 +0200 Subject: [PATCH 55/59] Fix cmake warning - custom command succeeds without creating own OUTPUT. --- sql/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 19fdd788207..17779adcff8 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -404,6 +404,7 @@ IF(WIN32 AND MYSQLD_EXECUTABLE) COMMAND ${CMAKE_COMMAND} -E make_directory data COMMAND ${CMAKE_COMMAND} -E chdir data ${CMAKE_COMMAND} ${CONFIG_PARAM} -P ${CMAKE_CURRENT_BINARY_DIR}/create_initial_db.cmake + COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/initdb.dep WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/ DEPENDS mysqld ) From 6e6a4227c050eefda70fd5d4f906e9274cb12f39 Mon Sep 17 00:00:00 2001 From: Kentoku SHIBA Date: Thu, 28 May 2020 10:21:46 +0900 Subject: [PATCH 56/59] Add grn_db_fin_mecab_tokenizer to finalyze mecab tokenizer --- storage/mroonga/vendor/groonga/lib/db.c | 3 ++ .../mroonga/vendor/groonga/lib/tokenizers.c | 30 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/storage/mroonga/vendor/groonga/lib/db.c b/storage/mroonga/vendor/groonga/lib/db.c index cba22aa0e64..f3769f9aa4c 100644 --- a/storage/mroonga/vendor/groonga/lib/db.c +++ b/storage/mroonga/vendor/groonga/lib/db.c @@ -445,6 +445,9 @@ grn_db_close(grn_ctx *ctx, grn_obj *db) ctx_used_db = ctx->impl && ctx->impl->db == db; if (ctx_used_db) { +#ifdef GRN_WITH_MECAB + grn_db_fin_mecab_tokenizer(ctx); +#endif grn_ctx_loader_clear(ctx); if (ctx->impl->parser) { grn_expr_parser_close(ctx); diff --git a/storage/mroonga/vendor/groonga/lib/tokenizers.c b/storage/mroonga/vendor/groonga/lib/tokenizers.c index 11f274e72db..3daacce7ef9 100644 --- a/storage/mroonga/vendor/groonga/lib/tokenizers.c +++ b/storage/mroonga/vendor/groonga/lib/tokenizers.c @@ -797,6 +797,36 @@ grn_db_init_mecab_tokenizer(grn_ctx *ctx) } } +void +grn_db_fin_mecab_tokenizer(grn_ctx *ctx) +{ + switch (GRN_CTX_GET_ENCODING(ctx)) { + case GRN_ENC_EUC_JP : + case GRN_ENC_UTF8 : + case GRN_ENC_SJIS : +#if defined(GRN_EMBEDDED) && defined(GRN_WITH_MECAB) + { + GRN_PLUGIN_DECLARE_FUNCTIONS(tokenizers_mecab); + GRN_PLUGIN_IMPL_NAME_TAGGED(fin, tokenizers_mecab)(ctx); + } +#else /* defined(GRN_EMBEDDED) && defined(GRN_WITH_MECAB) */ + { + const char *mecab_plugin_name = "tokenizers/mecab"; + char *path; + path = grn_plugin_find_path(ctx, mecab_plugin_name); + if (path) { + GRN_FREE(path); + grn_plugin_unregister(ctx, mecab_plugin_name); + } + } +#endif /* defined(GRN_EMBEDDED) && defined(GRN_WITH_MECAB) */ + break; + default : + break; + } + return; +} + #define DEF_TOKENIZER(name, init, next, fin, vars)\ (grn_proc_create(ctx, (name), (sizeof(name) - 1),\ GRN_PROC_TOKENIZER, (init), (next), (fin), 3, (vars))) From 38ea795bb622cce6f7178291ed737ca7396a124a Mon Sep 17 00:00:00 2001 From: Kentoku SHIBA Date: Fri, 29 May 2020 01:51:30 +0900 Subject: [PATCH 57/59] Add a counter to avoid multiple initialization of Groonga mecab tokenizer --- storage/mroonga/vendor/groonga/lib/grn_tokenizers.h | 1 + .../mroonga/vendor/groonga/plugins/tokenizers/mecab.c | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/storage/mroonga/vendor/groonga/lib/grn_tokenizers.h b/storage/mroonga/vendor/groonga/lib/grn_tokenizers.h index e90dbfc0b31..81ac2ab6c46 100644 --- a/storage/mroonga/vendor/groonga/lib/grn_tokenizers.h +++ b/storage/mroonga/vendor/groonga/lib/grn_tokenizers.h @@ -30,6 +30,7 @@ grn_rc grn_tokenizers_init(void); grn_rc grn_tokenizers_fin(void); grn_rc grn_db_init_mecab_tokenizer(grn_ctx *ctx); +void grn_db_fin_mecab_tokenizer(grn_ctx *ctx); grn_rc grn_db_init_builtin_tokenizers(grn_ctx *ctx); #ifdef __cplusplus diff --git a/storage/mroonga/vendor/groonga/plugins/tokenizers/mecab.c b/storage/mroonga/vendor/groonga/plugins/tokenizers/mecab.c index 3dd969a89c5..cabf2c94e53 100644 --- a/storage/mroonga/vendor/groonga/plugins/tokenizers/mecab.c +++ b/storage/mroonga/vendor/groonga/plugins/tokenizers/mecab.c @@ -31,6 +31,7 @@ #include #include +static unsigned int sole_mecab_init_counter = 0; static mecab_t *sole_mecab = NULL; static grn_plugin_mutex *sole_mecab_mutex = NULL; static grn_encoding sole_mecab_encoding = GRN_ENC_NONE; @@ -563,6 +564,11 @@ check_mecab_dictionary_encoding(grn_ctx *ctx) grn_rc GRN_PLUGIN_INIT(grn_ctx *ctx) { + ++sole_mecab_init_counter; + if (sole_mecab_init_counter > 1) + { + return GRN_SUCCESS; + } { char env[GRN_ENV_BUFFER_SIZE]; @@ -636,6 +642,11 @@ GRN_PLUGIN_REGISTER(grn_ctx *ctx) grn_rc GRN_PLUGIN_FIN(grn_ctx *ctx) { + --sole_mecab_init_counter; + if (sole_mecab_init_counter > 0) + { + return GRN_SUCCESS; + } if (sole_mecab) { mecab_destroy(sole_mecab); sole_mecab = NULL; From 4783494a5e21939424ecca92bf23d2df65528266 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Fri, 29 May 2020 16:18:50 +0300 Subject: [PATCH 58/59] MDEV-22283 Server crashes in key_copy or unexpected error 156 (The table already existed in the storage engine) Wrong algorithm of closing partitions on error doesn't close last partition. --- .../suite/versioning/r/partition.result | 23 +++++++++++++++--- mysql-test/suite/versioning/t/partition.test | 24 +++++++++++++++---- sql/ha_partition.cc | 10 ++++---- 3 files changed, 46 insertions(+), 11 deletions(-) diff --git a/mysql-test/suite/versioning/r/partition.result b/mysql-test/suite/versioning/r/partition.result index 68512fbea6a..cba47537e7a 100644 --- a/mysql-test/suite/versioning/r/partition.result +++ b/mysql-test/suite/versioning/r/partition.result @@ -658,6 +658,23 @@ pk f update t1 set f=pk; delete from t1; drop table t1; -# Test cleanup -drop database test; -create database test; +# +# MDEV-22283 Server crashes in key_copy or unexpected error 156: The table already existed in the storage engine +# +create table t1 (a int primary key) engine=aria page_checksum=0 +with system versioning +partition by system_time (partition p1 history, partition pn current); +alter table t1 add partition (partition p2 history); +Warnings: +Warning 4115 Maybe missing parameters: no rotation condition for multiple HISTORY partitions. +show table status; +Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment Max_index_length Temporary +t1 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL Got error 174 "Fatal error during initialization of handler" from storage engine Aria NULL NULL +Warnings: +Warning 1030 Got error 174 "Fatal error during initialization of handler" from storage engine Aria +drop table t1; +create table t1 (b int) engine=aria row_format=dynamic with system versioning +partition by system_time (partition p1 history, partition pn current); +insert into t1 values (1); +replace into t1 values (1); +drop table t1; diff --git a/mysql-test/suite/versioning/t/partition.test b/mysql-test/suite/versioning/t/partition.test index a03fb33108d..271c17ac7dc 100644 --- a/mysql-test/suite/versioning/t/partition.test +++ b/mysql-test/suite/versioning/t/partition.test @@ -574,7 +574,6 @@ update t1 left join t2 on a > b set b= 2 order by b; # cleanup drop table t1, t2; ---source suite/versioning/common_finish.inc --echo # --echo # MDEV-17091 Assertion `old_part_id == m_last_part' failed in --echo # ha_partition::update_row or `part_id == m_last_part' in @@ -596,6 +595,23 @@ update t1 set f=pk; delete from t1; drop table t1; ---echo # Test cleanup -drop database test; -create database test; +--echo # +--echo # MDEV-22283 Server crashes in key_copy or unexpected error 156: The table already existed in the storage engine +--echo # +create table t1 (a int primary key) engine=aria page_checksum=0 +with system versioning +partition by system_time (partition p1 history, partition pn current); + +alter table t1 add partition (partition p2 history); +show table status; +drop table t1; + +create table t1 (b int) engine=aria row_format=dynamic with system versioning +partition by system_time (partition p1 history, partition pn current); +insert into t1 values (1); +replace into t1 values (1); + +# cleanup +drop table t1; + +--source suite/versioning/common_finish.inc diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index af60ca68c82..6af69fc67b6 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -3657,11 +3657,13 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) err_handler: DEBUG_SYNC(ha_thd(), "partition_open_error"); - file= &m_file[m_tot_parts - 1]; - while (file-- != m_file) + DBUG_ASSERT(m_tot_parts > 0); + for (uint i= m_tot_parts - 1; ; --i) { - if (bitmap_is_set(&m_opened_partitions, (uint)(file - m_file))) - (*file)->ha_close(); + if (bitmap_is_set(&m_opened_partitions, i)) + m_file[i]->ha_close(); + if (!i) + break; } err_alloc: free_partition_bitmaps(); From 2e1d10ecacc8ccfc0bda8a199c741ee36df981be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Sat, 30 May 2020 10:48:11 +0300 Subject: [PATCH 59/59] Add end-of-test markers to ease merges --- mysql-test/suite/versioning/r/partition.result | 1 + mysql-test/suite/versioning/r/partition_innodb.result | 1 + mysql-test/suite/versioning/t/partition.test | 2 ++ mysql-test/suite/versioning/t/partition_innodb.test | 2 ++ 4 files changed, 6 insertions(+) diff --git a/mysql-test/suite/versioning/r/partition.result b/mysql-test/suite/versioning/r/partition.result index 4d0426512e6..ab206db1213 100644 --- a/mysql-test/suite/versioning/r/partition.result +++ b/mysql-test/suite/versioning/r/partition.result @@ -673,3 +673,4 @@ delete from v1; ERROR HY000: Table 't1' was locked with a READ lock and can't be updated drop view v1; drop table t1; +# End of 10.3 tests diff --git a/mysql-test/suite/versioning/r/partition_innodb.result b/mysql-test/suite/versioning/r/partition_innodb.result index 2489af91b7f..b9870797cd2 100644 --- a/mysql-test/suite/versioning/r/partition_innodb.result +++ b/mysql-test/suite/versioning/r/partition_innodb.result @@ -77,3 +77,4 @@ select * from t1 where i > 0 or pk = 1000 limit 1; pk i c 1 1 a drop table t1; +# End of 10.3 tests diff --git a/mysql-test/suite/versioning/t/partition.test b/mysql-test/suite/versioning/t/partition.test index 6cd88caf1a9..726b991c00e 100644 --- a/mysql-test/suite/versioning/t/partition.test +++ b/mysql-test/suite/versioning/t/partition.test @@ -635,4 +635,6 @@ delete from v1; drop view v1; drop table t1; +--echo # End of 10.3 tests + --source suite/versioning/common_finish.inc diff --git a/mysql-test/suite/versioning/t/partition_innodb.test b/mysql-test/suite/versioning/t/partition_innodb.test index 79130dbbccf..29ec58af13c 100644 --- a/mysql-test/suite/versioning/t/partition_innodb.test +++ b/mysql-test/suite/versioning/t/partition_innodb.test @@ -89,4 +89,6 @@ replace into t1 select * from t1; select * from t1 where i > 0 or pk = 1000 limit 1; drop table t1; +--echo # End of 10.3 tests + --source suite/versioning/common_finish.inc