Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 71 additions & 40 deletions sql/rpl_info_file.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
#define RPL_INFO_FILE_H

#include <cstdint> // uintN_t
#include <charconv> // std::from/to_chars and other helpers
#include <functional> // superclass of Info_file::Mem_fn
#include <my_sys.h> // IO_CACHE, FN_REFLEN, ...

Expand All @@ -36,7 +35,6 @@ namespace Int_IO_CACHE
*/
template<typename I> static constexpr size_t BUF_SIZE=
std::numeric_limits<I>::digits10 + 1 + std::numeric_limits<I>::is_signed;
static constexpr auto ERRC_OK= std::errc();

/**
@ref IO_CACHE (reading one line with the `\n`) version of std::from_chars()
Expand All @@ -45,19 +43,38 @@ namespace Int_IO_CACHE
*/
template<typename I> static bool from_chars(IO_CACHE *file, I &value)
{
int error;
/**
+2 for the terminating `\n\0` (They are ignored by
std::from_chars(), but my_b_gets() includes them.)
+2 for the terminating `\n\0`
(They are ignored, but my_b_gets() includes them.)
*/
char buf[BUF_SIZE<I> + 2];
/// includes the `\n` but excludes the `\0`
size_t length= my_b_gets(file, buf, sizeof(buf));
if (!length) // EOF
return true;
// SFINAE if `I` is not a numeric type
std::from_chars_result result= std::from_chars(buf, &(buf[length]), value);
// Return `true` if the conversion failed or if the number ended early
return result.ec != ERRC_OK || *(result.ptr) != '\n';
char *end= &(buf[length]);
longlong val= my_strtoll10(buf, &end, &error);
switch (error) {
case -1:
if (!std::numeric_limits<I>::is_signed)
return true;
[[fallthrough]];
case 0:
/*TODO
This upper range check is not needed when using type-
specific variants of a safe string-to-integer converter
(e.g., std::from_chars() when all platforms support it).
*/
if (*end == '\n' && value <= std::numeric_limits<I>::max())
{
value= static_cast<I>(val);
return false;
}
[[fallthrough]];
default:
return true;
}
}
/**
Convenience overload of from_chars(IO_CACHE *, I &) for `operator=` types
Expand All @@ -79,14 +96,19 @@ namespace Int_IO_CACHE
*/
template<typename I> static void to_chars(IO_CACHE *file, I value)
{
/**
my_b_printf() uses a buffer too,
so we might as well save on format parsing and buffer resizing
*/
char buf[BUF_SIZE<I>];
std::to_chars_result result= std::to_chars(buf, &buf[sizeof(buf)], value);
DBUG_ASSERT(result.ec == ERRC_OK);
my_b_write(file, reinterpret_cast<const uchar *>(buf), result.ptr - buf);
/*TODO:
* my_b_printf() needs updates and so doesn't
support `long long`s at the moment.
* We can avoid format parsing by expanding
int10_to_str() if not supporting std::to_chars().
*/
int len= std::numeric_limits<I>::is_signed ?
snprintf(buf, BUF_SIZE<I>, "%lld", static_cast<long long>(value)) :
snprintf(buf, BUF_SIZE<I>, "%llu", static_cast<unsigned long long>(value))
;
DBUG_ASSERT(len > 0);
my_b_write(file, reinterpret_cast<const uchar *>(buf), len);
}
};

