diff --git a/mysql-test/suite/binlog/r/binlog_signedness_parse.result b/mysql-test/suite/binlog/r/binlog_signedness_parse.result new file mode 100644 index 0000000000000..f118f3a5a35ad --- /dev/null +++ b/mysql-test/suite/binlog/r/binlog_signedness_parse.result @@ -0,0 +1,65 @@ +ALTER DATABASE test CHARACTER SET latin1 COLLATE latin1_swedish_ci; +RESET MASTER; +SET GLOBAL binlog_row_metadata = MINIMAL; +# +# Test 1: Non-numeric column between two numeric columns does not +# consume a signedness bit (signed INT, CHAR, unsigned INT) +# +CREATE TABLE t1(c_signed INT, c_char CHAR(10), c_unsigned INT UNSIGNED); +INSERT INTO t1 VALUES(-1, 'x', 1); +# Columns(INT, +# CHAR(10) CHARSET latin1 COLLATE latin1_swedish_ci, +# INT UNSIGNED) +DROP TABLE t1; +RESET MASTER; +# +# Test 2: Byte boundary — 8 signed numerics then a non-numeric then an +# unsigned numeric. The non-numeric must not shift the bit +# cursor into the second byte prematurely. +# +CREATE TABLE t1( +c1 TINYINT, c2 TINYINT, c3 TINYINT, c4 TINYINT, +c5 TINYINT, c6 TINYINT, c7 TINYINT, c8 TINYINT, +c_text TEXT, +c9 TINYINT UNSIGNED); +INSERT INTO t1(c1) VALUES(1); +# Columns(TINYINT, +# TINYINT, +# TINYINT, +# TINYINT, +# TINYINT, +# TINYINT, +# TINYINT, +# TINYINT, +# TEXT CHARSET latin1 COLLATE latin1_swedish_ci, +# TINYINT UNSIGNED) +DROP TABLE t1; +RESET MASTER; +# +# Test 3: Multiple non-numeric columns between numeric columns across +# a byte boundary +# +CREATE TABLE t1( +c1 TINYINT UNSIGNED, c2 TINYINT UNSIGNED, +c3 TINYINT UNSIGNED, c4 TINYINT UNSIGNED, +c5 TINYINT UNSIGNED, c6 TINYINT UNSIGNED, +c7 TINYINT UNSIGNED, c8 TINYINT UNSIGNED, +c_blob BLOB, c_varchar VARCHAR(100), +c9 TINYINT, c10 TINYINT UNSIGNED); +INSERT INTO t1(c1) VALUES(1); +# Columns(TINYINT UNSIGNED, +# TINYINT UNSIGNED, +# TINYINT UNSIGNED, +# TINYINT UNSIGNED, +# TINYINT UNSIGNED, +# TINYINT UNSIGNED, +# TINYINT UNSIGNED, +# TINYINT UNSIGNED, +# BLOB, +# VARCHAR(100) CHARSET latin1 COLLATE latin1_swedish_ci, +# TINYINT, +# TINYINT UNSIGNED) +DROP TABLE t1; +RESET MASTER; +SET GLOBAL binlog_row_metadata = NO_LOG; +ALTER DATABASE test CHARACTER SET utf8mb4 COLLATE utf8mb4_uca1400_ai_ci; diff --git a/mysql-test/suite/binlog/r/binlog_table_map_optional_metadata.result b/mysql-test/suite/binlog/r/binlog_table_map_optional_metadata.result index 4a290a0b8a81b..9b351c3735608 100644 --- a/mysql-test/suite/binlog/r/binlog_table_map_optional_metadata.result +++ b/mysql-test/suite/binlog/r/binlog_table_map_optional_metadata.result @@ -9,7 +9,7 @@ c_datetime DATETIME, c_datetime_f DATETIME(3), c_timestamp TIMESTAMP NOT NULL DEFAULT NOW(), c_timestamp_f TIMESTAMP(3) DEFAULT "2017-1-1 10:10:10"); INSERT INTO t1(c_year) VALUES(2017); -# Columns(YEAR, +# Columns(YEAR UNSIGNED, # DATE, # TIME, # TIME(3), diff --git a/mysql-test/suite/binlog/t/binlog_signedness_parse.test b/mysql-test/suite/binlog/t/binlog_signedness_parse.test new file mode 100644 index 0000000000000..665f8aefc0fa9 --- /dev/null +++ b/mysql-test/suite/binlog/t/binlog_signedness_parse.test @@ -0,0 +1,70 @@ +################################################################################ +# Regression tests for parse_signedness() in log_event.cc. +# +# The old implementation (upstream) pushed all bits from every byte into a flat +# vector without regard to column type. The new implementation iterates over +# column metadata and only consumes a bit for columns where is_numeric_type() +# returns true, skipping non-numeric columns. +# +# Tests focus on: +# 1. YEAR is treated as a numeric type and always shown UNSIGNED. +# 2. Non-numeric columns interspersed between numeric columns do not consume +# a signedness bit, so the second numeric column reads the correct bit. +# 3. Correct behavior at byte boundaries when non-numeric columns appear +# between the 8th and 9th numeric column. +################################################################################ +--source include/have_binlog_format_row.inc +--source include/test_db_charset_latin1.inc + +RESET MASTER; +SET GLOBAL binlog_row_metadata = MINIMAL; + +--let $MYSQLD_DATADIR= `select @@datadir` +--let $binlog_file= $MYSQLD_DATADIR/master-bin.000001 + +--echo # +--echo # Test 1: Non-numeric column between two numeric columns does not +--echo # consume a signedness bit (signed INT, CHAR, unsigned INT) +--echo # +CREATE TABLE t1(c_signed INT, c_char CHAR(10), c_unsigned INT UNSIGNED); +INSERT INTO t1 VALUES(-1, 'x', 1); +--source suite/binlog/include/print_optional_metadata.inc + +DROP TABLE t1; +RESET MASTER; + +--echo # +--echo # Test 2: Byte boundary — 8 signed numerics then a non-numeric then an +--echo # unsigned numeric. The non-numeric must not shift the bit +--echo # cursor into the second byte prematurely. +--echo # +CREATE TABLE t1( + c1 TINYINT, c2 TINYINT, c3 TINYINT, c4 TINYINT, + c5 TINYINT, c6 TINYINT, c7 TINYINT, c8 TINYINT, + c_text TEXT, + c9 TINYINT UNSIGNED); +INSERT INTO t1(c1) VALUES(1); +--source suite/binlog/include/print_optional_metadata.inc + +DROP TABLE t1; +RESET MASTER; + +--echo # +--echo # Test 3: Multiple non-numeric columns between numeric columns across +--echo # a byte boundary +--echo # +CREATE TABLE t1( + c1 TINYINT UNSIGNED, c2 TINYINT UNSIGNED, + c3 TINYINT UNSIGNED, c4 TINYINT UNSIGNED, + c5 TINYINT UNSIGNED, c6 TINYINT UNSIGNED, + c7 TINYINT UNSIGNED, c8 TINYINT UNSIGNED, + c_blob BLOB, c_varchar VARCHAR(100), + c9 TINYINT, c10 TINYINT UNSIGNED); +INSERT INTO t1(c1) VALUES(1); +--source suite/binlog/include/print_optional_metadata.inc + +DROP TABLE t1; +RESET MASTER; + +SET GLOBAL binlog_row_metadata = NO_LOG; +--source include/test_db_charset_restore.inc diff --git a/sql/log_event.cc b/sql/log_event.cc index c814b65066cdd..e7e040c980e67 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -92,13 +92,13 @@ TYPELIB binlog_checksum_typelib= CREATE_TYPELIB_FOR(binlog_checksum_type_names); */ #define FMT_G_BUFSIZE(PREC) (3 + (PREC) + 5 + 1) -/* +/* replication event checksum is introduced in the following "checksum-home" version. The checksum-aware servers extract FD's version to decide whether the FD event carries checksum info. - TODO: correct the constant when it has been determined - (which main tree to push and when) + TODO: correct the constant when it has been determined + (which main tree to push and when) */ const Version checksum_version_split_mysql(5, 6, 1); const Version checksum_version_split_mariadb(5, 3, 0); @@ -287,7 +287,7 @@ char *str_to_hex(char *to, const uchar *from, size_t len) uint32 binlog_get_compress_len(uint32 len) { /* 5 for the begin content, 1 reserved for a '\0'*/ - return ALIGN_SIZE((BINLOG_COMPRESSED_HEADER_LEN + BINLOG_COMPRESSED_ORIGINAL_LENGTH_MAX_BYTES) + return ALIGN_SIZE((BINLOG_COMPRESSED_HEADER_LEN + BINLOG_COMPRESSED_ORIGINAL_LENGTH_MAX_BYTES) + compressBound(len) + 1); } @@ -326,7 +326,7 @@ int binlog_buf_compress(const uchar *src, uchar *dst, uint32 len, uint32 *comlen dst[2]= uchar(len); lenlen= 2; } - else + else { dst[1]= uchar(len); lenlen= 1; @@ -396,19 +396,19 @@ query_event_uncompress(const Format_description_log_event *description_event, (contain_checksum ? BINLOG_CHECKSUM_LEN : 0)); uint32 un_len= binlog_get_uncompress_len(tmp); - // bad event + // bad event if (comp_len < 0 || un_len == 0) return 1; *newlen= (ulong)(tmp - src) + un_len; if (contain_checksum) *newlen+= BINLOG_CHECKSUM_LEN; - + uint32 alloc_size= (uint32)ALIGN_SIZE(*newlen); - - if (alloc_size <= buf_size) + + if (alloc_size <= buf_size) new_dst= buf; - else + else { new_dst= (uchar *) my_malloc(PSI_INSTRUMENT_ME, alloc_size, MYF(MY_WME)); if (!new_dst) @@ -481,7 +481,7 @@ row_log_event_uncompress(const Format_description_log_event *description_event, type= (Log_event_type)(type - WRITE_ROWS_COMPRESSED_EVENT + WRITE_ROWS_EVENT); } - else + else { /* get the uncompressed event type */ type= (Log_event_type) @@ -516,9 +516,9 @@ row_log_event_uncompress(const Format_description_log_event *description_event, *newlen+= BINLOG_CHECKSUM_LEN; size_t alloc_size= ALIGN_SIZE(*newlen); - + *is_malloc= false; - if (alloc_size <= buf_size) + if (alloc_size <= buf_size) { new_dst= buf; } @@ -1015,7 +1015,7 @@ Log_event* Log_event::read_log_event(const uchar *buf, size_t event_len, In the RL case, the alg is kept in FD_e (@fdle) which is reset to the newer read-out event after its execution with possibly new alg descriptor. Therefore in a typical sequence of RL: - {FD_s^0, FD_m, E_m^1} E_m^1 + {FD_s^0, FD_m, E_m^1} E_m^1 will be verified with (A) of FD_m. See legends definition on MYSQL_BIN_LOG::relay_log_checksum_alg docs @@ -1035,7 +1035,7 @@ Log_event* Log_event::read_log_event(const uchar *buf, size_t event_len, DBUG_PRINT("info", ("Corrupt the event at Log_event::read_log_event(char*,...): byte on position %d", debug_cor_pos)); DBUG_SET("-d,corrupt_read_log_event_char"); } - ); + ); if (crc_check && event_checksum_test(const_cast(buf), event_len, alg)) { #ifdef MYSQL_CLIENT @@ -1225,7 +1225,7 @@ Log_event *Log_event::read_log_event_no_checksum( ev= new Format_description_log_event(buf, static_cast(event_len), fdle); break; -#if defined(HAVE_REPLICATION) +#if defined(HAVE_REPLICATION) case WRITE_ROWS_EVENT_V1: case WRITE_ROWS_EVENT: ev= new Write_rows_log_event(buf, event_len, fdle); @@ -1360,7 +1360,7 @@ Log_event *Log_event::read_log_event_no_checksum( DBUG_RETURN(0); #endif } - DBUG_RETURN(ev); + DBUG_RETURN(ev); } @@ -1477,7 +1477,7 @@ get_str_len_and_pointer(const Log_event::Byte **src, return 0; } -static void copy_str_and_move(const char **src, Log_event::Byte **dst, +static void copy_str_and_move(const char **src, Log_event::Byte **dst, size_t len) { memcpy(*dst, *src, len); @@ -1613,7 +1613,7 @@ Query_log_event::Query_log_event(const uchar *buf, uint event_len, */ /* variable-part: the status vars; only in MySQL 5.0 */ - + start= (Log_event::Byte*) (buf+post_header_len); end= (const Log_event::Byte*) (start+status_vars_len); for (const Log_event::Byte* pos= start; pos < end;) @@ -1875,7 +1875,7 @@ Query_log_event::Query_log_event(const uchar *buf, uint event_len, my_alloc call above? /sven */ - /* A 2nd variable part; this is common to all versions */ + /* A 2nd variable part; this is common to all versions */ memcpy((char*) start, end, data_len); // Copy db and query start[data_len]= '\0'; // End query with \0 (For safetly) db= (char *)start; @@ -2479,7 +2479,7 @@ Format_description_log_event::is_version_before_checksum(const master_version_sp /** @param buf buffer holding serialized FD event @param len netto (possible checksum is stripped off) length of the event buf - + @return the version-safe checksum alg descriptor where zero designates no checksum, 255 - the orginator is checksum-unaware (effectively no checksum) and the actual @@ -2497,7 +2497,7 @@ enum_binlog_checksum_alg get_checksum_alg(const uchar *buf, size_t len) buf + LOG_EVENT_MINIMAL_HEADER_LEN + ST_SERVER_VER_OFFSET, ST_SERVER_VER_LEN); version[ST_SERVER_VER_LEN - 1]= 0; - + Format_description_log_event::master_version_split version_split(version); ret= Format_description_log_event::is_version_before_checksum(&version_split) ? BINLOG_CHECKSUM_ALG_UNDEF @@ -3016,7 +3016,7 @@ User_var_log_event(const uchar *buf, uint event_len, if (is_null) { val_len= 0; - val= 0; + val= 0; } else { @@ -3073,7 +3073,7 @@ Append_block_log_event(const uchar *buf, uint len, :Log_event(buf, description_event),block(0) { DBUG_ENTER("Append_block_log_event::Append_block_log_event(char*,...)"); - uint8 common_header_len= description_event->common_header_len; + uint8 common_header_len= description_event->common_header_len; uint8 append_block_header_len= description_event->post_header_len[APPEND_BLOCK_EVENT-1]; uint total_header_len= common_header_len+append_block_header_len; @@ -3476,7 +3476,7 @@ int Rows_log_event::get_data_size() data_size+= no_bytes_in_export_map(&m_cols_ai); data_size+= (uint) (m_rows_cur - m_rows_buf); - return data_size; + return data_size; } @@ -3532,34 +3532,34 @@ bool Annotate_rows_log_event::is_valid() const /** @page How replication of field metadata works. - - When a table map is created, the master first calls - Table_map_log_event::save_field_metadata() which calculates how many - values will be in the field metadata. Only those fields that require the - extra data are added. The method also loops through all of the fields in + + When a table map is created, the master first calls + Table_map_log_event::save_field_metadata() which calculates how many + values will be in the field metadata. Only those fields that require the + extra data are added. The method also loops through all of the fields in the table calling the method Field::save_field_metadata() which returns the values for the field that will be saved in the metadata and replicated to the slave. Once all fields have been processed, the table map is written to the binlog adding the size of the field metadata and the field metadata to the end of the body of the table map. - When a table map is read on the slave, the field metadata is read from the - table map and passed to the table_def class constructor which saves the - field metadata from the table map into an array based on the type of the - field. Field metadata values not present (those fields that do not use extra - data) in the table map are initialized as zero (0). The array size is the + When a table map is read on the slave, the field metadata is read from the + table map and passed to the table_def class constructor which saves the + field metadata from the table map into an array based on the type of the + field. Field metadata values not present (those fields that do not use extra + data) in the table map are initialized as zero (0). The array size is the same as the columns for the table on the slave. - Additionally, values saved for field metadata on the master are saved as a + Additionally, values saved for field metadata on the master are saved as a string of bytes (uchar) in the binlog. A field may require 1 or more bytes - to store the information. In cases where values require multiple bytes - (e.g. values > 255), the endian-safe methods are used to properly encode + to store the information. In cases where values require multiple bytes + (e.g. values > 255), the endian-safe methods are used to properly encode the values on the master and decode them on the slave. When the field metadata values are captured on the slave, they are stored in an array of type uint16. This allows the least number of casts to prevent casting bugs when the field metadata is used in comparisons of field attributes. When the field metadata is used for calculating addresses in pointer math, the - type used is uint32. + type used is uint32. */ /* @@ -3718,109 +3718,183 @@ Table_map_log_event::~Table_map_log_event() /** Parses SIGNEDNESS field. - @param[out] vec stores the signedness flags extracted from field. + @param[out] column_metadata store all column metadata here (including signedness) @param[in] field SIGNEDNESS field in table_map_event. @param[in] length length of the field */ -static void parse_signedness(std::vector &vec, - unsigned char *field, unsigned int length) +using Optional_column_metadata= Table_map_log_event::Optional_metadata_fields::Column_metadata; +static void parse_signedness( + Dynamic_array& column_metadata, + unsigned char *field, unsigned int length) { - for (unsigned int i= 0; i < length; i++) + unsigned char *p= field; + unsigned char *field_end= p + length; + unsigned char unsigned_bitfield= 0; + unsigned char mask= 0; + + for (uint col= 0; col < column_metadata.size(); col++) { - for (unsigned char c= 0x80; c != 0; c>>= 1) - vec.push_back(field[i] & c); + auto &col_meta= column_metadata.at(col); + if (!is_numeric_type(col_meta.column_type)) + continue; + if (mask == 0) + { + if (p >= field_end) + return; + unsigned_bitfield= *p++; + mask= 0x80; + } + col_meta.is_unsigned= unsigned_bitfield & mask; + mask >>= 1; } } /** Parses DEFAULT_CHARSET field. - @param[out] default_charset stores collation numbers extracted from field. + @param[out] fields store all optional metadata here (including charsets) @param[in] field DEFAULT_CHARSET field in table_map_event. @param[in] length length of the field */ -static void parse_default_charset(Table_map_log_event::Optional_metadata_fields:: - Default_charset &default_charset, - unsigned char *field, unsigned int length) +static void parse_default_charset( + Table_map_log_event::Optional_metadata_fields& fields, + unsigned char *field, unsigned int length) { unsigned char* p= field; - - default_charset.default_charset= net_field_length(&p); + uint default_cs= net_field_length(&p); + for (uint i= 0; i < fields.m_column_metadata.size(); i++) + if (is_character_type(fields.m_column_metadata.at(i).column_type)) + fields.m_column_metadata.at(i).charset= default_cs; while (p < field + length) { unsigned int col_index= net_field_length(&p); - unsigned int col_charset= net_field_length(&p); + fields.m_column_metadata.at(col_index).charset= net_field_length(&p); + } +} + +/** + Parses ENUM_AND_SET_DEFAULT_CHARSET field. - default_charset.charset_pairs.push_back(std::make_pair(col_index, - col_charset)); + @param[out] fields store all optional metadata here (including charsets) + @param[in] field ENUM_AND_SET_DEFAULT_CHARSET field in table_map_event. + @param[in] length length of the field + */ +static void parse_enum_and_set_default_charset( + Table_map_log_event::Optional_metadata_fields& fields, + unsigned char *field, unsigned int length) +{ + unsigned char* p= field; + uint default_cs= net_field_length(&p); + for (uint i= 0; i < fields.m_column_metadata.size(); i++) + if (is_enum_or_set_type(fields.m_column_metadata.at(i).column_type)) + fields.m_column_metadata.at(i).enum_and_set_column_charset= default_cs; + while (p < field + length) + { + unsigned int col_index= net_field_length(&p); + fields.m_column_metadata.at(col_index).enum_and_set_column_charset= net_field_length(&p); } } /** Parses COLUMN_CHARSET field. - @param[out] vec stores collation numbers extracted from field. + @param[out] column_metadata store all column metadata here (including charsets) @param[in] field COLUMN_CHARSET field in table_map_event. @param[in] length length of the field */ -static void parse_column_charset(std::vector &vec, - unsigned char *field, unsigned int length) +static void parse_column_charset( + Dynamic_array& column_metadata, + unsigned char *field, unsigned int length) { unsigned char* p= field; + for (uint col= 0; p < field + length && col < column_metadata.size(); col++) + { + if (!is_character_type(column_metadata.at(col).column_type)) + continue; + column_metadata.at(col).charset= net_field_length(&p); + } +} - while (p < field + length) - vec.push_back(net_field_length(&p)); +/** + Parses ENUM_AND_SET_COLUMN_CHARSET field. + + @param[out] column_metadata store all column metadata here (including charsets) + @param[in] field ENUM_AND_SET_COLUMN_CHARSET field in table_map_event. + @param[in] length length of the field + */ +static void parse_enum_and_set_column_charset( + Dynamic_array& column_metadata, + unsigned char *field, unsigned int length) +{ + unsigned char* p= field; + for (uint col= 0; p < field + length && col < column_metadata.size(); col++) + { + if (!is_enum_or_set_type(column_metadata.at(col).column_type)) + continue; + column_metadata.at(col).enum_and_set_column_charset= net_field_length(&p); + } } /** Parses COLUMN_NAME field. @param[in] root Allocate memory here - @param[out] name Store column names extracted from field here + @param[out] column_metadata store all column metadata here (including column names) @param[in] field COLUMN_NAME field in table_map_event. @param[in] length length of the field */ -static bool parse_column_name(MEM_ROOT *root, LEX_CSTRING *name, - unsigned char *field, unsigned int length) +static bool parse_column_name(MEM_ROOT *root, + Dynamic_array& column_metadata, + unsigned char *field, unsigned int length) { - for (uchar *end= field+length; field < end ; name++) + unsigned int col = 0; + for (uchar *end= field+length; field < end && col < column_metadata.size(); col++) { + LEX_CSTRING& name = column_metadata.at(col).column_name; uint name_length= net_field_length(&field); - if (!(name->str= strmake_root(root, (char*) field, name_length))) + if (!(name.str= strmake_root(root, (char*) field, name_length))) return 1; - name->length= name_length; + name.length= name_length; field+= name_length; } - name->str= 0; // End marker return 0; } /** Parses SET_STR_VALUE/ENUM_STR_VALUE field. - @param[out] vec stores SET/ENUM column's string values extracted from - field. Each SET/ENUM column's string values are stored - into a string separate vector. All of them are stored - in 'vec'. - @param[in] field COLUMN_NAME field in table_map_event. - @param[in] length length of the field + @param[out] column_metadata store all column metadata here (including + set/enum strings) + @param[in] field SET_STR_VALUE/ENUM_STR_VALUE field in table_map_event. + @param[in] length length of the field + @param[in] column_type type of the given column (i.e. MYSQL_TYPE_XYZ) + @param[in] var_to_set pointer-to-member selecting which str_vector to fill */ -static void parse_set_str_value(std::vector &vec, - unsigned char *field, unsigned int length) +static void parse_set_str_value( + Dynamic_array &column_metadata, + unsigned char *field, unsigned int length, + uchar column_type, + Table_map_log_event::Optional_metadata_fields::str_vector + Optional_column_metadata::*var_to_set) { unsigned char* p= field; - while (p < field + length) + for (uint col= 0; p < field + length && col < column_metadata.size(); col++) { + if (column_metadata.at(col).column_type != column_type) + continue; + unsigned int count= net_field_length(&p); + auto* column_strings= &(column_metadata.at(col).*var_to_set); + + if (column_strings->reserve(count)) + return; - vec.push_back(std::vector()); for (unsigned int i= 0; i < count; i++) { unsigned len1= net_field_length(&p); - vec.back().push_back(std::string(reinterpret_cast(p), len1)); + column_strings->append(LEX_CSTRING{reinterpret_cast(p), len1}); p+= len1; } } @@ -3829,52 +3903,56 @@ static void parse_set_str_value(std::vector &vec, - unsigned char *field, unsigned int length) +static void parse_geometry_type( + Dynamic_array &column_metadata, + unsigned char *field, unsigned int length) { unsigned char* p= field; - while (p < field + length) - vec.push_back(net_field_length(&p)); + for (unsigned int col = 0; p < field + length && col < column_metadata.size(); col++) + { + auto& col_metadata = column_metadata.at(col); + if (col_metadata.column_type == MYSQL_TYPE_GEOMETRY) + { + col_metadata.geometry_type = net_field_length(&p); + } + } } /** Parses SIMPLE_PRIMARY_KEY field. - @param[out] vec stores primary key's column information extracted from - field. Each column has an index and a prefix which are - stored as a unit_pair. prefix is always 0 for - SIMPLE_PRIMARY_KEY field. + @param[out] column_metadata store all column metadata here (including + primary key information). The prefix is always 0 for SIMPLE_PRIMARY_KEY. @param[in] field SIMPLE_PRIMARY_KEY field in table_map_event. @param[in] length length of the field */ -static void parse_simple_pk(std::vector &vec, - unsigned char *field, unsigned int length) +static void parse_simple_pk( + Dynamic_array &column_metadata, + unsigned char *field, unsigned int length) { unsigned char* p= field; while (p < field + length) - vec.push_back(std::make_pair(net_field_length(&p), 0)); + column_metadata.at(net_field_length(&p)).primary_key = 0; } /** Parses PRIMARY_KEY_WITH_PREFIX field. - @param[out] vec stores primary key's column information extracted from - field. Each column has an index and a prefix which are - stored as a unit_pair. + @param[out] column_metadata store all column metadata here (including + primary key lengths) @param[in] field PRIMARY_KEY_WITH_PREFIX field in table_map_event. @param[in] length length of the field */ - -static void parse_pk_with_prefix(std::vector &vec, - unsigned char *field, unsigned int length) +static void parse_pk_with_prefix( + Dynamic_array &column_metadata, + unsigned char *field, unsigned int length) { unsigned char* p= field; @@ -3882,21 +3960,41 @@ static void parse_pk_with_prefix(std::vector +#include "mysql/psi/psi_base.h" #include "rpl_constants.h" +#include "sql_array.h" #include #include #include #include #include +#include #include +static inline bool is_numeric_type(uint type) +{ + switch (type) + { + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_NEWDECIMAL: + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: + case MYSQL_TYPE_YEAR: + return true; + default: + return false; + } +} + +static inline bool is_character_type(uint type) +{ + switch (type) + { + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_BLOB: + // Base class is blob for geom type + case MYSQL_TYPE_GEOMETRY: + return true; + default: + return false; + } +} + +static inline bool is_enum_or_set_type(uint type) +{ + return type == MYSQL_TYPE_ENUM || type == MYSQL_TYPE_SET; +} + #ifdef MYSQL_CLIENT #include "sql_const.h" #include "rpl_utility.h" @@ -4418,6 +4461,42 @@ class table_def; */ +/** + Advances a field metadata pointer past the metadata for one column. + The advancement size depends on the column type, mirroring the layout + written by Field::save_field_metadata(). + + @param[in] type The resolved column type (e.g. MYSQL_TYPE_ENUM, + not MYSQL_TYPE_STRING, for enum columns) + @param[in,out] meta_ptr Pointer into the field metadata blob; advanced + in-place by the number of bytes consumed. +*/ +inline void advance_field_metadata_ptr(uint type, const uchar** meta_ptr) +{ + switch (type) { + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: + case MYSQL_TYPE_TIMESTAMP2: + case MYSQL_TYPE_DATETIME2: + case MYSQL_TYPE_TIME2: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_GEOMETRY: + (*meta_ptr)++; + break; + case MYSQL_TYPE_NEWDECIMAL: + case MYSQL_TYPE_BIT: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_STRING: + (*meta_ptr) += 2; + break; + default: + break; + } +} + class Table_map_log_event : public Log_event { public: @@ -4501,42 +4580,32 @@ class Table_map_log_event : public Log_event }; struct Optional_metadata_fields { - typedef std::pair uint_pair; - typedef std::vector str_vector; + typedef Dynamic_array str_vector; bool allocation_error; /* Set if allocation of data structures fails */ - struct Default_charset + struct Column_metadata { - Default_charset() : default_charset(0) {} - bool empty() const { return default_charset == 0; } + LEX_CSTRING column_name{ nullptr, 0 }; + uchar column_type{}; + bool is_unsigned{ false }; + + // each str_vector stores values for the enum/set column + str_vector enum_str_values{PSI_INSTRUMENT_MEM, 0}; + str_vector set_str_values{PSI_INSTRUMENT_MEM, 0}; - // Default charset for the columns which are not in charset_pairs. - unsigned int default_charset; + uint geometry_type{ 0 }; - /* The uint_pair means . */ - std::vector charset_pairs; + // Prefix length of primary key (if applicable), where a value of 0 + // indicates to use the entire column as a primary key + // (SIMPLE_PRIMARY_KEY). + std::optional primary_key{}; + + // 0 means no charset information present + uint charset{ 0 }; + uint enum_and_set_column_charset{ 0 }; }; - // Contents of DEFAULT_CHARSET field is converted into Default_charset. - Default_charset m_default_charset; - // Contents of ENUM_AND_SET_DEFAULT_CHARSET are converted into - // Default_charset. - Default_charset m_enum_and_set_default_charset; - std::vector m_signedness; - // Character set number of every string column - std::vector m_column_charset; - // Character set number of every ENUM or SET column. - std::vector m_enum_and_set_column_charset; - LEX_CSTRING *m_column_name; - // each str_vector stores values of one enum/set column - std::vector m_enum_str_value; - std::vector m_set_str_value; - std::vector m_geometry_type; - /* - The uint_pair means . Prefix length is 0 if - whole column value is used. - */ - std::vector m_primary_key; + Dynamic_array m_column_metadata; /* It parses m_optional_metadata and populates into above variables. @@ -4549,6 +4618,8 @@ class Table_map_log_event : public Log_event @param[in] only_column_names Only read column names */ Optional_metadata_fields(MEM_ROOT *root, uint master_cols, + const uchar* column_types, + const uchar* field_metadata, uchar* optional_metadata, size_t optional_metadata_len, bool only_column_names); @@ -4691,11 +4762,7 @@ class Table_map_log_event : public Log_event bool init_primary_key_field(); #endif -#ifdef MYSQL_CLIENT - class Charset_iterator; - class Default_charset_iterator; - class Column_charset_iterator; -#endif + char const *m_dbnam; size_t m_dblen; char const *m_tblnam; diff --git a/sql/log_event_client.cc b/sql/log_event_client.cc index 468c196d8b6f2..fb68d72e61928 100644 --- a/sql/log_event_client.cc +++ b/sql/log_event_client.cc @@ -18,6 +18,7 @@ #include "log_event.h" +#include #ifndef MYSQL_CLIENT #error MYSQL_CLIENT must be defined here #endif @@ -286,45 +287,6 @@ static bool hexdump_data_to_io_cache(IO_CACHE *file, return 1; } -static inline bool is_numeric_type(uint type) -{ - switch (type) - { - case MYSQL_TYPE_TINY: - case MYSQL_TYPE_SHORT: - case MYSQL_TYPE_INT24: - case MYSQL_TYPE_LONG: - case MYSQL_TYPE_LONGLONG: - case MYSQL_TYPE_NEWDECIMAL: - case MYSQL_TYPE_FLOAT: - case MYSQL_TYPE_DOUBLE: - return true; - default: - return false; - } - return false; -} - -static inline bool is_character_type(uint type) -{ - switch (type) - { - case MYSQL_TYPE_STRING: - case MYSQL_TYPE_VAR_STRING: - case MYSQL_TYPE_VARCHAR: - case MYSQL_TYPE_BLOB: - // Base class is blob for geom type - case MYSQL_TYPE_GEOMETRY: - return true; - default: - return false; - } -} - -static inline bool is_enum_or_set_type(uint type) { - return type == MYSQL_TYPE_ENUM || type == MYSQL_TYPE_SET; -} - /* Log_event::print_header() @@ -433,7 +395,7 @@ my_b_write_quoted(IO_CACHE *file, const uchar *ptr, uint length) /** Prints a bit string to io cache in format b'1010'. - + @param[in] file IO cache @param[in] ptr Pointer to string @param[in] nbits Number of bits @@ -456,11 +418,11 @@ my_b_write_bit(IO_CACHE *file, const uchar *ptr, uint nbits) Prints a packed string to io cache. The string consists of length packed to 1 or 2 bytes, followed by string data itself. - + @param[in] file IO cache @param[in] ptr Pointer to string @param[in] length String size - + @retval - number of bytes scanned. */ static size_t @@ -483,7 +445,7 @@ my_b_write_quoted_with_length(IO_CACHE *file, const uchar *ptr, uint length) /** Prints a 32-bit number in both signed and unsigned representation - + @param[in] file IO cache @param[in] sl Signed number @param[in] ul Unsigned number @@ -501,14 +463,14 @@ my_b_write_sint32_and_uint32(IO_CACHE *file, int32 si, uint32 ui) /** Print a packed value of the given SQL type into IO cache - + @param[in] file IO cache @param[in] ptr Pointer to string @param[in] type Column type @param[in] meta Column meta information @param[out] typestr SQL type string buffer (for verbose output) @param[out] typestr_length Size of typestr - + @retval - number of bytes scanned from ptr. Except in case of NULL, in which case we return 1 to indicate ok */ @@ -526,7 +488,7 @@ log_event_print_value(IO_CACHE *file, PRINT_EVENT_INFO *print_event_info, { uint byte0= meta >> 8; uint byte1= meta & 0xFF; - + if ((byte0 & 0x30) != 0x30) { /* a long CHAR() field: see #37426 */ @@ -575,7 +537,7 @@ log_event_print_value(IO_CACHE *file, PRINT_EVENT_INFO *print_event_info, my_b_write_sint32_and_uint32(file, si, ui); return 2; } - + case MYSQL_TYPE_INT24: { strmake(typestr, "MEDIUMINT", typestr_length); @@ -603,7 +565,7 @@ log_event_print_value(IO_CACHE *file, PRINT_EVENT_INFO *print_event_info, { ulonglong ui= uint8korr(ptr); longlong10_to_str((longlong) ui, tmp, 10); - my_b_printf(file, " (%s)", tmp); + my_b_printf(file, " (%s)", tmp); } return 8; } @@ -651,7 +613,7 @@ log_event_print_value(IO_CACHE *file, PRINT_EVENT_INFO *print_event_info, my_b_printf(file, tmp, "%s"); return 8; } - + case MYSQL_TYPE_BIT: { /* Meta-data: bit_len, bytes_in_rec, 2 bytes */ @@ -780,7 +742,7 @@ log_event_print_value(IO_CACHE *file, PRINT_EVENT_INFO *print_event_info, my_b_printf(file , "'%s'", buf); return 3; } - + case MYSQL_TYPE_DATE: { strmake(typestr, "DATE", typestr_length); @@ -793,7 +755,7 @@ log_event_print_value(IO_CACHE *file, PRINT_EVENT_INFO *print_event_info, (int)(i32 % 32L)); return 3; } - + case MYSQL_TYPE_YEAR: { strmake(typestr, "YEAR", typestr_length); @@ -804,7 +766,7 @@ log_event_print_value(IO_CACHE *file, PRINT_EVENT_INFO *print_event_info, my_b_printf(file, "%04d", i32+ 1900); return 1; } - + case MYSQL_TYPE_ENUM: switch (meta & 0xFF) { case 1: @@ -825,11 +787,11 @@ log_event_print_value(IO_CACHE *file, PRINT_EVENT_INFO *print_event_info, return 2; } default: - my_b_printf(file, "!! Unknown ENUM packlen=%d", meta & 0xFF); + my_b_printf(file, "!! Unknown ENUM packlen=%d", meta & 0xFF); return 0; } break; - + case MYSQL_TYPE_SET: my_snprintf(typestr, typestr_length, "SET(%d bytes)", meta & 0xFF); if (!ptr) @@ -837,7 +799,7 @@ log_event_print_value(IO_CACHE *file, PRINT_EVENT_INFO *print_event_info, my_b_write_bit(file, ptr , (meta & 0xFF) * 8); return meta & 0xFF; - + case MYSQL_TYPE_BLOB_COMPRESSED: case MYSQL_TYPE_BLOB: switch (meta) { @@ -932,14 +894,14 @@ log_event_print_value(IO_CACHE *file, PRINT_EVENT_INFO *print_event_info, /** Print a packed row into IO cache - + @param[in] file IO cache @param[in] td Table definition @param[in] print_event_into Print parameters @param[in] cols_bitmap Column bitmaps. @param[in] value Pointer to packed row @param[in] prefix Row's SQL clause ("SET", "WHERE", etc) - + @retval 0 error # number of bytes scanned. */ @@ -975,7 +937,7 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td, for (uint i= 0; i < (uint)td->size(); i ++) { size_t size; - int is_null= (null_bits[null_bit_index / 8] + int is_null= (null_bits[null_bit_index / 8] >> (null_bit_index % 8)) & 0x01; if (bitmap_is_set(cols_bitmap, i) == 0) @@ -1933,7 +1895,7 @@ bool Query_log_event::print_query_header(IO_CACHE* file, different_db= memcmp(print_event_info->db, db, db_len + 1); if (different_db) memcpy(print_event_info->db, db, db_len + 1); - if (db[0] && different_db) + if (db[0] && different_db) if (my_b_printf(file, "use %`s%s\n", db, print_event_info->delimiter)) goto err; } @@ -1973,7 +1935,7 @@ bool Query_log_event::print_query_header(IO_CACHE* file, if (likely(flags2_inited)) /* likely as this will mainly read 5.0 logs */ { /* tmp is a bitmask of bits which have changed. */ - if (likely(print_event_info->flags2_inited)) + if (likely(print_event_info->flags2_inited)) /* All bits which have changed */ tmp= (print_event_info->flags2) ^ flags2; else /* that's the first Query event we read */ @@ -3062,7 +3024,7 @@ bool Annotate_rows_log_event::print(FILE *file, PRINT_EVENT_INFO *pinfo) representation taken from a binary log. It resets m_dbnam and m_dblen and rewrites temp_buf with new db name. - RETURN + RETURN 0 - Success other - Error */ @@ -3174,7 +3136,10 @@ bool Table_map_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info) { MEM_ROOT root; init_alloc_root(0, &root, 4096, 0, 0); - Optional_metadata_fields fields(&root, m_colcnt, + Optional_metadata_fields fields(&root, + m_colcnt, + m_coltype, + m_field_metadata, m_optional_metadata, m_optional_metadata_len, 0); @@ -3215,93 +3180,7 @@ bool Table_map_log_event::print_body(PRINT_EVENT_INFO *print_event_info) return 0; } -/** - Interface for iterator over charset columns. -*/ -class Table_map_log_event::Charset_iterator -{ - public: - typedef Table_map_log_event::Optional_metadata_fields::Default_charset - Default_charset; - virtual const CHARSET_INFO *next()= 0; - virtual ~Charset_iterator(){}; - /** - Factory method to create an instance of the appropriate subclass. - */ - static std::unique_ptr create_charset_iterator( - const Default_charset &default_charset, - const std::vector &column_charset); -}; -/** - Implementation of charset iterator for the DEFAULT_CHARSET type. -*/ -class Table_map_log_event::Default_charset_iterator : public Charset_iterator -{ - public: - Default_charset_iterator(const Default_charset &default_charset) - : m_iterator(default_charset.charset_pairs.begin()), - m_end(default_charset.charset_pairs.end()), - m_column_index(0), - m_default_charset_info( - get_charset(default_charset.default_charset, 0)) {} - - const CHARSET_INFO *next() override { - const CHARSET_INFO *ret; - if (m_iterator != m_end && m_iterator->first == m_column_index) { - ret = get_charset(m_iterator->second, 0); - m_iterator++; - } else - ret = m_default_charset_info; - m_column_index++; - return ret; - } - ~Default_charset_iterator(){}; - - private: - std::vector::const_iterator m_iterator, - m_end; - uint m_column_index; - const CHARSET_INFO *m_default_charset_info; -}; -//Table_map_log_event::Default_charset_iterator::~Default_charset_iterator(){int a=8;a++; a--;}; -/** - Implementation of charset iterator for the COLUMNT_CHARSET type. -*/ -class Table_map_log_event::Column_charset_iterator : public Charset_iterator -{ - public: - Column_charset_iterator(const std::vector &column_charset) - : m_iterator(column_charset.begin()), m_end(column_charset.end()) {} - - const CHARSET_INFO *next() override { - const CHARSET_INFO *ret = nullptr; - if (m_iterator != m_end) { - ret = get_charset(*m_iterator, 0); - m_iterator++; - } - return ret; - } - - ~Column_charset_iterator(){}; - private: - std::vector::const_iterator m_iterator; - std::vector::const_iterator m_end; -}; -//Table_map_log_event::Column_charset_iterator::~Column_charset_iterator(){int a=8;a++; a--;}; - -std::unique_ptr -Table_map_log_event::Charset_iterator::create_charset_iterator( - const Default_charset &default_charset, - const std::vector &column_charset) -{ - if (!default_charset.empty()) - return std::unique_ptr( - new Default_charset_iterator(default_charset)); - else - return std::unique_ptr( - new Column_charset_iterator(column_charset)); -} /** return the string name of a type. @@ -3336,43 +3215,36 @@ static void get_type_name(uint type, unsigned char** meta_ptr, my_snprintf(typestr, typestr_length, "BIGINT"); break; case MYSQL_TYPE_NEWDECIMAL: - my_snprintf(typestr, typestr_length, "DECIMAL(%d,%d)", - (*meta_ptr)[0], (*meta_ptr)[1]); - (*meta_ptr)+= 2; - break; + my_snprintf(typestr, typestr_length, "DECIMAL(%d,%d)", + (*meta_ptr)[0], (*meta_ptr)[1]); + break; case MYSQL_TYPE_FLOAT: my_snprintf(typestr, typestr_length, "FLOAT"); - (*meta_ptr)++; break; case MYSQL_TYPE_DOUBLE: my_snprintf(typestr, typestr_length, "DOUBLE"); - (*meta_ptr)++; break; case MYSQL_TYPE_BIT: my_snprintf(typestr, typestr_length, "BIT(%d)", (((*meta_ptr)[0])) + (*meta_ptr)[1]*8); - (*meta_ptr)+= 2; break; case MYSQL_TYPE_TIMESTAMP2: if (**meta_ptr != 0) my_snprintf(typestr, typestr_length, "TIMESTAMP(%d)", **meta_ptr); else my_snprintf(typestr, typestr_length, "TIMESTAMP"); - (*meta_ptr)++; break; case MYSQL_TYPE_DATETIME2: if (**meta_ptr != 0) my_snprintf(typestr, typestr_length, "DATETIME(%d)", **meta_ptr); else my_snprintf(typestr, typestr_length, "DATETIME"); - (*meta_ptr)++; break; case MYSQL_TYPE_TIME2: if (**meta_ptr != 0) my_snprintf(typestr, typestr_length, "TIME(%d)", **meta_ptr); else my_snprintf(typestr, typestr_length, "TIME"); - (*meta_ptr)++; break; case MYSQL_TYPE_NEWDATE: case MYSQL_TYPE_DATE: @@ -3383,11 +3255,9 @@ static void get_type_name(uint type, unsigned char** meta_ptr, break; case MYSQL_TYPE_ENUM: my_snprintf(typestr, typestr_length, "ENUM"); - (*meta_ptr)+= 2; break; case MYSQL_TYPE_SET: my_snprintf(typestr, typestr_length, "SET"); - (*meta_ptr)+= 2; break; case MYSQL_TYPE_BLOB: { @@ -3406,8 +3276,6 @@ static void get_type_name(uint type, unsigned char** meta_ptr, my_snprintf(typestr, typestr_length, "INVALID_%s(%d)", type_name, size); else my_snprintf(typestr, typestr_length, "%s%s", names[size], type_name); - - (*meta_ptr)++; } break; case MYSQL_TYPE_VARCHAR: @@ -3418,8 +3286,6 @@ static void get_type_name(uint type, unsigned char** meta_ptr, else my_snprintf(typestr, typestr_length, "VARBINARY(%d)", uint2korr(*meta_ptr)); - - (*meta_ptr)+= 2; break; case MYSQL_TYPE_STRING: { @@ -3431,8 +3297,6 @@ static void get_type_name(uint type, unsigned char** meta_ptr, my_snprintf(typestr, typestr_length, "CHAR(%d)", len/cs->mbmaxlen); else my_snprintf(typestr, typestr_length, "BINARY(%d)", len); - - (*meta_ptr)+= 2; } break; case MYSQL_TYPE_GEOMETRY: @@ -3446,43 +3310,26 @@ static void get_type_name(uint type, unsigned char** meta_ptr, else my_snprintf(typestr, typestr_length, "INVALID_GEOMETRY_TYPE(%u)", geometry_type); - (*meta_ptr)++; } break; default: *typestr= 0; break; } + advance_field_metadata_ptr(type, (const uchar**) meta_ptr); } void Table_map_log_event::print_columns(IO_CACHE *file, const Optional_metadata_fields &fields) { unsigned char* field_metadata_ptr= m_field_metadata; - std::vector::const_iterator signedness_it= fields.m_signedness.begin(); - - std::unique_ptr charset_it = - Charset_iterator::create_charset_iterator(fields.m_default_charset, - fields.m_column_charset); - std::unique_ptr enum_and_set_charset_it = - Charset_iterator::create_charset_iterator( - fields.m_enum_and_set_default_charset, - fields.m_enum_and_set_column_charset); - std::vector::const_iterator - set_str_values_it= fields.m_set_str_value.begin(); - std::vector::const_iterator - enum_str_values_it= fields.m_enum_str_value.begin(); - std::vector::const_iterator geometry_type_it= - fields.m_geometry_type.begin(); - LEX_CSTRING *col_names= fields.m_column_name; - - uint geometry_type= 0; my_b_printf(file, "# Columns("); for (unsigned long i= 0; i < m_colcnt; i++) { - uint real_type = m_coltype[i]; + const Optional_metadata_fields::Column_metadata& column_metadata= fields.m_column_metadata.at(i); + uint real_type= m_coltype[i]; if (real_type == MYSQL_TYPE_STRING && (*field_metadata_ptr == MYSQL_TYPE_ENUM || *field_metadata_ptr == MYSQL_TYPE_SET)) @@ -3490,32 +3337,31 @@ void Table_map_log_event::print_columns(IO_CACHE *file, // Get current column's collation id if it is a character, enum, // or set column - const CHARSET_INFO *cs = NULL; + const CHARSET_INFO *cs= NULL; if (is_character_type(real_type)) - cs = charset_it->next(); + { + if (column_metadata.charset) + cs= get_charset(column_metadata.charset, 0); + } else if (is_enum_or_set_type(real_type)) - cs = enum_and_set_charset_it->next(); - - // Print column name - if (col_names && col_names->str) { - pretty_print_identifier(file, col_names->str, col_names->length); - my_b_printf(file, " "); - col_names++; + if (column_metadata.enum_and_set_column_charset) + cs= get_charset(column_metadata.enum_and_set_column_charset, 0); } - // update geometry_type for geometry columns - if (real_type == MYSQL_TYPE_GEOMETRY) + // Print column name + const LEX_CSTRING& col_name= column_metadata.column_name; + if (col_name.str) { - geometry_type= (geometry_type_it != fields.m_geometry_type.end()) ? - *geometry_type_it++ : 0; + pretty_print_identifier(file, col_name.str, col_name.length); + my_b_printf(file, " "); } // print column type const uint TYPE_NAME_LEN = 100; char type_name[TYPE_NAME_LEN]; get_type_name(real_type, &field_metadata_ptr, cs, type_name, - TYPE_NAME_LEN, geometry_type); + TYPE_NAME_LEN, column_metadata.geometry_type); if (type_name[0] == '\0') { @@ -3525,12 +3371,10 @@ void Table_map_log_event::print_columns(IO_CACHE *file, my_b_printf(file, "%s", type_name); // Print UNSIGNED for numeric column - if (is_numeric_type(real_type) && - signedness_it != fields.m_signedness.end()) + if (is_numeric_type(real_type)) { - if (*signedness_it == true) + if (column_metadata.is_unsigned == true) my_b_printf(file, " UNSIGNED"); - signedness_it++; } // if the column is not marked as 'null', print 'not null' @@ -3539,27 +3383,23 @@ void Table_map_log_event::print_columns(IO_CACHE *file, // Print string values of SET and ENUM column const Optional_metadata_fields::str_vector *str_values= NULL; - if (real_type == MYSQL_TYPE_ENUM && - enum_str_values_it != fields.m_enum_str_value.end()) + if (real_type == MYSQL_TYPE_ENUM && !column_metadata.enum_str_values.empty()) { - str_values= &(*enum_str_values_it); - enum_str_values_it++; + str_values= &column_metadata.enum_str_values; } - else if (real_type == MYSQL_TYPE_SET && - set_str_values_it != fields.m_set_str_value.end()) + else if (real_type == MYSQL_TYPE_SET && !column_metadata.set_str_values.empty()) { - str_values= &(*set_str_values_it); - set_str_values_it++; + str_values= &column_metadata.set_str_values; } if (str_values != NULL) { const char *separator= "("; - for (Optional_metadata_fields::str_vector::const_iterator it= - str_values->begin(); it != str_values->end(); it++) + for (const LEX_CSTRING *it= str_values->front(); it != str_values->end(); + it++) { my_b_printf(file, "%s", separator); - pretty_print_str(file, it->c_str(), it->size()); + pretty_print_str(file, it->str, it->length); separator= ","; } my_b_printf(file, ")"); @@ -3578,27 +3418,40 @@ void Table_map_log_event::print_columns(IO_CACHE *file, void Table_map_log_event::print_primary_key (IO_CACHE *file,const Optional_metadata_fields &fields) { - if (!fields.m_primary_key.empty()) + auto& col_metadata= fields.m_column_metadata; + + bool has_pk= false; + for (uint i = 0; i < col_metadata.size(); i++) { + if (col_metadata[i].primary_key.has_value()) { + has_pk= true; + break; + } + } + + if (has_pk) { my_b_printf(file, "# Primary Key("); - std::vector::const_iterator it= - fields.m_primary_key.begin(); - - for (; it != fields.m_primary_key.end(); it++) + const char *delimiter = ""; + for (uint col_idx= 0; col_idx < col_metadata.size(); col_idx++) { - if (it != fields.m_primary_key.begin()) - my_b_printf(file, ", "); + const auto& col= col_metadata.at(col_idx); + if (!col.primary_key.has_value()) + continue; + + my_b_printf(file, delimiter); // Print column name or column index - if (!fields.m_column_name) - my_b_printf(file, "%u", it->first); + const LEX_CSTRING& column_name= col.column_name; + if (column_name.str) + my_b_printf(file, "%s", column_name.str); else - my_b_printf(file, "%s", fields.m_column_name[it->first].str); + my_b_printf(file, "%u", col_idx); // Print prefix length - if (it->second != 0) - my_b_printf(file, "(%u)", it->second); + if (col.primary_key.value() != 0) + my_b_printf(file, "(%u)", col.primary_key.value()); + delimiter= ", "; } my_b_printf(file, ")\n"); diff --git a/sql/rpl_utility.h b/sql/rpl_utility.h index 10051d8d35e07..4e6f87d0c6e4c 100644 --- a/sql/rpl_utility.h +++ b/sql/rpl_utility.h @@ -94,6 +94,7 @@ class table_def { return static_cast(m_type[index]); } + const uchar *field_types() const { return m_type; } /* Return a representation of the type data for one field. diff --git a/sql/rpl_utility_server.cc b/sql/rpl_utility_server.cc index cade88c093024..f97f595aea69b 100644 --- a/sql/rpl_utility_server.cc +++ b/sql/rpl_utility_server.cc @@ -1351,10 +1351,13 @@ void RPL_TABLE_LIST::create_column_mapping(rpl_group_info *rgi) Table_map_log_event::Optional_metadata_fields opt_metadata(rgi->thd->mem_root, master_cols, + m_tabledef.field_types(), + nullptr, (uchar*) m_tabledef.optional_metadata.str, m_tabledef.optional_metadata.length, 1); - if (!opt_metadata.m_column_name) + if (!opt_metadata.m_column_metadata.empty() && + !opt_metadata.m_column_metadata.at(0).column_name.str) { /* If there are no column names provided in the optional metadata @@ -1366,7 +1369,7 @@ void RPL_TABLE_LIST::create_column_mapping(rpl_group_info *rgi) for (uint col= 0; col < master_cols; col++) { - const LEX_CSTRING *field_name= &opt_metadata.m_column_name[col]; + const LEX_CSTRING *field_name= &opt_metadata.m_column_metadata.at(col).column_name; Field *field= table->find_field_by_name(field_name); if (unlikely(!field)) { diff --git a/sql/sql_array.h b/sql/sql_array.h index 812e8aae12b6d..c2d5c36cfa5d6 100644 --- a/sql/sql_array.h +++ b/sql/sql_array.h @@ -197,6 +197,8 @@ template class Dynamic_array size_t size() const { return array.elements; } + bool empty() const { return array.elements == 0; } + const Elem *end() const { return back() + 1;