1
0
mirror of https://github.com/MariaDB/server.git synced 2026-10-23 18:49:29 +03:00
Files
mariadb/sql/rpl_info_file.cc
Monty c7032278d4 Remove C++ STL machinery from sql/rpl_*info_file.* and CHANGE MASTER parser
Replaces std::function / std::optional / std::unordered_map /
std::unordered_set / std::string_view and template-heavy code in the
master.info / relay-log.info persistence layer with plain MariaDB
idioms.
- On-disk file format unchanged.
- SQL syntrax unchanged
- No notable test changes, except those for which new error messages was
  added and output from SHOW VARIABLES.
- master_* CLI options moved from inline globals in
  rpl_master_info_file.h to Sys_var_* in sys_vars.cc (READ_ONLY,
  AUTO_SET on use_gtid and heartbeat_period). master_heartbeat_period
  is Sys_var_double (seconds); master_use_gtid is Sys_var_enum. Global
  storage sits outside HAVE_REPLICATION so libmariadbd links.
   - This adds the rpl_master_info variables to SHOW VARIABLES
- Value classes are no longer templates. Persistent renamed to Value;
  Optional_* classes renamed (Uint_value_with_default, Path_value,
  etc.).  has_value flag in the base; is_default()
  non-virtual. Multi-line method bodies moved to new rpl_info_file.cc
  and rpl_master_info_file.cc.
- VALUE_LIST (Mem_fn array) and VALUE_MAP (std::unordered_map)
  replaced by per-instance Value *const value_list[] / value_map[]
  arrays + a static TYPELIB populated at first construction from each
  Value's variable_name. seen-set dedup is now a uint32 bitmask.
- Default handling is not anymore done with using special values.
  Setting a value to default is now done by calling set_default(),
  which sets the has_value member to 0 and stores the default value
  in the variable.
  When reading a value, one does not have to check if the value is
  a default value.
  This makes the interface cleaner and easier to manage.
- CHANGE MASTER TO parser no longer builds std::function lambdas:
  LEX_MASTER_INFO carries mi_used_options / mi_used_default bitmasks
  plus plain value fields; change_master() applies them via bit-test +
  direct copy or set_default(). std::optional<T> and trilean are gone
  from the value class API.
- Heartbeat_period_value::load_from uses my_strtod() instead of
  my_decimal for the on-disk format. from_decimal kept for CHANGE
  MASTER.
- RocksDB #if __cplusplus < 201703L guard at the top of
  rpl_master_info_file.h dropped; header now compiles in C++11.
  cstdint dropped; uint32_t / UINT32_MAX in touched code replaced by
  MariaDB's uint32 / UINT_MAX32. include/my_global.h gains
  INT_BUFFER_SIZE next to LONGLONG_BUFFER_SIZE.
- Pointer-style throughout (T * not T &). Multi-line inline functions
  used in more than one place moved to .cc files per style.

New features:
- SHOW VARIABLES now shows the master default values.
- If there is an wrong value for a row in a master.info file the
  corresponding variable name and the value is now written to the
  error log.