Expand Down Expand Up @@ -209,7 +231,6 @@ struct Info_file
*/
struct Mem_fn: std::function<Persistent &(Info_file *self)>
{
using List= const std::initializer_list<Mem_fn>;
/// Null Constructor
Mem_fn(std::nullptr_t null= nullptr):
std::function<Persistent &(Info_file *)>(null) {}
Expand Down Expand Up @@ -242,36 +263,55 @@ struct Info_file
(either contains a `.` or is entirely empty) rather than an integer.
@return `false` if the file has parsed successfully or `true` if error
*/
bool load_from_file(Mem_fn::List value_list, size_t default_line_count)
template<size_t size> bool load_from_file(
const Mem_fn (&value_list)[size],
size_t default_line_count= 0
) { return load_from_file(value_list, size, default_line_count); }
/**
Flush the MySQL line-based section to the @ref file
@param value_list List of wrapped member pointers to values.
@param total_line_count
The number of lines to describe the file as on the first line of the file.
If this is larger than `value_list.size()`, suffix the file with empty
lines until the line count (including the line count line) is this many.
This reservation provides compatibility with MySQL,
who has added more old-style lines while MariaDB innovated.
*/
template<size_t size> void save_to_file(
const Mem_fn (&value_list)[size],
size_t total_line_count= size + /* line count line */ 1
) { return save_to_file(value_list, size, total_line_count); }

private:
bool
load_from_file(const Mem_fn *values, size_t size, size_t default_line_count)
{
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.
*/
auto &line1= dynamic_cast<String_value<> &>((*(value_list.begin()))(this));
auto &line1= dynamic_cast<String_value<> &>(values[0](this));
if (line1.load_from(&file))
return true;
size_t line_count;
std::from_chars_result result= std::from_chars(
line1.buf, &line1.buf[sizeof(line1.buf)], line_count);
char *end= str2int(line1.buf, 10, 0, INT32_MAX, &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 then skip over it, the index 0 of the list.
*/
size_t i= result.ec != Int_IO_CACHE::ERRC_OK || *(result.ptr) != '\0';
size_t i= !end || *end != '\0';
/*
Set the default after parsing: While std::from_chars() does not replace
the output if it failed, it does replace if the line is not fully spent.
*/
if (i)
line_count= default_line_count;
size_t line_count= i ? static_cast<size_t>(val) : default_line_count;
for (; i < line_count; ++i)
{
int c;
if (i < value_list.size()) // line known in the `value_list`
if (i < size) // line known in the `value_list`
{
const Mem_fn &pm= value_list.begin()[i];
const Mem_fn &pm= values[i];
if (pm)
{
if (pm(this).load_from(&file))
Expand All @@ -294,19 +334,9 @@ struct Info_file
return false;
}

/**
Flush the MySQL line-based section to the @ref file
@param value_list List of wrapped member pointers to values.
@param total_line_count
The number of lines to describe the file as on the first line of the file.
If this is larger than `value_list.size()`, suffix the file with empty
lines until the line count (including the line count line) is this many.
This reservation provides compatibility with MySQL,
who has added more old-style lines while MariaDB innovated.
*/
void save_to_file(Mem_fn::List value_list, size_t total_line_count)
void save_to_file(const Mem_fn *values, size_t size, size_t total_line_count)
{
DBUG_ASSERT(total_line_count > value_list.size());
DBUG_ASSERT(total_line_count > size);
my_b_seek(&file, 0);
/*
If the new contents take less space than the previous file contents,
Expand All @@ -316,8 +346,9 @@ struct Info_file
*/
Int_IO_CACHE::to_chars(&file, total_line_count);
my_b_write_byte(&file, '\n');
for (const Mem_fn &pm: value_list)
for (size_t i= 0; i < size; ++i)
{
const Mem_fn &pm= values[i];
if (pm)
pm(this).save_to(&file);
my_b_write_byte(&file, '\n');
Expand All @@ -327,7 +358,7 @@ struct Info_file
(1 for the line count line + line count) inclusive -> max line inclusive
= line count exclusive <- max line inclusive
*/
for (; total_line_count > value_list.size(); --total_line_count)
for (; total_line_count > size; --total_line_count)
my_b_write_byte(&file, '\n');
}

Expand Down
47 changes: 17 additions & 30 deletions sql/rpl_master_info_file.h
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ struct Master_info_file: Info_file

bool load_from(IO_CACHE *file) override
{
uint32_t count;
long count;
size_t i;
/// +1 for the terminating delimiter
char buf[Int_IO_CACHE::BUF_SIZE<uint32_t> + 1];
Expand All @@ -257,22 +257,18 @@ struct Master_info_file: Info_file
if (c == /* End of Line */ '\n' || c == /* End of Count */ ' ')
break;
}
/*
* std::from_chars() fails if `count` will overflow in any way.
* exclusive end index of the string = size
*/
std::from_chars_result result= std::from_chars(buf, &buf[i], count);
char *end= str2int(buf, 10, 1, INT32_MAX, &count);
// Reserve enough elements ahead of time.
if (result.ec != Int_IO_CACHE::ERRC_OK || allocate_dynamic(&array, count))
if (!end || allocate_dynamic(&array, count))
return true;
while (count--)
{
uint32_t value;
long value;
/*
Check that the previous number ended with a ` `,
not `\n` or anything else.
*/
if (*(result.ptr) != ' ')
if (*end != ' ')
return true;
for (i= 0; i < sizeof(buf); ++i)
{
Expand All @@ -287,8 +283,8 @@ struct Master_info_file: Info_file
if (c == /* End of Count */ ' ' || c == /* End of Line */ '\n')
break;
}
result= std::from_chars(buf, &buf[i], value);
if (result.ec != Int_IO_CACHE::ERRC_OK)
end= str2int(buf, 10, 1, INT32_MAX, &value);
if (!end)
return true;
ulong id= value;
bool oom= insert_dynamic(&array, (uchar *)&id);
Expand All @@ -301,7 +297,7 @@ struct Master_info_file: Info_file
return true;
}
// Check that the last number ended with a `\n`, not ` ` or anything else.
if (*(result.ptr) != '\n')
if (*end != '\n')
return true;
sort_dynamic(&array, change_master_id_cmp); // to be safe
return false;
Expand Down Expand Up @@ -417,8 +413,8 @@ struct Master_info_file: Info_file
}
void save_to(IO_CACHE *file) override
{
my_b_write_byte(file,
'0' + static_cast<unsigned char>(operator enum_master_use_gtid()));
my_b_write_byte(file, static_cast<uchar>(
'0' + static_cast<unsigned char>(operator enum_master_use_gtid())));
}
}
/// Using_Gtid
Expand Down Expand Up @@ -485,9 +481,9 @@ struct Master_info_file: Info_file
overprecise= decimal.frac > 3;
// decomposed from my_decimal2int() to reduce a bit of computations
auto rounded= my_decimal(), product= my_decimal();
int unexpected_error=
decimal_round(&decimal, &rounded, 3, HALF_UP) |
decimal_mul(&rounded, &THOUSAND, &product) |
bool unexpected_error=
decimal_round(&decimal, &rounded, 3, HALF_UP) ||
decimal_mul(&rounded, &THOUSAND, &product) ||
decimal2ulonglong(&product, &decimal_out);
DBUG_ASSERT(!unexpected_error);
result= static_cast<uint32_t>(decimal_out);
Expand Down Expand Up @@ -532,18 +528,8 @@ struct Master_info_file: Info_file
full advantage of the non-negative `DECIMAL(10,3)` format.
*/
void save_to(IO_CACHE *file) override {
char buf[Int_IO_CACHE::BUF_SIZE<uint32_t> - /* decimal part */ 3];
auto[integer_part, decimal_part]= div(operator uint32_t(), 1000);
std::to_chars_result result=
std::to_chars(buf, &buf[sizeof(buf)], integer_part);
DBUG_ASSERT(result.ec == Int_IO_CACHE::ERRC_OK);
my_b_write(file, reinterpret_cast<const uchar *>(buf), result.ptr - buf);
my_b_write_byte(file, '.');
result= std::to_chars(buf, &buf[sizeof(buf)], decimal_part);
DBUG_ASSERT(result.ec == Int_IO_CACHE::ERRC_OK);
for (ptrdiff_t digits= result.ptr - buf; digits < 3; ++digits)
my_b_write_byte(file, '0');
my_b_write(file, reinterpret_cast<const uchar *>(buf), result.ptr - buf);
my_b_printf(file, "%u.%03u", integer_part, decimal_part);
}
}
/// `Slave_heartbeat_period` of SHOW ALL SLAVES STATUS
Expand All @@ -552,7 +538,7 @@ struct Master_info_file: Info_file
/// }@


inline static Mem_fn::List VALUE_LIST= {
inline static const Mem_fn VALUE_LIST[] {
&Master_info_file::master_log_file,
&Master_info_file::master_log_pos,
&Master_info_file::master_host,
Expand Down Expand Up @@ -587,7 +573,8 @@ struct Master_info_file: Info_file
keys should match the corresponding old property name in @ref Master_info.
*/
// C++ default allocator to match that `mysql_execute_command()` uses `new`
inline static const std::unordered_map<std::string_view, Mem_fn> VALUE_MAP= {
inline static
const std::unordered_map<std::string_view, const Mem_fn> VALUE_MAP= {
/*
These are here to annotate whether they are `DEFAULT`.
They are repeated from @ref VALUE_LIST to enable bidirectional
Expand Down
5 changes: 2 additions & 3 deletions sql/rpl_relay_log_info_file.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ struct Relay_log_info_file: Info_file
Int_value<uint32_t> sql_delay;
/// }@

inline static const Mem_fn::List VALUE_LIST= {
inline static const Mem_fn VALUE_LIST[] {
&Relay_log_info_file::relay_log_file,
&Relay_log_info_file::relay_log_pos,
&Relay_log_info_file::read_master_log_file,
Expand All @@ -50,8 +50,7 @@ struct Relay_log_info_file: Info_file
}
void save_to_file() override
{
return Info_file::save_to_file(VALUE_LIST,
VALUE_LIST.size() + /* line count line */ 1);
return Info_file::save_to_file(VALUE_LIST);
}
};

Expand Down