Skip to content

Commit 8b8fe07

Browse files
PS-9777: binlog_utils_udf plugin should handle binlog.index entries the same as the server code does
https://perconadev.atlassian.net/browse/PS-9777 Problem: Some tools like PXB, when moving back a backup, can produce a malformed binlog.index file. This file contains an entry that is just a binlog file name without relative/absolute path. Server is able to handle sucha case in a proper way, however binlog_utils_udf plugin complains about not being able to find a binlog file. Cause: When the server processes queries like SHOW BINARY LOGS or SHOW BINLOG EVENTS IN, it does two things: 1. expand provided binlog name to the full binlog path 2. expand entries in binlog.index file to their full path Then it does comparison (to deduce if the provided binlog file name is a valid one) binlog_utils_udf does the expansion of the provided binlog file name, but gets binlog.index entries as they are. This causes that in problematic case the entry is not found in binlog.index file causing the error returned by plugin. Solution: Do the full expansion of binlog.index entries in the same way as the expansion of the provided file name is done.
1 parent 2df90d5 commit 8b8fe07

File tree

3 files changed

+73
-8
lines changed

3 files changed

+73
-8
lines changed

mysql-test/r/percona_binlog_utils_udf.result

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1655,7 +1655,5 @@ DELETE FROM loaded_ts;
16551655
LOAD DATA LOCAL INFILE 'MYSQLTEST_VARDIR/filtered_binlog_file' INTO TABLE loaded_ts;
16561656
include/assert.inc ['last record timestamp extracted via get_last_record_timestamp_by_binlog() for stage 16 should match the value extracted via mysqlbinlog']
16571657
DROP FUNCTION get_last_record_timestamp_by_binlog;
1658-
DROP TABLE loaded_ts;
16591658
UNINSTALL PLUGIN binlog_utils_udf;
1660-
DROP TABLE captured_gtid;
16611659
DROP TABLE t1;

mysql-test/t/percona_binlog_utils_udf.test

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -338,11 +338,59 @@ while($stage <= $number_of_stages)
338338
}
339339
DROP FUNCTION get_last_record_timestamp_by_binlog;
340340

341-
# temporary table cleanup
342-
DROP TABLE loaded_ts;
341+
342+
#
343+
# PS-9777 - binlog_utils_udf plugin should handle binlog.index entries the same as the server code does
344+
# Check that binlog_utils_udf plugin handles binlog.index file entries
345+
# that are filenames (no relative or absolute path) correctly.
346+
#
347+
--source include/have_util_sed.inc
348+
349+
# We only expect that the following won't fail at any stage. Actual results don't matter.
350+
--disable_query_log
351+
--disable_result_log
352+
eval CREATE FUNCTION get_gtid_set_by_binlog RETURNS STRING SONAME "$BINLOG_UTILS_UDF_LIB";
353+
eval CREATE FUNCTION get_binlog_by_gtid_set RETURNS STRING SONAME "$BINLOG_UTILS_UDF_LIB";
354+
eval CREATE FUNCTION get_binlog_by_gtid RETURNS STRING SONAME "$BINLOG_UTILS_UDF_LIB";
355+
eval CREATE FUNCTION get_last_gtid_from_binlog RETURNS STRING SONAME "$BINLOG_UTILS_UDF_LIB";
356+
eval CREATE FUNCTION get_first_record_timestamp_by_binlog RETURNS INTEGER SONAME "$BINLOG_UTILS_UDF_LIB";
357+
eval CREATE FUNCTION get_last_record_timestamp_by_binlog RETURNS INTEGER SONAME "$BINLOG_UTILS_UDF_LIB";
358+
359+
# Modify the index file (remove leading ./ leaving only binlog file names)
360+
--let $index_file = `SELECT @@log_bin_index`
361+
--exec $SED -i 's|^\./binlog\.|binlog.|' $index_file
362+
363+
# There will be no temp tables after restart
364+
--let $binlog_file_name = `SELECT binlog_name FROM captured_gtid LIMIT 1`
365+
--let $gtid_value = `SELECT gtid_value FROM captured_gtid LIMIT 1`
366+
367+
# mysqld uses a cache over index file. Restart the server to fetch the new index file content.
368+
--source include/restart_mysqld.inc
369+
370+
# restart re-enables query and resutl logs
371+
--disable_query_log
372+
--disable_result_log
373+
eval SELECT get_gtid_set_by_binlog('$binlog_file_name');
374+
--let $gtidset = `SELECT get_gtid_set_by_binlog('$binlog_file_name')`
375+
eval SELECT get_binlog_by_gtid_set('$gtidset');
376+
eval SELECT get_binlog_by_gtid('$gtid_value');
377+
eval SELECT get_last_gtid_from_binlog('$binlog_file_name');
378+
eval SELECT get_first_record_timestamp_by_binlog('$binlog_file_name');
379+
eval SELECT get_last_record_timestamp_by_binlog('$binlog_file_name');
380+
381+
DROP FUNCTION get_gtid_set_by_binlog;
382+
DROP FUNCTION get_binlog_by_gtid_set;
383+
DROP FUNCTION get_binlog_by_gtid;
384+
DROP FUNCTION get_last_gtid_from_binlog;
385+
DROP FUNCTION get_first_record_timestamp_by_binlog;
386+
DROP FUNCTION get_last_record_timestamp_by_binlog;
387+
--enable_query_log
388+
--enable_result_log
389+
390+
391+
# temporary table cleanup not needed as we restarted the server
343392