Bug fixes uncovered by the refactor:
- master_ssl_verify_server_cert had a pre-existing inconsistency with
  security implications: the documented default and the Sys_var_mybool
  default are TRUE (validate the master's certificate), but the OLD
  init_ssl_config() set the per-Master_info field to FALSE on RESET
  SLAVE ALL. Concretely: after RESET SLAVE ALL followed by a bare
  CHANGE MASTER TO (no explicit MASTER_SSL_VERIFY_SERVER_CERT), the
  slave would connect with MASTER_SSL=1 enabled but with certificate
  validation silently disabled.
  Fixed by not resetting master_ssl_verify_server_cert in
  RESET SLAVE ALL;
- As some replication setups sets master_ssl_verify_server_cert= 0
  while other does not do that, the expected value used in
  check-testcase.inc "Master_SSL_Verify_Server_Cert No" does not make
  sense.  The value of this variable is now ignored by
  check-testcase.inc
- The old code would fail to read a master info file if there was a new
  variable longer than 23 characters. This is now extended to 80.
- Removed warning on RESET SLAVE that "RESET SLAVE is implicitly changing"
  the value of 'Using_Gtid' from 'No' to 'Slave_Pos'" as this change did
  not really happen. This error was given if the master could support
  GTID but the user had specified directly or indirectly to not use GTID.

Other things
- Size of mariadb (optimize build) shrunk with 1,408,009 bytes
  (Based on 'dec' from the size command)
- Size of code in sql directory did not change notable.
- Reworked Master_use_gtid_value to remember users last choice and use
  this as default when connecting to slave.
  Replace mi->using_gtid, with was a pointer to master_use_gtid, with
  two functions:
  - using_gtid() returns the current mode, for the code that needs the
    value itself (SHOW SLAVE STATUS and performance schema output,
    comparisons against CURRENT_POS and SLAVE_POS).
  - is_gtid_supported() tells whether the connection uses gtid at all.
    The many callers that compared the mode against USE_GTID_NO with
    == or != now call this function instead of testing the enum value.
 - Master_use_gtid_value::supported() did the same test as
   is_gtid_supported() and is removed
- Removed some operators in rpl_master_info_file.* that hid
  that functions was used to read and update values.

Behavior changes:
- The last used value in CHANGE MASTER for MASTER_USE_GTID will be
  remembered for future CHANGE MASTER for the same connection. This is
  true even if RESET SLAVE is called.
- If Using git is not used with change master but log_file_name, pos,
  relay_log_name or relay_log_pos is specified this is treated as
  if Using_git=NO would have been given. (Old behavior, but not
  documented).
--auto-master-use-gtid does nothing. The default value of
  master_use_gtid is by default SlavePos that will automatically
  change to NO if the master does not support GTID. This already
  handles what auto-master-use-gtid tries to do.
- CHANGE MASTER's MASTER_HEARTBEAT_PERIOD now works like other MASTER
  options:  When set to a value, this value is kept until value is
  changed or set to DEFAULT. The DEFAULT is
  @global.master_heartbeat_period if this is set as startup, if not
  then @global.slave_net_timeout/2 is used.
2026-10-16 16:56:44 +03:00

233 lines
6.4 KiB
C++

/*
Copyright (c) 2025 MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA.
*/
#include "mariadb.h"
#include "rpl_info_file.h"
#include <mysqld_error.h> // Error numbers
#include <m_string.h> // strmake, int10_to_str, ll2str
bool uint_from_chars(IO_CACHE *file, ulonglong max_value, ulonglong *result)
{
int error;
longlong val;
/* The +1 is for the end \n that my_b_gets() includes */
char buf[LONGLONG_BUFFER_SIZE+1];
char *end;
size_t length;
if (!(length= my_b_gets(file, buf, sizeof(buf))))
return true;
end= buf + length;
val= my_strtoll10(buf, &end, &error);
if (error != 0 || *end != '\n' || (ulonglong) val > max_value)
return true;
*result= (ulonglong) val;
return false;
}
void uint_to_chars(IO_CACHE *file, ulonglong value)
{
char buf[LONGLONG_BUFFER_SIZE];
char *end= longlong10_to_str((longlong) value, buf, 10);
my_b_write(file, (const uchar *) buf, end - buf);
}
bool Info_file::Uint_value::load_from(IO_CACHE *file)
{
ulonglong tmp;
if (uint_from_chars(file, UINT_MAX32, &tmp))
{
set_default();
return true;
}
value= (uint) tmp;
has_value= true;
return false;
}
void Info_file::Uint_value::save_to(IO_CACHE *file)
{
uint_to_chars(file, value);
}
bool Info_file::Ulonglong_value::load_from(IO_CACHE *file)
{
if (uint_from_chars(file, ULONGLONG_MAX, &value))
{
set_default();
return true;
}
has_value= true;
return false;
}
void Info_file::Ulonglong_value::save_to(IO_CACHE *file)
{
uint_to_chars(file, value);
}
bool Info_file::String_value::load_from(IO_CACHE *file)
{
size_t length= my_b_gets(file, buf, buf_size);
if (!length)
{
set_default();
return true;
}
/* If we stopped on a newline, kill it. */
char *last_char= buf + length - 1;
if (*last_char == '\n')
{
*last_char= '\0';
has_value= true;
return false;
}
/* Consume the lost line break, or error if the line overflowed. */
if (my_b_get(file) != '\n')
{
set_default();
return true;
}
has_value= true;
return false;
}
void Info_file::String_value::save_to(IO_CACHE *file)
{
my_b_write(file, (const uchar *) buf, strlen(buf));
}
/*
Read a master.info or relay_log.info file
@param values Read fixed position values here
@param size Number of values
@param default_line_count
Number of values in old files that does not have a line
count at first line
@param value_count_correction
1 for master.info files and 0 for relay_log_files.
This is because in master.info files the line_count includes
the counter while for relay_log it does not include the
row counter
*/
bool Info_file::load_from_file(Value *const *values, size_t size,
size_t default_line_count,
int value_count_correction)
{
long val;
/*
The first row is temporarily stored in the first value. If it is a
line count and not a log name (new format), the second row will
overwrite it. The first value, instead of a temporary buffer,
to be able to use the value if there is no line count.
Note that for this to work it requires that the first real value in
the file is a string!
*/
String_value *line1= dynamic_cast<String_value *>(values[0]);
if (line1->load_from(&file))
{
my_printf_error(ER_WRONG_VALUE, "Wrong data at start of master info file",
MYF(ME_FATAL | ME_ERROR_LOG));
return true;
}
char *end= str2int(line1->buf, line1->buf_size, 10, &val);
/*
If this first line was not a number - the line count, then it was
the first value for real, so the for loop should skip over it.
*/
size_t i= !end || *end != '\0';
size_t line_count= (i ?
default_line_count :
(size_t) val - value_count_correction);
for (; i < line_count; i++)
{
int chr;
if (i < size)
{
Value *value= values[i];
if (value)
{
my_off_t filepos= my_b_tell(&file);
if (value->load_from(&file))
{
if (!(reinit_io_cache(&file, READ_CACHE, filepos, 0, 0)))
{
char buf[160];
buf[0]= 0;
size_t length= my_b_gets(&file, buf, sizeof(buf));
/* Strip the trailing '\n' that my_b_gets() includes. */
if (length && buf[length - 1] == '\n')
buf[length - 1]= '\0';
my_error(ER_WRONG_VALUE, MYF(ME_FATAL | ME_ERROR_LOG),
value->variable_name, buf);
}
return true;
}
continue;
}
}
/*
Count and discard unrecognized lines. This is especially to prepare
for Master_info_file for MariaDB 10.0+, which reserves a bunch of
lines before its unique key=value section to accomodate any future
line-based (old-style) additions in MySQL.
*/
while ((chr= my_b_get(&file)) != '\n')
if (chr == my_b_EOF)
{
my_printf_error(ER_WRONG_VALUE, "Garbage at end of file",
MYF(ME_FATAL));
return true;
}
}
return false;
}
void Info_file::save_to_file(Value *const *values, size_t size,
size_t total_line_count)
{
DBUG_ASSERT(total_line_count > size);
my_b_seek(&file, 0);
/*
If the new contents take less space than the previous file contents,
we may leave trailing garbage. The garbage does not matter thanks
to the effective-line count on the first line of the file.
*/
uint_to_chars(&file, total_line_count);
my_b_write_byte(&file, '\n');
for (Value *const *value= values, * const *end= value + size;
value < end;
value++)
{
if (*value)
(*value)->save_to(&file);
my_b_write_byte(&file, '\n');
}
/* Pad additional reserved lines. */
for (; total_line_count > size; --total_line_count)
my_b_write_byte(&file, '\n');
}