From eee533cc136bf5d37522ef601f6d5efbcf210142 Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 2 Mar 2026 15:07:02 -0500 Subject: [PATCH 01/16] MDEV-38144: update Optional_metadata_fields to use MariaDB types Currently Optional_metadata_fields has many members that use classes from the C++ standard library, most notably the use of std::vector and std::string. These members have been updated to use existing MariaDB types instead. --- sql/log_event.cc | 54 +++++++++++++++++++++++++---------------- sql/log_event.h | 24 ++++++++++-------- sql/log_event_client.cc | 53 +++++++++++++++++++--------------------- sql/sql_array.h | 6 +++-- 4 files changed, 76 insertions(+), 61 deletions(-) diff --git a/sql/log_event.cc b/sql/log_event.cc index c814b65066cdd..408e98ab36525 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -17,6 +17,7 @@ #include "mariadb.h" +#include "mysql/psi/psi_base.h" #include "sql_priv.h" #include "handler.h" #ifndef MYSQL_CLIENT @@ -3722,13 +3723,13 @@ Table_map_log_event::~Table_map_log_event() @param[in] field SIGNEDNESS field in table_map_event. @param[in] length length of the field */ -static void parse_signedness(std::vector &vec, +static void parse_signedness(Dynamic_array &vec, unsigned char *field, unsigned int length) { for (unsigned int i= 0; i < length; i++) { for (unsigned char c= 0x80; c != 0; c>>= 1) - vec.push_back(field[i] & c); + vec.append(field[i] & c); } } @@ -3751,8 +3752,8 @@ static void parse_default_charset(Table_map_log_event::Optional_metadata_fields: unsigned int col_index= net_field_length(&p); unsigned int col_charset= net_field_length(&p); - default_charset.charset_pairs.push_back(std::make_pair(col_index, - col_charset)); + default_charset.charset_pairs.append(std::make_pair(col_index, + col_charset)); } } @@ -3763,13 +3764,13 @@ static void parse_default_charset(Table_map_log_event::Optional_metadata_fields: @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, +static void parse_column_charset(Dynamic_array &vec, unsigned char *field, unsigned int length) { unsigned char* p= field; while (p < field + length) - vec.push_back(net_field_length(&p)); + vec.append(net_field_length(&p)); } /** @@ -3806,9 +3807,10 @@ static bool parse_column_name(MEM_ROOT *root, LEX_CSTRING *name, @param[in] field COLUMN_NAME field in table_map_event. @param[in] length length of the field */ -static void parse_set_str_value(std::vector &vec, - unsigned char *field, unsigned int length) +static void parse_set_str_value( + Dynamic_array + &vec, + unsigned char *field, unsigned int length) { unsigned char* p= field; @@ -3816,11 +3818,14 @@ static void parse_set_str_value(std::vector()); + if (vec.reserve(vec.elements() + 1)) + return; + vec.elements(vec.elements() + 1); + vec.back()->init(PSI_INSTRUMENT_MEM); for (unsigned int i= 0; i < count; i++) { unsigned len1= net_field_length(&p); - vec.back().push_back(std::string(reinterpret_cast(p), len1)); + vec.back()->append(LEX_CSTRING{reinterpret_cast(p), len1}); p+= len1; } } @@ -3833,13 +3838,13 @@ static void parse_set_str_value(std::vector &vec, +static void parse_geometry_type(Dynamic_array &vec, unsigned char *field, unsigned int length) { unsigned char* p= field; while (p < field + length) - vec.push_back(net_field_length(&p)); + vec.append(net_field_length(&p)); } /** @@ -3852,14 +3857,15 @@ static void parse_geometry_type(std::vector &vec, @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 + &vec, + 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)); + vec.append(std::make_pair(net_field_length(&p), 0)); } /** @@ -3872,9 +3878,10 @@ static void parse_simple_pk(std::vector &vec, - unsigned char *field, unsigned int length) +static void parse_pk_with_prefix( + Dynamic_array + &vec, + unsigned char *field, unsigned int length) { unsigned char* p= field; @@ -3882,7 +3889,7 @@ static void parse_pk_with_prefix(std::vector #include "rpl_constants.h" +#include "sql_array.h" #include #include #include @@ -4502,19 +4503,22 @@ 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 { - Default_charset() : default_charset(0) {} + Default_charset() : default_charset(0), charset_pairs(PSI_INSTRUMENT_MEM) + { + } bool empty() const { return default_charset == 0; } // Default charset for the columns which are not in charset_pairs. unsigned int default_charset; /* The uint_pair means . */ - std::vector charset_pairs; + // std::vector charset_pairs; + Dynamic_array charset_pairs; }; // Contents of DEFAULT_CHARSET field is converted into Default_charset. @@ -4522,21 +4526,21 @@ class Table_map_log_event : public Log_event // Contents of ENUM_AND_SET_DEFAULT_CHARSET are converted into // Default_charset. Default_charset m_enum_and_set_default_charset; - std::vector m_signedness; + Dynamic_array m_signedness; // Character set number of every string column - std::vector m_column_charset; + Dynamic_array m_column_charset; // Character set number of every ENUM or SET column. - std::vector m_enum_and_set_column_charset; + Dynamic_array 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; + Dynamic_array m_enum_str_value; + Dynamic_array m_set_str_value; + Dynamic_array 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_primary_key; /* It parses m_optional_metadata and populates into above variables. diff --git a/sql/log_event_client.cc b/sql/log_event_client.cc index 468c196d8b6f2..4164b3df3e0d7 100644 --- a/sql/log_event_client.cc +++ b/sql/log_event_client.cc @@ -3230,7 +3230,7 @@ class Table_map_log_event::Charset_iterator */ static std::unique_ptr create_charset_iterator( const Default_charset &default_charset, - const std::vector &column_charset); + const Dynamic_array &column_charset); }; /** @@ -3240,9 +3240,8 @@ 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_iterator(default_charset.charset_pairs.front()), + m_end(default_charset.charset_pairs.end()), m_column_index(0), m_default_charset_info( get_charset(default_charset.default_charset, 0)) {} @@ -3259,8 +3258,8 @@ class Table_map_log_event::Default_charset_iterator : public Charset_iterator ~Default_charset_iterator(){}; private: - std::vector::const_iterator m_iterator, - m_end; + const Optional_metadata_fields::uint_pair *m_iterator; + const Optional_metadata_fields::uint_pair *m_end; uint m_column_index; const CHARSET_INFO *m_default_charset_info; }; @@ -3271,8 +3270,9 @@ class Table_map_log_event::Default_charset_iterator : public Charset_iterator 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()) {} + Column_charset_iterator(const Dynamic_array &column_charset) + : m_iterator(column_charset.front()), m_end(column_charset.end()) + {} const CHARSET_INFO *next() override { const CHARSET_INFO *ret = nullptr; @@ -3285,15 +3285,15 @@ class Table_map_log_event::Column_charset_iterator : public Charset_iterator ~Column_charset_iterator(){}; private: - std::vector::const_iterator m_iterator; - std::vector::const_iterator m_end; + const uint *m_iterator; + const uint *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) + const Dynamic_array &column_charset) { if (!default_charset.empty()) return std::unique_ptr( @@ -3459,7 +3459,7 @@ 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(); + const bool *signedness_it= fields.m_signedness.front(); std::unique_ptr charset_it = Charset_iterator::create_charset_iterator(fields.m_default_charset, @@ -3468,12 +3468,11 @@ void Table_map_log_event::print_columns(IO_CACHE *file, 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(); + const Optional_metadata_fields::str_vector *set_str_values_it= + fields.m_set_str_value.front(); + const Optional_metadata_fields::str_vector *enum_str_values_it= + fields.m_enum_str_value.front(); + const uint *geometry_type_it= fields.m_geometry_type.front(); LEX_CSTRING *col_names= fields.m_column_name; uint geometry_type= 0; @@ -3542,24 +3541,24 @@ void Table_map_log_event::print_columns(IO_CACHE *file, if (real_type == MYSQL_TYPE_ENUM && enum_str_values_it != fields.m_enum_str_value.end()) { - str_values= &(*enum_str_values_it); + str_values= enum_str_values_it; enum_str_values_it++; } else if (real_type == MYSQL_TYPE_SET && set_str_values_it != fields.m_set_str_value.end()) { - str_values= &(*set_str_values_it); + str_values= set_str_values_it; set_str_values_it++; } 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, ")"); @@ -3582,12 +3581,10 @@ void Table_map_log_event::print_primary_key { my_b_printf(file, "# Primary Key("); - std::vector::const_iterator it= - fields.m_primary_key.begin(); - - for (; it != fields.m_primary_key.end(); it++) + for (auto *it= fields.m_primary_key.front(); + it != fields.m_primary_key.end(); it++) { - if (it != fields.m_primary_key.begin()) + if (it != fields.m_primary_key.front()) my_b_printf(file, ", "); // Print column name or column index diff --git a/sql/sql_array.h b/sql/sql_array.h index 812e8aae12b6d..8e72244a98f8c 100644 --- a/sql/sql_array.h +++ b/sql/sql_array.h @@ -42,7 +42,7 @@ template class Bounds_checked_array {} void reset() { m_array= NULL; m_size= 0; } - + void reset(Element_type *array_arg, size_t size_arg) { m_array= array_arg; @@ -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; @@ -282,7 +284,7 @@ template class Dynamic_array size_t old_size= elements(); if (reserve(new_size)) return true; - + if (new_size > old_size) { set_dynamic(&array, (uchar*)&default_val, new_size - 1); From bbb5fff11074a23c9c875d8abaa415cd741e90fc Mon Sep 17 00:00:00 2001 From: Eric Date: Sun, 8 Mar 2026 11:12:13 -0400 Subject: [PATCH 02/16] Create new Column_metadata struct Beginning to create a per-field aggregate struct for all of the possibly required metadata values. Slowly going to start incorporating the existing vectors into this. --- sql/log_event.cc | 31 +++++++++++++++++-------------- sql/log_event.h | 8 +++++++- sql/log_event_client.cc | 14 +++++++------- sql/rpl_utility_server.cc | 5 +++-- 4 files changed, 34 insertions(+), 24 deletions(-) diff --git a/sql/log_event.cc b/sql/log_event.cc index 408e98ab36525..660e36aa83843 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -3777,23 +3777,25 @@ static void parse_column_charset(Dynamic_array &vec, 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; } @@ -3902,13 +3904,18 @@ Optional_metadata_fields(MEM_ROOT *root, uint master_columns, m_enum_and_set_column_charset(PSI_INSTRUMENT_MEM), m_enum_str_value(PSI_INSTRUMENT_MEM), m_set_str_value(PSI_INSTRUMENT_MEM), m_geometry_type(PSI_INSTRUMENT_MEM), - m_primary_key(PSI_INSTRUMENT_MEM) + m_primary_key(PSI_INSTRUMENT_MEM), + m_column_metadata(PSI_INSTRUMENT_MEM, master_columns) { unsigned int len; uchar *metadata_end; allocation_error= 0; - m_column_name= 0; + + for (uint i= 0; i < master_columns; i++) + if (m_column_metadata.append(Column_metadata())) + goto error; + if (optional_metadata == NULL) return; @@ -3935,11 +3942,7 @@ Optional_metadata_fields(MEM_ROOT *root, uint master_columns, parse_column_charset(m_column_charset, field, len); break; case COLUMN_NAME: - if (!(m_column_name= (LEX_CSTRING*) alloc_root(root, - sizeof(LEX_CSTRING) * - (master_columns +1 )))) - goto error; - if (parse_column_name(root, m_column_name, field, len)) + if (parse_column_name(root, m_column_metadata, field, len)) goto error; break; case SET_STR_VALUE: diff --git a/sql/log_event.h b/sql/log_event.h index 937761846cf8c..22249cb1c7cef 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -4531,7 +4531,6 @@ class Table_map_log_event : public Log_event Dynamic_array m_column_charset; // Character set number of every ENUM or SET column. Dynamic_array m_enum_and_set_column_charset; - LEX_CSTRING *m_column_name; // each str_vector stores values of one enum/set column Dynamic_array m_enum_str_value; Dynamic_array m_set_str_value; @@ -4542,6 +4541,13 @@ class Table_map_log_event : public Log_event */ Dynamic_array m_primary_key; + struct Column_metadata + { + LEX_CSTRING column_name{ nullptr, 0 }; + }; + + Dynamic_array m_column_metadata; + /* It parses m_optional_metadata and populates into above variables. diff --git a/sql/log_event_client.cc b/sql/log_event_client.cc index 4164b3df3e0d7..2338c3e5ef499 100644 --- a/sql/log_event_client.cc +++ b/sql/log_event_client.cc @@ -3473,7 +3473,6 @@ void Table_map_log_event::print_columns(IO_CACHE *file, const Optional_metadata_fields::str_vector *enum_str_values_it= fields.m_enum_str_value.front(); const uint *geometry_type_it= fields.m_geometry_type.front(); - LEX_CSTRING *col_names= fields.m_column_name; uint geometry_type= 0; @@ -3496,11 +3495,11 @@ void Table_map_log_event::print_columns(IO_CACHE *file, cs = enum_and_set_charset_it->next(); // Print column name - if (col_names && col_names->str) + const LEX_CSTRING& col_name = fields.m_column_metadata.at(i).column_name; + if (col_name.str) { - pretty_print_identifier(file, col_names->str, col_names->length); + pretty_print_identifier(file, col_name.str, col_name.length); my_b_printf(file, " "); - col_names++; } // update geometry_type for geometry columns @@ -3588,10 +3587,11 @@ void Table_map_log_event::print_primary_key my_b_printf(file, ", "); // Print column name or column index - if (!fields.m_column_name) - my_b_printf(file, "%u", it->first); + const LEX_CSTRING& column_name = fields.m_column_metadata.at(it->first).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", it->first); // Print prefix length if (it->second != 0) diff --git a/sql/rpl_utility_server.cc b/sql/rpl_utility_server.cc index cade88c093024..dc2e25bc15ab9 100644 --- a/sql/rpl_utility_server.cc +++ b/sql/rpl_utility_server.cc @@ -1354,7 +1354,8 @@ void RPL_TABLE_LIST::create_column_mapping(rpl_group_info *rgi) (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 +1367,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)) { From ba219dbe0beb4be886d35d8f3a818a7fd4d4f15d Mon Sep 17 00:00:00 2001 From: Eric Date: Sun, 8 Mar 2026 12:35:52 -0400 Subject: [PATCH 03/16] Add signed flags to Column_metadata --- sql/log_event.cc | 38 +++++++++++++++++++++++++++++--------- sql/log_event.h | 22 +++++++++++++++++++++- sql/log_event_client.cc | 32 +++++++------------------------- sql/rpl_utility.h | 1 + sql/rpl_utility_server.cc | 1 + 5 files changed, 59 insertions(+), 35 deletions(-) diff --git a/sql/log_event.cc b/sql/log_event.cc index 660e36aa83843..5e5e99dcd0948 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -3719,17 +3719,32 @@ 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(Dynamic_array &vec, - unsigned char *field, unsigned int length) +static void parse_signedness( + Dynamic_array& column_metadata, + unsigned char *field, unsigned int length) { - for (unsigned int i= 0; i < length; i++) + unsigned char *field_end= field + 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.append(field[i] & c); + auto &col_meta= column_metadata.at(col); + if (!is_numeric_type(col_meta.column_type)) + continue; + if (mask == 0) + { + if (field >= field_end) + return; + unsigned_bitfield= *field++; + mask= 0x80; + } + col_meta.is_unsigned= unsigned_bitfield & mask; + mask >>= 1; } } @@ -3897,10 +3912,11 @@ static void parse_pk_with_prefix( Table_map_log_event::Optional_metadata_fields:: Optional_metadata_fields(MEM_ROOT *root, uint master_columns, + const uchar* column_types, uchar* optional_metadata, size_t optional_metadata_len, bool only_column_names) - : m_signedness(PSI_INSTRUMENT_MEM), m_column_charset(PSI_INSTRUMENT_MEM), + : m_column_charset(PSI_INSTRUMENT_MEM), m_enum_and_set_column_charset(PSI_INSTRUMENT_MEM), m_enum_str_value(PSI_INSTRUMENT_MEM), m_set_str_value(PSI_INSTRUMENT_MEM), m_geometry_type(PSI_INSTRUMENT_MEM), @@ -3913,8 +3929,12 @@ Optional_metadata_fields(MEM_ROOT *root, uint master_columns, allocation_error= 0; for (uint i= 0; i < master_columns; i++) - if (m_column_metadata.append(Column_metadata())) + { + Column_metadata column_metadata{}; + column_metadata.column_type = column_types[i]; + if (m_column_metadata.append(column_metadata)) goto error; + } if (optional_metadata == NULL) return; @@ -3933,7 +3953,7 @@ Optional_metadata_fields(MEM_ROOT *root, uint master_columns, switch(type) { case SIGNEDNESS: - parse_signedness(m_signedness, field, len); + parse_signedness(m_column_metadata, field, len); break; case DEFAULT_CHARSET: parse_default_charset(m_default_charset, field, len); diff --git a/sql/log_event.h b/sql/log_event.h index 22249cb1c7cef..997f6f7b53680 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -39,6 +39,24 @@ #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: + return true; + default: + return false; + } +} + #ifdef MYSQL_CLIENT #include "sql_const.h" #include "rpl_utility.h" @@ -4526,7 +4544,6 @@ class Table_map_log_event : public Log_event // Contents of ENUM_AND_SET_DEFAULT_CHARSET are converted into // Default_charset. Default_charset m_enum_and_set_default_charset; - Dynamic_array m_signedness; // Character set number of every string column Dynamic_array m_column_charset; // Character set number of every ENUM or SET column. @@ -4544,6 +4561,8 @@ class Table_map_log_event : public Log_event struct Column_metadata { LEX_CSTRING column_name{ nullptr, 0 }; + uchar column_type{}; + bool is_unsigned{ false }; }; Dynamic_array m_column_metadata; @@ -4559,6 +4578,7 @@ 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, uchar* optional_metadata, size_t optional_metadata_len, bool only_column_names); diff --git a/sql/log_event_client.cc b/sql/log_event_client.cc index 2338c3e5ef499..08297012b1183 100644 --- a/sql/log_event_client.cc +++ b/sql/log_event_client.cc @@ -286,24 +286,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) { @@ -3174,7 +3156,9 @@ 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_optional_metadata, m_optional_metadata_len, 0); @@ -3459,7 +3443,6 @@ void Table_map_log_event::print_columns(IO_CACHE *file, const Optional_metadata_fields &fields) { unsigned char* field_metadata_ptr= m_field_metadata; - const bool *signedness_it= fields.m_signedness.front(); std::unique_ptr charset_it = Charset_iterator::create_charset_iterator(fields.m_default_charset, @@ -3480,6 +3463,7 @@ void Table_map_log_event::print_columns(IO_CACHE *file, for (unsigned long i= 0; i < m_colcnt; 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 || @@ -3495,7 +3479,7 @@ void Table_map_log_event::print_columns(IO_CACHE *file, cs = enum_and_set_charset_it->next(); // Print column name - const LEX_CSTRING& col_name = fields.m_column_metadata.at(i).column_name; + const LEX_CSTRING& col_name= column_metadata.column_name; if (col_name.str) { pretty_print_identifier(file, col_name.str, col_name.length); @@ -3523,12 +3507,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' 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 dc2e25bc15ab9..17a5ece3f42a1 100644 --- a/sql/rpl_utility_server.cc +++ b/sql/rpl_utility_server.cc @@ -1351,6 +1351,7 @@ 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(), (uchar*) m_tabledef.optional_metadata.str, m_tabledef.optional_metadata.length, 1); From b86625500c8555ae86161cd8126a22d2ca27b07c Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 11 Mar 2026 14:58:15 -0400 Subject: [PATCH 04/16] Implement string value vector for enums and sets in optional metadata --- sql/log_event.cc | 64 +++++++++++++++++++++++++++------------ sql/log_event.h | 45 +++++++++++++++++++++++++-- sql/log_event_client.cc | 40 ++++++------------------ sql/rpl_utility_server.cc | 1 + 4 files changed, 96 insertions(+), 54 deletions(-) diff --git a/sql/log_event.cc b/sql/log_event.cc index 5e5e99dcd0948..7ef6bc29a9770 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -3817,32 +3817,48 @@ static bool parse_column_name(MEM_ROOT *root, /** 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[out] column_metadata store all column metadata here (including + set/enum strings) @param[in] field COLUMN_NAME 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) */ static void parse_set_str_value( - Dynamic_array - &vec, - unsigned char *field, unsigned int length) + Dynamic_array + &column_metadata, + unsigned char *field, unsigned int length, + uchar column_type) { + using str_vector = Table_map_log_event::Optional_metadata_fields::str_vector; 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); + str_vector* column_strings; + if (column_type == MYSQL_TYPE_SET) + { + column_strings = &column_metadata.at(col).set_str_values; + } + else if (column_type == MYSQL_TYPE_ENUM) + { + column_strings = &column_metadata.at(col).enum_str_values; + } + else + { + DBUG_ASSERT(0); + return; + } - if (vec.reserve(vec.elements() + 1)) + if (column_strings->reserve(count)) return; - vec.elements(vec.elements() + 1); - vec.back()->init(PSI_INSTRUMENT_MEM); + for (unsigned int i= 0; i < count; i++) { unsigned len1= net_field_length(&p); - vec.back()->append(LEX_CSTRING{reinterpret_cast(p), len1}); + column_strings->append(LEX_CSTRING{reinterpret_cast(p), len1}); p+= len1; } } @@ -3913,13 +3929,13 @@ static void parse_pk_with_prefix( Table_map_log_event::Optional_metadata_fields:: Optional_metadata_fields(MEM_ROOT *root, uint master_columns, const uchar* column_types, + const uchar* field_metadata, uchar* optional_metadata, size_t optional_metadata_len, bool only_column_names) : m_column_charset(PSI_INSTRUMENT_MEM), m_enum_and_set_column_charset(PSI_INSTRUMENT_MEM), - m_enum_str_value(PSI_INSTRUMENT_MEM), - m_set_str_value(PSI_INSTRUMENT_MEM), m_geometry_type(PSI_INSTRUMENT_MEM), + m_geometry_type(PSI_INSTRUMENT_MEM), m_primary_key(PSI_INSTRUMENT_MEM), m_column_metadata(PSI_INSTRUMENT_MEM, master_columns) { @@ -3930,9 +3946,17 @@ Optional_metadata_fields(MEM_ROOT *root, uint master_columns, for (uint i= 0; i < master_columns; i++) { - Column_metadata column_metadata{}; - column_metadata.column_type = column_types[i]; - if (m_column_metadata.append(column_metadata)) + Column_metadata col{}; + col.column_type = column_types[i]; + if (field_metadata) + { + if (col.column_type == MYSQL_TYPE_STRING && + (*field_metadata == MYSQL_TYPE_ENUM || + *field_metadata == MYSQL_TYPE_SET)) + col.column_type= *field_metadata; + advance_field_metadata_ptr(col.column_type, &field_metadata); + } + if (m_column_metadata.append(col)) goto error; } @@ -3966,10 +3990,10 @@ Optional_metadata_fields(MEM_ROOT *root, uint master_columns, goto error; break; case SET_STR_VALUE: - parse_set_str_value(m_set_str_value, field, len); + parse_set_str_value(m_column_metadata, field, len, MYSQL_TYPE_SET); break; case ENUM_STR_VALUE: - parse_set_str_value(m_enum_str_value, field, len); + parse_set_str_value(m_column_metadata, field, len, MYSQL_TYPE_ENUM); break; case GEOMETRY_TYPE: parse_geometry_type(m_geometry_type, field, len); diff --git a/sql/log_event.h b/sql/log_event.h index 997f6f7b53680..a8edc524b6fa5 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -30,6 +30,7 @@ #define _log_event_h #include +#include "mysql/psi/psi_base.h" #include "rpl_constants.h" #include "sql_array.h" #include @@ -4437,6 +4438,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: @@ -4548,9 +4585,6 @@ class Table_map_log_event : public Log_event Dynamic_array m_column_charset; // Character set number of every ENUM or SET column. Dynamic_array m_enum_and_set_column_charset; - // each str_vector stores values of one enum/set column - Dynamic_array m_enum_str_value; - Dynamic_array m_set_str_value; Dynamic_array m_geometry_type; /* The uint_pair means . Prefix length is 0 if @@ -4563,6 +4597,10 @@ class Table_map_log_event : public Log_event 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}; }; Dynamic_array m_column_metadata; @@ -4579,6 +4617,7 @@ class Table_map_log_event : public Log_event */ 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); diff --git a/sql/log_event_client.cc b/sql/log_event_client.cc index 08297012b1183..6f82d710f28bd 100644 --- a/sql/log_event_client.cc +++ b/sql/log_event_client.cc @@ -3159,6 +3159,7 @@ bool Table_map_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info) Optional_metadata_fields fields(&root, m_colcnt, m_coltype, + m_field_metadata, m_optional_metadata, m_optional_metadata_len, 0); @@ -3320,43 +3321,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: @@ -3367,11 +3361,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: { @@ -3390,8 +3382,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: @@ -3402,8 +3392,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: { @@ -3415,8 +3403,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: @@ -3430,13 +3416,13 @@ 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, @@ -3451,10 +3437,6 @@ void Table_map_log_event::print_columns(IO_CACHE *file, Charset_iterator::create_charset_iterator( fields.m_enum_and_set_default_charset, fields.m_enum_and_set_column_charset); - const Optional_metadata_fields::str_vector *set_str_values_it= - fields.m_set_str_value.front(); - const Optional_metadata_fields::str_vector *enum_str_values_it= - fields.m_enum_str_value.front(); const uint *geometry_type_it= fields.m_geometry_type.front(); uint geometry_type= 0; @@ -3519,17 +3501,13 @@ 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) diff --git a/sql/rpl_utility_server.cc b/sql/rpl_utility_server.cc index 17a5ece3f42a1..f97f595aea69b 100644 --- a/sql/rpl_utility_server.cc +++ b/sql/rpl_utility_server.cc @@ -1352,6 +1352,7 @@ 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); From 83e91cef441e9e0e1b72b4ceed20c470a69ce2ea Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 11 Mar 2026 21:03:11 -0400 Subject: [PATCH 05/16] Add geometry type to the Column metadata type --- sql/log_event.cc | 23 ++++++++++++++++------- sql/log_event.h | 3 ++- sql/log_event_client.cc | 12 +----------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/sql/log_event.cc b/sql/log_event.cc index 7ef6bc29a9770..96ef11b432d58 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -18,6 +18,7 @@ #include "mariadb.h" #include "mysql/psi/psi_base.h" +#include "mysql_com.h" #include "sql_priv.h" #include "handler.h" #ifndef MYSQL_CLIENT @@ -3867,17 +3868,26 @@ static void parse_set_str_value( /** Parses GEOMETRY_TYPE field. - @param[out] vec stores geometry column's types extracted from field. + @param[out] column_metadata store all column metadata here (including + geometry types) @param[in] field GEOMETRY_TYPE field in table_map_event. @param[in] length length of the field */ -static void parse_geometry_type(Dynamic_array &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.append(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); + } + } } /** @@ -3935,7 +3945,6 @@ Optional_metadata_fields(MEM_ROOT *root, uint master_columns, bool only_column_names) : m_column_charset(PSI_INSTRUMENT_MEM), m_enum_and_set_column_charset(PSI_INSTRUMENT_MEM), - m_geometry_type(PSI_INSTRUMENT_MEM), m_primary_key(PSI_INSTRUMENT_MEM), m_column_metadata(PSI_INSTRUMENT_MEM, master_columns) { @@ -3996,7 +4005,7 @@ Optional_metadata_fields(MEM_ROOT *root, uint master_columns, parse_set_str_value(m_column_metadata, field, len, MYSQL_TYPE_ENUM); break; case GEOMETRY_TYPE: - parse_geometry_type(m_geometry_type, field, len); + parse_geometry_type(m_column_metadata, field, len); break; case SIMPLE_PRIMARY_KEY: parse_simple_pk(m_primary_key, field, len); diff --git a/sql/log_event.h b/sql/log_event.h index a8edc524b6fa5..6c4ff3dfa5080 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -4585,7 +4585,6 @@ class Table_map_log_event : public Log_event Dynamic_array m_column_charset; // Character set number of every ENUM or SET column. Dynamic_array m_enum_and_set_column_charset; - Dynamic_array m_geometry_type; /* The uint_pair means . Prefix length is 0 if whole column value is used. @@ -4601,6 +4600,8 @@ class Table_map_log_event : public Log_event // 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}; + + uint geometry_type{ 0 }; }; Dynamic_array m_column_metadata; diff --git a/sql/log_event_client.cc b/sql/log_event_client.cc index 6f82d710f28bd..ea45a7aa61ebc 100644 --- a/sql/log_event_client.cc +++ b/sql/log_event_client.cc @@ -3437,9 +3437,6 @@ void Table_map_log_event::print_columns(IO_CACHE *file, Charset_iterator::create_charset_iterator( fields.m_enum_and_set_default_charset, fields.m_enum_and_set_column_charset); - const uint *geometry_type_it= fields.m_geometry_type.front(); - - uint geometry_type= 0; my_b_printf(file, "# Columns("); @@ -3468,18 +3465,11 @@ void Table_map_log_event::print_columns(IO_CACHE *file, my_b_printf(file, " "); } - // update geometry_type for geometry columns - if (real_type == MYSQL_TYPE_GEOMETRY) - { - geometry_type= (geometry_type_it != fields.m_geometry_type.end()) ? - *geometry_type_it++ : 0; - } - // 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') { From 410d25059f1d0d0df24de85c3351e977a380c006 Mon Sep 17 00:00:00 2001 From: Eric Date: Fri, 13 Mar 2026 19:19:19 -0400 Subject: [PATCH 06/16] Update Column_metadata to include primary key information --- sql/log_event.cc | 25 +++++++++++++------------ sql/log_event.h | 11 ++++++----- sql/log_event_client.cc | 29 ++++++++++++++++++++--------- 3 files changed, 39 insertions(+), 26 deletions(-) diff --git a/sql/log_event.cc b/sql/log_event.cc index 96ef11b432d58..402b27d8f8372 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -3897,33 +3897,35 @@ static void parse_geometry_type( 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 prefix lengths). Note: a prefix is always 0 for + SIMPLE_PRIMARY_KEY field. @param[in] field SIMPLE_PRIMARY_KEY field in table_map_event. @param[in] length length of the field */ static void parse_simple_pk( - Dynamic_array - &vec, + Dynamic_array + &column_metadata, unsigned char *field, unsigned int length) { unsigned char* p= field; while (p < field + length) - vec.append(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( - Dynamic_array - &vec, + Dynamic_array + &column_metadata, unsigned char *field, unsigned int length) { unsigned char* p= field; @@ -3932,7 +3934,7 @@ static void parse_pk_with_prefix( { unsigned int col_index= net_field_length(&p); unsigned int col_prefix= net_field_length(&p); - vec.append(std::make_pair(col_index, col_prefix)); + column_metadata.at(col_index).primary_key = col_prefix; } } @@ -3945,7 +3947,6 @@ Optional_metadata_fields(MEM_ROOT *root, uint master_columns, bool only_column_names) : m_column_charset(PSI_INSTRUMENT_MEM), m_enum_and_set_column_charset(PSI_INSTRUMENT_MEM), - m_primary_key(PSI_INSTRUMENT_MEM), m_column_metadata(PSI_INSTRUMENT_MEM, master_columns) { unsigned int len; @@ -4008,10 +4009,10 @@ Optional_metadata_fields(MEM_ROOT *root, uint master_columns, parse_geometry_type(m_column_metadata, field, len); break; case SIMPLE_PRIMARY_KEY: - parse_simple_pk(m_primary_key, field, len); + parse_simple_pk(m_column_metadata, field, len); break; case PRIMARY_KEY_WITH_PREFIX: - parse_pk_with_prefix(m_primary_key, field, len); + parse_pk_with_prefix(m_column_metadata, field, len); break; case ENUM_AND_SET_DEFAULT_CHARSET: parse_default_charset(m_enum_and_set_default_charset, field, len); diff --git a/sql/log_event.h b/sql/log_event.h index 6c4ff3dfa5080..02676c1411ccf 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -38,6 +38,7 @@ #include #include #include +#include #include static inline bool is_numeric_type(uint type) @@ -4585,11 +4586,6 @@ class Table_map_log_event : public Log_event Dynamic_array m_column_charset; // Character set number of every ENUM or SET column. Dynamic_array m_enum_and_set_column_charset; - /* - The uint_pair means . Prefix length is 0 if - whole column value is used. - */ - Dynamic_array m_primary_key; struct Column_metadata { @@ -4602,6 +4598,11 @@ class Table_map_log_event : public Log_event str_vector set_str_values{PSI_INSTRUMENT_MEM, 0}; uint geometry_type{ 0 }; + + // 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{}; }; Dynamic_array m_column_metadata; diff --git a/sql/log_event_client.cc b/sql/log_event_client.cc index ea45a7aa61ebc..17998c329b925 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 @@ -3442,7 +3443,7 @@ void Table_map_log_event::print_columns(IO_CACHE *file, for (unsigned long i= 0; i < m_colcnt; i++) { - const Optional_metadata_fields::Column_metadata column_metadata = fields.m_column_metadata.at(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 || @@ -3526,26 +3527,36 @@ 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= std::any_of(col_metadata.front(), col_metadata.end(), [](const auto& col){ + return col.primary_key.has_value(); + }); + + if (has_pk) { my_b_printf(file, "# Primary Key("); - for (auto *it= fields.m_primary_key.front(); - it != fields.m_primary_key.end(); it++) + bool printed_first_key = false; + for (uint col_idx = 0; col_idx < col_metadata.size(); col_idx++) { - if (it != fields.m_primary_key.front()) + const auto& col= col_metadata.at(col_idx); + if (!col.primary_key.has_value()) + continue; + + if (printed_first_key) my_b_printf(file, ", "); // Print column name or column index - const LEX_CSTRING& column_name = fields.m_column_metadata.at(it->first).column_name; + 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, "%u", it->first); + 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()); + printed_first_key= true; } my_b_printf(file, ")\n"); From d02f83eb0e279185f317cdce84dc35b8d68605fa Mon Sep 17 00:00:00 2001 From: Eric Date: Fri, 13 Mar 2026 20:18:35 -0400 Subject: [PATCH 07/16] Update logic for storing charset information in Optional_metadata_fields --- sql/log_event.cc | 95 +++++++++++++++++++----------- sql/log_event.h | 40 +++---------- sql/log_event_client.cc | 125 +++++++--------------------------------- 3 files changed, 89 insertions(+), 171 deletions(-) diff --git a/sql/log_event.cc b/sql/log_event.cc index 402b27d8f8372..67886c97c6bee 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -3752,41 +3752,73 @@ static void parse_signedness( /** 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); + fields.m_default_charset= net_field_length(&p); 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); + } +} - default_charset.charset_pairs.append(std::make_pair(col_index, - col_charset)); +/** + Parses ENUM_AND_SET_DEFAULT_CHARSET field. + + @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; + fields.m_enum_and_set_default_charset= net_field_length(&p); + 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(Dynamic_array &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++) + column_metadata.at(col).charset= net_field_length(&p); +} - while (p < field + length) - vec.append(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++) + column_metadata.at(col).enum_and_set_column_charset= net_field_length(&p); } /** @@ -3797,7 +3829,7 @@ static void parse_column_charset(Dynamic_array &vec, @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, +static bool parse_column_name(MEM_ROOT *root, Dynamic_array& column_metadata, unsigned char *field, unsigned int length) { @@ -3818,14 +3850,14 @@ static bool parse_column_name(MEM_ROOT *root, /** Parses SET_STR_VALUE/ENUM_STR_VALUE field. - @param[out] column_metadata store all column metadata here (including + @param[out] column_metadata store all column metadata here (including set/enum strings) - @param[in] field COLUMN_NAME field in table_map_event. + @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) */ static void parse_set_str_value( - Dynamic_array + Dynamic_array &column_metadata, unsigned char *field, unsigned int length, uchar column_type) @@ -3833,12 +3865,13 @@ static void parse_set_str_value( using str_vector = Table_map_log_event::Optional_metadata_fields::str_vector; unsigned char* p= field; - for (uint col = 0; p < field + length && col < column_metadata.size(); col++) { + 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); - str_vector* column_strings; + str_vector* column_strings; if (column_type == MYSQL_TYPE_SET) { column_strings = &column_metadata.at(col).set_str_values; @@ -3874,7 +3907,7 @@ static void parse_set_str_value( @param[in] length length of the field */ static void parse_geometry_type( - Dynamic_array + Dynamic_array &column_metadata, unsigned char *field, unsigned int length) { @@ -3893,13 +3926,8 @@ static void parse_geometry_type( /** 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 prefix lengths). Note: a prefix is always 0 for - SIMPLE_PRIMARY_KEY field. + 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 */ @@ -3922,7 +3950,6 @@ static void parse_simple_pk( @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( Dynamic_array &column_metadata, @@ -3945,9 +3972,7 @@ Optional_metadata_fields(MEM_ROOT *root, uint master_columns, uchar* optional_metadata, size_t optional_metadata_len, bool only_column_names) - : m_column_charset(PSI_INSTRUMENT_MEM), - m_enum_and_set_column_charset(PSI_INSTRUMENT_MEM), - m_column_metadata(PSI_INSTRUMENT_MEM, master_columns) + : m_column_metadata(PSI_INSTRUMENT_MEM, master_columns) { unsigned int len; uchar *metadata_end; @@ -3990,10 +4015,10 @@ Optional_metadata_fields(MEM_ROOT *root, uint master_columns, parse_signedness(m_column_metadata, field, len); break; case DEFAULT_CHARSET: - parse_default_charset(m_default_charset, field, len); + parse_default_charset(*this, field, len); break; case COLUMN_CHARSET: - parse_column_charset(m_column_charset, field, len); + parse_column_charset(m_column_metadata, field, len); break; case COLUMN_NAME: if (parse_column_name(root, m_column_metadata, field, len)) @@ -4015,10 +4040,10 @@ Optional_metadata_fields(MEM_ROOT *root, uint master_columns, parse_pk_with_prefix(m_column_metadata, field, len); break; case ENUM_AND_SET_DEFAULT_CHARSET: - parse_default_charset(m_enum_and_set_default_charset, field, len); + parse_enum_and_set_default_charset(*this, field, len); break; case ENUM_AND_SET_COLUMN_CHARSET: - parse_column_charset(m_enum_and_set_column_charset, field, len); + parse_enum_and_set_column_charset(m_column_metadata, field, len); break; default: DBUG_ASSERT(0); diff --git a/sql/log_event.h b/sql/log_event.h index 02676c1411ccf..228f8078f0afb 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -4468,7 +4468,7 @@ inline void advance_field_metadata_ptr(uint type, const uchar** meta_ptr) case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_STRING: - (*meta_ptr)+= 2; + (*meta_ptr) += 2; break; default: break; @@ -4558,34 +4558,11 @@ class Table_map_log_event : public Log_event }; struct Optional_metadata_fields { - typedef std::pair uint_pair; typedef Dynamic_array str_vector; bool allocation_error; /* Set if allocation of data structures fails */ - struct Default_charset - { - Default_charset() : default_charset(0), charset_pairs(PSI_INSTRUMENT_MEM) - { - } - bool empty() const { return default_charset == 0; } - - // Default charset for the columns which are not in charset_pairs. - unsigned int default_charset; - - /* The uint_pair means . */ - // std::vector charset_pairs; - Dynamic_array charset_pairs; - }; - - // 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; - // Character set number of every string column - Dynamic_array m_column_charset; - // Character set number of every ENUM or SET column. - Dynamic_array m_enum_and_set_column_charset; + std::optional m_default_charset{}; + std::optional m_enum_and_set_default_charset{}; struct Column_metadata { @@ -4598,11 +4575,14 @@ class Table_map_log_event : public Log_event str_vector set_str_values{PSI_INSTRUMENT_MEM, 0}; uint geometry_type{ 0 }; - + // 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{}; + + std::optional charset{}; + std::optional enum_and_set_column_charset{}; }; Dynamic_array m_column_metadata; @@ -4762,11 +4742,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 17998c329b925..5d3c83c278165 100644 --- a/sql/log_event_client.cc +++ b/sql/log_event_client.cc @@ -3157,7 +3157,7 @@ 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, + Optional_metadata_fields fields(&root, m_colcnt, m_coltype, m_field_metadata, @@ -3201,93 +3201,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 Dynamic_array &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.front()), - 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: - const Optional_metadata_fields::uint_pair *m_iterator; - const Optional_metadata_fields::uint_pair *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 Dynamic_array &column_charset) - : m_iterator(column_charset.front()), 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: - const uint *m_iterator; - const uint *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 Dynamic_array &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. @@ -3431,20 +3345,12 @@ void Table_map_log_event::print_columns(IO_CACHE *file, { unsigned char* field_metadata_ptr= m_field_metadata; - 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); - my_b_printf(file, "# Columns("); for (unsigned long i= 0; i < m_colcnt; i++) { - const Optional_metadata_fields::Column_metadata& column_metadata = fields.m_column_metadata.at(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)) @@ -3452,11 +3358,22 @@ 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(); + { + const auto& cs_num= column_metadata.charset.has_value() ? + column_metadata.charset : fields.m_default_charset; + if (cs_num.has_value()) + cs= get_charset(*cs_num, 0); + } else if (is_enum_or_set_type(real_type)) - cs = enum_and_set_charset_it->next(); + { + const auto& cs_num= column_metadata.enum_and_set_column_charset.has_value() ? + column_metadata.enum_and_set_column_charset : + fields.m_enum_and_set_default_charset; + if (cs_num.has_value()) + cs= get_charset(*cs_num, 0); + } // Print column name const LEX_CSTRING& col_name= column_metadata.column_name; @@ -3536,18 +3453,18 @@ void Table_map_log_event::print_primary_key { my_b_printf(file, "# Primary Key("); - bool printed_first_key = false; - for (uint col_idx = 0; col_idx < col_metadata.size(); col_idx++) + bool printed_first_key= false; + for (uint col_idx= 0; col_idx < col_metadata.size(); col_idx++) { const auto& col= col_metadata.at(col_idx); if (!col.primary_key.has_value()) - continue; + continue; if (printed_first_key) my_b_printf(file, ", "); // Print column name or column index - const LEX_CSTRING& column_name = col.column_name; + const LEX_CSTRING& column_name= col.column_name; if (column_name.str) my_b_printf(file, "%s", column_name.str); else From cd93c9c6acb148bfcf521cdaeca3acd87caa3740 Mon Sep 17 00:00:00 2001 From: Eric Date: Fri, 13 Mar 2026 20:51:48 -0400 Subject: [PATCH 08/16] Add type check during charset parsing --- sql/log_event.cc | 10 ++++++++-- sql/log_event.h | 21 +++++++++++++++++++++ sql/log_event_client.cc | 21 --------------------- 3 files changed, 29 insertions(+), 23 deletions(-) diff --git a/sql/log_event.cc b/sql/log_event.cc index 67886c97c6bee..7ba3741e19765 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -17,8 +17,6 @@ #include "mariadb.h" -#include "mysql/psi/psi_base.h" -#include "mysql_com.h" #include "sql_priv.h" #include "handler.h" #ifndef MYSQL_CLIENT @@ -3802,7 +3800,11 @@ static void parse_column_charset( { 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); + } } /** @@ -3818,7 +3820,11 @@ static void parse_enum_and_set_column_charset( { 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); + } } /** diff --git a/sql/log_event.h b/sql/log_event.h index 228f8078f0afb..958a34c4ec060 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -59,6 +59,27 @@ static inline bool is_numeric_type(uint type) } } +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" diff --git a/sql/log_event_client.cc b/sql/log_event_client.cc index 5d3c83c278165..427dfec2265cd 100644 --- a/sql/log_event_client.cc +++ b/sql/log_event_client.cc @@ -288,27 +288,6 @@ static bool hexdump_data_to_io_cache(IO_CACHE *file, } -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() */ From 01fc0480182b002c0eaed46e0b5a57ff972c16b0 Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 11 May 2026 14:14:12 -0400 Subject: [PATCH 09/16] Use 'using' for complex typename --- sql/log_event.cc | 99 +++++++++++++++++++++++------------------------- 1 file changed, 48 insertions(+), 51 deletions(-) diff --git a/sql/log_event.cc b/sql/log_event.cc index 7ba3741e19765..57cc87fe31161 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. */ /* @@ -3722,8 +3722,9 @@ Table_map_log_event::~Table_map_log_event() @param[in] field SIGNEDNESS field in table_map_event. @param[in] length length of the field */ +using Optional_column_metadata= Table_map_log_event::Optional_metadata_fields::Column_metadata; static void parse_signedness( - Dynamic_array& column_metadata, + Dynamic_array& column_metadata, unsigned char *field, unsigned int length) { unsigned char *field_end= field + length; @@ -3795,7 +3796,7 @@ static void parse_enum_and_set_default_charset( @param[in] length length of the field */ static void parse_column_charset( - Dynamic_array& column_metadata, + Dynamic_array& column_metadata, unsigned char *field, unsigned int length) { unsigned char* p= field; @@ -3815,7 +3816,7 @@ static void parse_column_charset( @param[in] length length of the field */ static void parse_enum_and_set_column_charset( - Dynamic_array& column_metadata, + Dynamic_array& column_metadata, unsigned char *field, unsigned int length) { unsigned char* p= field; @@ -3836,7 +3837,7 @@ static void parse_enum_and_set_column_charset( @param[in] length length of the field */ static bool parse_column_name(MEM_ROOT *root, - Dynamic_array& column_metadata, + Dynamic_array& column_metadata, unsigned char *field, unsigned int length) { unsigned int col = 0; @@ -3863,8 +3864,7 @@ static bool parse_column_name(MEM_ROOT *root, @param[in] column_type type of the given column (i.e. MYSQL_TYPE_XYZ) */ static void parse_set_str_value( - Dynamic_array - &column_metadata, + Dynamic_array &column_metadata, unsigned char *field, unsigned int length, uchar column_type) { @@ -3913,8 +3913,7 @@ static void parse_set_str_value( @param[in] length length of the field */ static void parse_geometry_type( - Dynamic_array - &column_metadata, + Dynamic_array &column_metadata, unsigned char *field, unsigned int length) { unsigned char* p= field; @@ -3938,8 +3937,7 @@ static void parse_geometry_type( @param[in] length length of the field */ static void parse_simple_pk( - Dynamic_array - &column_metadata, + Dynamic_array &column_metadata, unsigned char *field, unsigned int length) { unsigned char* p= field; @@ -3957,8 +3955,7 @@ static void parse_simple_pk( @param[in] length length of the field */ static void parse_pk_with_prefix( - Dynamic_array - &column_metadata, + Dynamic_array &column_metadata, unsigned char *field, unsigned int length) { unsigned char* p= field; From ce02670274ffcf385042f866a45c8f95a754bf7a Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 11 May 2026 14:17:49 -0400 Subject: [PATCH 10/16] use pointer to iterate over bitfields --- sql/log_event.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sql/log_event.cc b/sql/log_event.cc index 57cc87fe31161..b794f8d793dfc 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -3727,7 +3727,8 @@ static void parse_signedness( Dynamic_array& column_metadata, unsigned char *field, unsigned int length) { - unsigned char *field_end= field + length; + unsigned char *p= field; + unsigned char *field_end= p + length; unsigned char unsigned_bitfield= 0; unsigned char mask= 0; @@ -3738,9 +3739,9 @@ static void parse_signedness( continue; if (mask == 0) { - if (field >= field_end) + if (p >= field_end) return; - unsigned_bitfield= *field++; + unsigned_bitfield= *p++; mask= 0x80; } col_meta.is_unsigned= unsigned_bitfield & mask; From f8ba9b4db3886edef083e6d27d9030632e4947ab Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 11 May 2026 14:56:50 -0400 Subject: [PATCH 11/16] Use pointer-to-member in enum and set parsing function --- sql/log_event.cc | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/sql/log_event.cc b/sql/log_event.cc index b794f8d793dfc..65810a2fefd79 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -3860,16 +3860,18 @@ static bool parse_column_name(MEM_ROOT *root, @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] 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( Dynamic_array &column_metadata, unsigned char *field, unsigned int length, - uchar column_type) + uchar column_type, + Table_map_log_event::Optional_metadata_fields::str_vector + Optional_column_metadata::*var_to_set) { - using str_vector = Table_map_log_event::Optional_metadata_fields::str_vector; unsigned char* p= field; for (uint col= 0; p < field + length && col < column_metadata.size(); col++) @@ -3878,20 +3880,7 @@ static void parse_set_str_value( continue; unsigned int count= net_field_length(&p); - str_vector* column_strings; - if (column_type == MYSQL_TYPE_SET) - { - column_strings = &column_metadata.at(col).set_str_values; - } - else if (column_type == MYSQL_TYPE_ENUM) - { - column_strings = &column_metadata.at(col).enum_str_values; - } - else - { - DBUG_ASSERT(0); - return; - } + auto* column_strings= &(column_metadata.at(col).*var_to_set); if (column_strings->reserve(count)) return; @@ -4029,10 +4018,12 @@ Optional_metadata_fields(MEM_ROOT *root, uint master_columns, goto error; break; case SET_STR_VALUE: - parse_set_str_value(m_column_metadata, field, len, MYSQL_TYPE_SET); + parse_set_str_value(m_column_metadata, field, len, MYSQL_TYPE_SET, + &Optional_column_metadata::set_str_values); break; case ENUM_STR_VALUE: - parse_set_str_value(m_column_metadata, field, len, MYSQL_TYPE_ENUM); + parse_set_str_value(m_column_metadata, field, len, MYSQL_TYPE_ENUM, + &Optional_column_metadata::enum_str_values); break; case GEOMETRY_TYPE: parse_geometry_type(m_column_metadata, field, len); From 044d35678bfefb5e07d42a3acd4b7e9817dc5e5b Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 11 May 2026 14:59:24 -0400 Subject: [PATCH 12/16] Add comment to initalization loop of Optioanal_metadata_fields constructor --- sql/log_event.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/sql/log_event.cc b/sql/log_event.cc index 65810a2fefd79..996d2c4d16319 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -3972,6 +3972,7 @@ Optional_metadata_fields(MEM_ROOT *root, uint master_columns, allocation_error= 0; + // initializing the elements of m_column_metadata and their types for (uint i= 0; i < master_columns; i++) { Column_metadata col{}; From 8723298ae8ad700bc2c5c8d905a69eadd988006d Mon Sep 17 00:00:00 2001 From: Eric Date: Tue, 12 May 2026 07:21:02 -0400 Subject: [PATCH 13/16] Remove fancy C++ and simplify PK printing --- sql/log_event_client.cc | 62 ++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/sql/log_event_client.cc b/sql/log_event_client.cc index 427dfec2265cd..cd833834e0fa7 100644 --- a/sql/log_event_client.cc +++ b/sql/log_event_client.cc @@ -395,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 @@ -418,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 @@ -445,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 @@ -463,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 */ @@ -488,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 */ @@ -537,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); @@ -565,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; } @@ -613,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 */ @@ -742,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); @@ -755,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); @@ -766,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: @@ -787,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) @@ -799,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) { @@ -894,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. */ @@ -937,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) @@ -1895,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; } @@ -1935,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 */ @@ -3024,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 */ @@ -3424,23 +3424,27 @@ void Table_map_log_event::print_primary_key (IO_CACHE *file,const Optional_metadata_fields &fields) { auto& col_metadata= fields.m_column_metadata; - bool has_pk= std::any_of(col_metadata.front(), col_metadata.end(), [](const auto& col){ - return col.primary_key.has_value(); - }); + + 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("); - bool printed_first_key= false; + const char *delimiter = ""; for (uint col_idx= 0; col_idx < col_metadata.size(); col_idx++) { const auto& col= col_metadata.at(col_idx); if (!col.primary_key.has_value()) continue; - if (printed_first_key) - my_b_printf(file, ", "); + my_b_printf(file, delimiter); // Print column name or column index const LEX_CSTRING& column_name= col.column_name; @@ -3452,7 +3456,7 @@ void Table_map_log_event::print_primary_key // Print prefix length if (col.primary_key.value() != 0) my_b_printf(file, "(%u)", col.primary_key.value()); - printed_first_key= true; + delimiter= ", "; } my_b_printf(file, ")\n"); From 2aa16f23ad2d74f1b3dcf274ec9e9c5be1699391 Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 13 May 2026 07:46:10 -0400 Subject: [PATCH 14/16] Simplify default charset handling Wasn't using the fact that the default charset and charset fields were mutually exclusive. Also removed the need to use optionals anywhere in the optional metadata stuff. Just filling the char columns with the charset first and then overrriding later... --- sql/log_event.cc | 10 ++++++++-- sql/log_event.h | 8 +++----- sql/log_event_client.cc | 13 ++++--------- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/sql/log_event.cc b/sql/log_event.cc index 996d2c4d16319..e7e040c980e67 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -3761,7 +3761,10 @@ static void parse_default_charset( unsigned char *field, unsigned int length) { unsigned char* p= field; - fields.m_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); @@ -3781,7 +3784,10 @@ static void parse_enum_and_set_default_charset( unsigned char *field, unsigned int length) { unsigned char* p= field; - fields.m_enum_and_set_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_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); diff --git a/sql/log_event.h b/sql/log_event.h index 958a34c4ec060..a3447278cc26b 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -4582,9 +4582,6 @@ class Table_map_log_event : public Log_event typedef Dynamic_array str_vector; bool allocation_error; /* Set if allocation of data structures fails */ - std::optional m_default_charset{}; - std::optional m_enum_and_set_default_charset{}; - struct Column_metadata { LEX_CSTRING column_name{ nullptr, 0 }; @@ -4602,8 +4599,9 @@ class Table_map_log_event : public Log_event // (SIMPLE_PRIMARY_KEY). std::optional primary_key{}; - std::optional charset{}; - std::optional enum_and_set_column_charset{}; + // 0 means no charset information present + uint charset{ 0 }; + uint enum_and_set_column_charset{ 0 }; }; Dynamic_array m_column_metadata; diff --git a/sql/log_event_client.cc b/sql/log_event_client.cc index cd833834e0fa7..fb68d72e61928 100644 --- a/sql/log_event_client.cc +++ b/sql/log_event_client.cc @@ -3340,18 +3340,13 @@ void Table_map_log_event::print_columns(IO_CACHE *file, const CHARSET_INFO *cs= NULL; if (is_character_type(real_type)) { - const auto& cs_num= column_metadata.charset.has_value() ? - column_metadata.charset : fields.m_default_charset; - if (cs_num.has_value()) - cs= get_charset(*cs_num, 0); + if (column_metadata.charset) + cs= get_charset(column_metadata.charset, 0); } else if (is_enum_or_set_type(real_type)) { - const auto& cs_num= column_metadata.enum_and_set_column_charset.has_value() ? - column_metadata.enum_and_set_column_charset : - fields.m_enum_and_set_default_charset; - if (cs_num.has_value()) - cs= get_charset(*cs_num, 0); + if (column_metadata.enum_and_set_column_charset) + cs= get_charset(column_metadata.enum_and_set_column_charset, 0); } // Print column name From aaee8a109c70a8fd25e93219992789ff9a3b5777 Mon Sep 17 00:00:00 2001 From: Eric Date: Fri, 15 May 2026 07:37:47 -0400 Subject: [PATCH 15/16] Implement test cases for changes to parsing logic --- .../binlog/r/binlog_signedness_parse.result | 65 +++++++++++++++++ .../binlog_table_map_optional_metadata.result | 2 +- .../binlog/t/binlog_signedness_parse.test | 70 +++++++++++++++++++ sql/log_event.h | 1 + 4 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 mysql-test/suite/binlog/r/binlog_signedness_parse.result create mode 100644 mysql-test/suite/binlog/t/binlog_signedness_parse.test 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.h b/sql/log_event.h index a3447278cc26b..fea162672e1e7 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -53,6 +53,7 @@ static inline bool is_numeric_type(uint type) case MYSQL_TYPE_NEWDECIMAL: case MYSQL_TYPE_FLOAT: case MYSQL_TYPE_DOUBLE: + case MYSQL_TYPE_YEAR: return true; default: return false; From 01087a64d0b83cba44da181e23ae25f5007424e8 Mon Sep 17 00:00:00 2001 From: Eric Date: Fri, 15 May 2026 07:50:40 -0400 Subject: [PATCH 16/16] Fix whitespace changes --- sql/sql_array.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/sql_array.h b/sql/sql_array.h index 8e72244a98f8c..c2d5c36cfa5d6 100644 --- a/sql/sql_array.h +++ b/sql/sql_array.h @@ -42,7 +42,7 @@ template class Bounds_checked_array {} void reset() { m_array= NULL; m_size= 0; } - + void reset(Element_type *array_arg, size_t size_arg) { m_array= array_arg; @@ -284,7 +284,7 @@ template class Dynamic_array size_t old_size= elements(); if (reserve(new_size)) return true; - + if (new_size > old_size) { set_dynamic(&array, (uchar*)&default_val, new_size - 1);