344393
--replace_result $BINLOG_UTILS_UDF_LIB BINLOG_UTILS_UDF_LIB
345394
UNINSTALL PLUGIN binlog_utils_udf;
346395

347-
DROP TABLE captured_gtid;
348396
DROP TABLE t1;

plugin/binlog_utils_udf/binlog_utils_udf.cc

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,25 @@ const char *get_short_binlog_name(const std::string &binlog_name) noexcept {
168168
return binlog_name.c_str() + dirname_length(binlog_name.c_str());
169169
}
170170

171+
//
172+
// Retrieves the contents of the index file. Then normalizes its entries (each
173+
// entry is prefixed with relative or absolute path)
174+
//
175+
std::pair<int, std::list<std::string>> get_normalized_log_index() {
176+
auto log_index = mysql_bin_log.get_log_index(true /* need_lock_index */);
177+
178+
auto &binlog_names = log_index.second;
179+
auto bg = std::begin(binlog_names);
180+
auto en = std::end(binlog_names);
181+
std::for_each(bg, en, [](std::string &s) {
182+
fn_reflen_buffer buf;
183+
check_and_normalize_binlog_name(get_short_binlog_name(s), buf);
184+
s.assign(buf);
185+
});
186+
187+
return log_index;
188+
}
189+
171190
log_event_ptr find_first_event(std::string_view binlog_name) {
172191
DBUG_TRACE;
173192

@@ -378,7 +397,7 @@ mysqlpp::udf_result_t<STRING_RESULT> get_binlog_by_gtid_impl::calculate(
378397
throw std::runtime_error("Cannot parse 'gtid_executed'");
379398
}
380399

381-
auto log_index = mysql_bin_log.get_log_index(true /* need_lock_index */);
400+
auto log_index = get_normalized_log_index();
382401
if (log_index.first != LOG_INFO_EOF)
383402
throw std::runtime_error("Cannot read binary log index'");
384403
if (log_index.second.empty())
@@ -480,7 +499,7 @@ mysqlpp::udf_result_t<STRING_RESULT> get_gtid_set_by_binlog_impl::calculate(
480499
const mysqlpp::udf_context &ctx) {
481500
DBUG_TRACE;
482501

483-
auto log_index = mysql_bin_log.get_log_index(true /* need_lock_index */);
502+
auto log_index = get_normalized_log_index();
484503
if (log_index.first != LOG_INFO_EOF)
485504
throw std::runtime_error("Cannot read binary log index");
486505
if (log_index.second.empty())
@@ -584,7 +603,7 @@ mysqlpp::udf_result_t<STRING_RESULT> get_binlog_by_gtid_set_impl::calculate(
584603
throw std::runtime_error("Cannot parse 'gtid_executed'");
585604
}
586605

587-
auto log_index = mysql_bin_log.get_log_index(true /* need_lock_index */);
606+
auto log_index = get_normalized_log_index();
588607
if (log_index.first != LOG_INFO_EOF)
589608
throw std::runtime_error("Cannot read binary log index'");
590609
if (log_index.second.empty())

0 commit comments

Comments
 (0)