mirror of
https://github.com/webmin/webmin.git
synced 2026-08-29 01:40:39 +01:00
Add MySQL binary log management
This PR adds a "Binary Logging" page to configure log format, file size and retention, rotate or purge logs, and support restore sessions that do not write back to binary logs.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
---- Changes since 2.641 ----
|
||||
Added defaults for alpine linux and fix package installs
|
||||
Added a Binary Logging page for enabling binary logs, setting their format, maximum file size and retention period, rotating logs on demand, and deleting old log files.
|
||||
---- Changes since 1.140 ----
|
||||
The not null flag and a default value can be specified for fields in new tables.
|
||||
The form for creating an initial table in a new database is now the same as the one for adding a table to an existing database.
|
||||
|
||||
169
mysql/edit_binlogs.cgi
Executable file
169
mysql/edit_binlogs.cgi
Executable file
@@ -0,0 +1,169 @@
|
||||
#!/usr/local/bin/perl
|
||||
# Show binary logging status, configuration options and log cleanup
|
||||
|
||||
require './mysql-lib.pl';
|
||||
$access{'perms'} == 1 || &error($text{'binlogs_ecannot'});
|
||||
&ReadParse();
|
||||
&ui_print_header(undef, $text{'binlogs_title'}, "", "binlogs");
|
||||
|
||||
# Make sure config exists
|
||||
$conf = &get_mysql_config();
|
||||
if (!$conf) {
|
||||
print &text('cnf_efile', "<tt>$config{'my_cnf'}</tt>",
|
||||
"../config.cgi?$module_name"),"<p>\n";
|
||||
&ui_print_footer("", $text{'index_return'});
|
||||
exit;
|
||||
}
|
||||
# Prefer the main server section over generic MariaDB sections that can
|
||||
# belong to a plugin-specific include file
|
||||
($mysqld) = grep { $_->{'name'} eq 'mysqld' } @$conf;
|
||||
($mysqld) = grep { $_->{'name'} eq 'mariadbd' } @$conf if (!$mysqld);
|
||||
($mysqld) = grep { $_->{'name'} eq 'mariadb' } @$conf if (!$mysqld);
|
||||
$mysqld || &error($text{'cnf_emysqld'});
|
||||
$mems = $mysqld->{'members'};
|
||||
$local = &is_mysql_local();
|
||||
|
||||
# Get the status reported by the running server
|
||||
($curfile, $curpos) = &get_binary_log_status();
|
||||
@logs = &list_binary_logs();
|
||||
$liveformat = eval {
|
||||
local $main::error_must_die = 1;
|
||||
my $d = &execute_sql($master_db,
|
||||
"show variables like 'binlog_format'");
|
||||
@{$d->{'data'}} ? $d->{'data'}->[0]->[1] : undef;
|
||||
};
|
||||
|
||||
# Show tabs for configuration and log cleanup
|
||||
@tabs = ( [ 'options', $text{'binlogs_tab_options'} ],
|
||||
$curfile ? ( [ 'purge', $text{'binlogs_tab_purge'} ] ) : ( ) );
|
||||
%valid = map { $_->[0] => 1 } @tabs;
|
||||
$mode = $in{'mode'} && $valid{$in{'mode'}} ? $in{'mode'} : 'options';
|
||||
print &ui_tabs_start(\@tabs, "mode", $mode, 1);
|
||||
|
||||
# Configuration tab
|
||||
print &ui_tabs_start_tab("mode", "options");
|
||||
print &ui_div($text{'binlogs_desc_options'});
|
||||
print &ui_form_start("save_binlogs.cgi", "post");
|
||||
print &ui_table_start($text{'binlogs_header'}, "width=100%", 2);
|
||||
|
||||
# Current state, as one read-only row
|
||||
if ($curfile) {
|
||||
$total = 0;
|
||||
foreach $l (@logs) {
|
||||
$total += $l->{'size'};
|
||||
}
|
||||
print &ui_table_row($text{'binlogs_active'},
|
||||
&text('binlogs_activeyes', "<tt>".&html_escape($curfile)."</tt>",
|
||||
$curpos, scalar(@logs), &nice_size($total)).
|
||||
($liveformat ? " ".&text('binlogs_activefmt',
|
||||
uc($liveformat)) : ""));
|
||||
}
|
||||
else {
|
||||
print &ui_table_row($text{'binlogs_active'}, $text{'no'});
|
||||
}
|
||||
|
||||
if ($local) {
|
||||
# Enabled state considers both positive and negative directives, the
|
||||
# live server status, and finally whether this server version logs
|
||||
# by default when nothing is configured (as MySQL 8+ does)
|
||||
$lbdir = &find("log_bin", $mems) || &find("log-bin", $mems);
|
||||
$skipdir = &find("skip-log-bin", $mems) ||
|
||||
&find("skip_log_bin", $mems) ||
|
||||
&find("disable-log-bin", $mems) ||
|
||||
&find("disable_log_bin", $mems);
|
||||
$lb = $lbdir ? $lbdir->{'value'} : undef;
|
||||
$enabled = $skipdir ? 0 : $lbdir ? 1 : $curfile ? 1 :
|
||||
&get_binlog_default_on();
|
||||
print &ui_table_row(&hlink($text{'binlogs_enabled'},
|
||||
"binlogs_enabled"),
|
||||
&ui_yesno_radio("enabled", $enabled));
|
||||
|
||||
print &ui_table_row(&hlink($text{'binlogs_base'}, "binlogs_base"),
|
||||
&ui_opt_textbox("base", $lb, 30, $text{'binlogs_base_def'}));
|
||||
|
||||
$sid = &find_value("server_id", $mems);
|
||||
$sid = &find_value("server-id", $mems) if (!defined($sid));
|
||||
print &ui_table_row(&hlink($text{'binlogs_serverid'},
|
||||
"binlogs_serverid"),
|
||||
&ui_opt_textbox("serverid", $sid, 10, $text{'default'}));
|
||||
|
||||
# Log format, which must be ROW for point-in-time recovery
|
||||
$fmt = &find_value("binlog_format", $mems);
|
||||
$fmt = &find_value("binlog-format", $mems) if (!defined($fmt));
|
||||
print &ui_table_row(&hlink($text{'binlogs_format'},
|
||||
"binlogs_format"),
|
||||
&ui_select("format", uc($fmt // ''),
|
||||
[ [ '', $text{'default'} ],
|
||||
[ 'ROW', $text{'binlogs_format_row'} ],
|
||||
[ 'MIXED', $text{'binlogs_format_mixed'} ],
|
||||
[ 'STATEMENT',
|
||||
$text{'binlogs_format_statement'} ] ]));
|
||||
|
||||
# Maximum size of one log file before rotation
|
||||
$maxsize = &find_value("max_binlog_size", $mems);
|
||||
$maxsize = &find_value("max-binlog-size", $mems)
|
||||
if (!defined($maxsize));
|
||||
print &ui_table_row(&hlink($text{'binlogs_maxsize'},
|
||||
"binlogs_maxsize"),
|
||||
&ui_radio("maxsize_def", defined($maxsize) ? 0 : 1,
|
||||
[ [ 1, $text{'default'} ], [ 0, " " ] ])."\n".
|
||||
&mysql_size_input("maxsize", $maxsize));
|
||||
|
||||
# Retention period, stored in a variant-specific variable
|
||||
($ver, $variant) = &get_mysql_variant_cached();
|
||||
$secs = &find_value("binlog_expire_logs_seconds", $mems);
|
||||
$days = &find_value("expire_logs_days", $mems);
|
||||
$expire = defined($secs) ? &format_binlog_expire_days($secs) :
|
||||
defined($days) ? $days : undef;
|
||||
print &ui_table_row(&hlink($text{'binlogs_expire'},
|
||||
"binlogs_expire"),
|
||||
&ui_opt_textbox("expire", $expire, 6,
|
||||
$text{'binlogs_expire_def'}).
|
||||
" ".$text{'binlogs_days'});
|
||||
|
||||
print &ui_table_end();
|
||||
print &ui_form_end([ [ "save", $text{'save'} ],
|
||||
&is_mysql_running() > 0 ?
|
||||
( [ "restart",
|
||||
$text{'binlogs_restart'} ] ) : ( ) ]);
|
||||
}
|
||||
else {
|
||||
# Config file editing only works for a local database server
|
||||
print &ui_table_end();
|
||||
print &ui_form_end();
|
||||
print &ui_alert($text{'binlogs_eremote'}, 'info');
|
||||
}
|
||||
print &ui_tabs_end_tab("mode", "options");
|
||||
|
||||
# Log cleanup tab
|
||||
if ($curfile) {
|
||||
print &ui_tabs_start_tab("mode", "purge");
|
||||
print &ui_div($text{'binlogs_desc_purge'});
|
||||
print &ui_form_start("purge_binlogs.cgi", "post");
|
||||
print &ui_table_start($text{'binlogs_purgeheader'},
|
||||
"width=100%", 2);
|
||||
print &ui_table_row(&hlink($text{'binlogs_purge'},
|
||||
"binlogs_purge"),
|
||||
&ui_radio("mode", 0,
|
||||
[ [ 0, &text('binlogs_purgedays',
|
||||
&ui_textbox("days", 7, 6))."<br>" ],
|
||||
[ 1, $text{'binlogs_purgeall'} ] ]));
|
||||
print &ui_table_end();
|
||||
print &ui_form_end([ [ "purge", $text{'binlogs_purgeok'} ] ]);
|
||||
print &ui_tabs_end_tab("mode", "purge");
|
||||
}
|
||||
|
||||
print &ui_tabs_end();
|
||||
|
||||
# Button to close the current log and start a new one, which gives backups
|
||||
# and log deletion a clean boundary to work with
|
||||
if ($curfile) {
|
||||
print &ui_hr();
|
||||
print &ui_buttons_start();
|
||||
print &ui_buttons_row("flush_binlogs.cgi", $text{'binlogs_rotateok'},
|
||||
&text('binlogs_rotatedesc',
|
||||
"<tt>".&html_escape($curfile)."</tt>"));
|
||||
print &ui_buttons_end();
|
||||
}
|
||||
|
||||
&ui_print_footer("", $text{'index_return'});
|
||||
@@ -13,9 +13,11 @@ if (!$conf) {
|
||||
&ui_print_footer("", $text{'index_return'});
|
||||
exit;
|
||||
}
|
||||
($mysqld) = grep { $_->{'name'} eq 'mysqld' ||
|
||||
$_->{'name'} eq 'mariadbd' ||
|
||||
$_->{'name'} eq 'mariadb' } @$conf;
|
||||
# Prefer the main server section over generic MariaDB sections that can
|
||||
# belong to a plugin-specific include file
|
||||
($mysqld) = grep { $_->{'name'} eq 'mysqld' } @$conf;
|
||||
($mysqld) = grep { $_->{'name'} eq 'mariadbd' } @$conf if (!$mysqld);
|
||||
($mysqld) = grep { $_->{'name'} eq 'mariadb' } @$conf if (!$mysqld);
|
||||
$mysqld || &error($text{'cnf_emysqld'});
|
||||
$mems = $mysqld->{'members'};
|
||||
|
||||
@@ -110,4 +112,3 @@ print &ui_form_end([ [ "save", $text{'save'} ],
|
||||
[ "restart", $text{'cnf_restart'} ] ]);
|
||||
|
||||
&ui_print_footer("", $text{'index_return'});
|
||||
|
||||
|
||||
13
mysql/flush_binlogs.cgi
Executable file
13
mysql/flush_binlogs.cgi
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/usr/local/bin/perl
|
||||
# Close the current binary log and start writing a new one
|
||||
|
||||
require './mysql-lib.pl';
|
||||
$access{'perms'} == 1 || &error($text{'binlogs_ecannot'});
|
||||
&error_setup($text{'binlogs_rotateerr'});
|
||||
&ReadParse();
|
||||
|
||||
my ($curfile) = &get_binary_log_status();
|
||||
$curfile || &error($text{'binlogs_epurgeoff'});
|
||||
&rotate_binary_logs();
|
||||
&webmin_log("flushbinlogs");
|
||||
&redirect("edit_binlogs.cgi");
|
||||
18
mysql/help/binlogs.html
Normal file
18
mysql/help/binlogs.html
Normal file
@@ -0,0 +1,18 @@
|
||||
<header>Binary Logging</header>
|
||||
|
||||
This page can be used to enable or disable binary logging on the MySQL or
|
||||
MariaDB database server, and to manage existing binary log files. Binary logs
|
||||
record all changes made to databases, and are needed for replication and for
|
||||
point-in-time recovery of backups. <p>
|
||||
|
||||
When binary logging is enabled, log files are written under the configured
|
||||
base name and grow as changes are made. To stop them from filling up the disk,
|
||||
a retention period can be set after which old logs are automatically deleted,
|
||||
or old logs can be deleted immediately using the form at the bottom of the
|
||||
page. <p>
|
||||
|
||||
For any changes on this page to take effect, the MySQL server must be
|
||||
re-started, either with the <b>Save and Restart MySQL</b> button or using the
|
||||
buttons on the module's main page. <p>
|
||||
|
||||
<footer>
|
||||
6
mysql/help/binlogs_base.html
Normal file
6
mysql/help/binlogs_base.html
Normal file
@@ -0,0 +1,6 @@
|
||||
<header>Binary log base name</header>
|
||||
|
||||
<p>The base name for binary log files, to which a sequence number like
|
||||
<tt>.000001</tt> is appended. A relative name creates the logs in the server's
|
||||
data directory, while an absolute path can be used to store them elsewhere,
|
||||
such as on a separate disk.</p>
|
||||
6
mysql/help/binlogs_enabled.html
Normal file
6
mysql/help/binlogs_enabled.html
Normal file
@@ -0,0 +1,6 @@
|
||||
<header>Binary logging enabled?</header>
|
||||
|
||||
<p>When set to <b>Yes</b>, the database server writes all changes made to
|
||||
databases to binary log files. These logs are needed for replication, and for
|
||||
point-in-time recovery of backups. The server must be re-started for a change
|
||||
to this option to take effect.</p>
|
||||
7
mysql/help/binlogs_expire.html
Normal file
7
mysql/help/binlogs_expire.html
Normal file
@@ -0,0 +1,7 @@
|
||||
<header>Automatically delete logs older than</header>
|
||||
|
||||
<p>The number of days that binary log files are kept before the server deletes
|
||||
them automatically. Without a limit, logs will grow until they fill up the
|
||||
disk. For point-in-time recovery the retention period must be at least as long
|
||||
as the interval between full backups, so that all transactions since the last
|
||||
backup remain available for replay.</p>
|
||||
10
mysql/help/binlogs_format.html
Normal file
10
mysql/help/binlogs_format.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<header>Binary log format</header>
|
||||
|
||||
<p>Controls how changes are recorded in the binary logs. <b>Row based</b>
|
||||
logs the actual row changes, which is the most reliable format and is required
|
||||
for point-in-time recovery of individual databases. <b>Statement based</b>
|
||||
logs the SQL statements that were executed, and <b>Mixed</b> switches between
|
||||
the two automatically.</p>
|
||||
|
||||
<p>Row based logging is recommended, as statement and mixed logs cannot be
|
||||
safely filtered by database when replaying transactions.</p>
|
||||
6
mysql/help/binlogs_maxsize.html
Normal file
6
mysql/help/binlogs_maxsize.html
Normal file
@@ -0,0 +1,6 @@
|
||||
<header>Maximum size of one log file</header>
|
||||
|
||||
<p>When a binary log file reaches this size, the server closes it and starts
|
||||
writing a new one. Smaller files make deleting old logs and point-in-time
|
||||
recovery more granular, at the cost of more files. The server accepts sizes
|
||||
between 4 kB and 1 GB, which is also the default.</p>
|
||||
7
mysql/help/binlogs_purge.html
Normal file
7
mysql/help/binlogs_purge.html
Normal file
@@ -0,0 +1,7 @@
|
||||
<header>Binary logs to delete</header>
|
||||
|
||||
<p>Deletes old binary log files from the server immediately to free up disk
|
||||
space, either those older than the chosen number of days, or all logs except
|
||||
the one currently being written. Logs that have been deleted can no longer be
|
||||
used for replication or point-in-time recovery, so make sure they are not
|
||||
needed by any replica or for replaying transactions since the last backup.</p>
|
||||
7
mysql/help/binlogs_serverid.html
Normal file
7
mysql/help/binlogs_serverid.html
Normal file
@@ -0,0 +1,7 @@
|
||||
<header>Unique server ID</header>
|
||||
|
||||
<p>A numeric ID identifying this server in a replication setup, which must be
|
||||
different on every server that replicates with others. MySQL requires an ID to
|
||||
be set when binary logging is enabled, so when this option is left as
|
||||
<b>Default</b> and no ID is configured yet, an ID of <tt>1</tt> is used. On a
|
||||
stand-alone server the value does not matter.</p>
|
||||
8
mysql/images/binlogs.svg
Normal file
8
mysql/images/binlogs.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48">
|
||||
<ellipse cx="20" cy="10" rx="14" ry="5" fill="#eef4ff" stroke="#2563eb" stroke-width="2"/>
|
||||
<path d="M6 10v22c0 2.8 6.3 5 14 5 2.1 0 4.1-.2 5.9-.5" fill="none" stroke="#2563eb" stroke-width="2"/>
|
||||
<path d="M34 10v10" fill="none" stroke="#2563eb" stroke-width="2"/>
|
||||
<path d="M6 21c0 2.8 6.3 5 14 5 2.6 0 5-.3 7-.7" fill="none" stroke="#2563eb" stroke-width="2"/>
|
||||
<circle cx="34" cy="32" r="10" fill="#eef4ff" stroke="#2563eb" stroke-width="2"/>
|
||||
<path d="M34 26v6l4.5 3" fill="none" stroke="#1e3a8a" stroke-width="2.5" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 643 B |
@@ -261,14 +261,16 @@ else {
|
||||
@links = ( 'list_users.cgi', 'list_dbs.cgi',
|
||||
$canhosts ? ( 'list_hosts.cgi' ) : ( ),
|
||||
'list_tprivs.cgi', 'list_cprivs.cgi',
|
||||
'edit_cnf.cgi', 'edit_manual.cgi', 'list_procs.cgi',
|
||||
'edit_cnf.cgi', 'edit_binlogs.cgi',
|
||||
'edit_manual.cgi', 'list_procs.cgi',
|
||||
$canvars ? ( 'list_vars.cgi' ) : ( ),
|
||||
'edit_ssl.cgi', 'root_form.cgi',
|
||||
);
|
||||
@titles = ( $text{'users_title'}, $text{'dbs_title'},
|
||||
$canhosts ? ( $text{'hosts_title'} ) : ( ),
|
||||
$text{'tprivs_title'}, $text{'cprivs_title'},
|
||||
$text{'cnf_title'}, $text{'manual_title'},
|
||||
$text{'cnf_title'}, $text{'binlogs_title'},
|
||||
$text{'manual_title'},
|
||||
$text{'procs_title'},
|
||||
$canvars ? ( $text{'vars_title'} ) : ( ),
|
||||
$text{'ssl_title'}, $text{'root_title'},
|
||||
@@ -276,7 +278,8 @@ else {
|
||||
@images = ( 'images/users.gif', 'images/dbs.gif',
|
||||
$canhosts ? ( 'images/hosts.gif' ) : ( ),
|
||||
'images/tprivs.gif', 'images/cprivs.gif',
|
||||
'images/cnf.gif', 'images/manual.gif',
|
||||
'images/cnf.gif', 'images/binlogs.svg',
|
||||
'images/manual.gif',
|
||||
'images/procs.gif',
|
||||
$canvars ? ( 'images/vars.gif' ) : ( ),
|
||||
'images/ssl.gif', 'images/root.gif',
|
||||
|
||||
@@ -921,4 +921,53 @@ ssl_ekeyexists=SSL key file $1 already exists!
|
||||
|
||||
stop_ecannot=You are not allowed to stop or start the MySQL server
|
||||
|
||||
binlogs_title=Binary Logging
|
||||
binlogs_tab_options=Binary Logging Options
|
||||
binlogs_tab_purge=Delete Logs
|
||||
binlogs_desc_options=Binary logs record all changes made to databases, and are needed for replication and point-in-time recovery of backups. These options control whether the database server writes them, in what format, and for how long they are kept.
|
||||
binlogs_desc_purge=Old binary logs can be deleted immediately to free up disk space, as long as they are no longer needed by any replica or for replaying transactions since the last backup.
|
||||
binlogs_active=Binary logging active?
|
||||
binlogs_activeyes=Yes, currently writing to $1 at position $2 ($3 files of total size $4)
|
||||
binlogs_activefmt=in $1 format
|
||||
binlogs_header=Binary logging options
|
||||
binlogs_enabled=Binary logging enabled?
|
||||
binlogs_base=Binary log base name
|
||||
binlogs_base_def=Default <tt>(mysql-bin)</tt>
|
||||
binlogs_serverid=Unique server ID
|
||||
binlogs_format=Binary log format
|
||||
binlogs_format_row=Row based
|
||||
binlogs_format_mixed=Mixed
|
||||
binlogs_format_statement=Statement based
|
||||
binlogs_maxsize=Maximum size of one log file
|
||||
binlogs_expire=Automatically delete logs older than
|
||||
binlogs_expire_def=Server default
|
||||
binlogs_days=days
|
||||
binlogs_restart=Save and Restart MySQL
|
||||
binlogs_rotatedesc=Click this button to close the current binary log $1 and start writing a new one, giving backups and log deletion a clean boundary.
|
||||
binlogs_rotateok=Rotate Binary Logs
|
||||
binlogs_rotateerr=Failed to rotate binary logs
|
||||
binlogs_purgeheader=Delete old binary logs
|
||||
binlogs_purge=Binary logs to delete
|
||||
binlogs_purgedays=Logs older than $1 days
|
||||
binlogs_purgeall=All except the current log
|
||||
binlogs_purgeok=Delete Now
|
||||
binlogs_err=Failed to save binary logging options
|
||||
binlogs_ecannot=You are not allowed to configure binary logging
|
||||
binlogs_eremote=Binary logging can only be configured when the database server is running on this system
|
||||
binlogs_ebase=Missing or invalid binary log base name
|
||||
binlogs_eserverid=Server ID must be a number between 1 and 4294967295
|
||||
binlogs_eformat=Invalid binary log format
|
||||
binlogs_eexpire=Missing or invalid number of days to keep logs
|
||||
binlogs_eexpiremax=The number of days to keep logs cannot be more than $1
|
||||
binlogs_eexpireint=Only a whole number of days to keep logs is supported on this MySQL version
|
||||
binlogs_emaxsize=Missing or invalid maximum log file size
|
||||
binlogs_emaxsizerange=The maximum log file size must be between 4 kB and 1 GB
|
||||
binlogs_purgeerr=Failed to delete binary logs
|
||||
binlogs_epurgedays=Missing or invalid number of days to keep logs
|
||||
binlogs_epurgeoff=Binary logging is not active
|
||||
|
||||
log_binlogs=Changed binary logging options
|
||||
log_purgebinlogs=Deleted old binary logs
|
||||
log_flushbinlogs=Rotated binary logs
|
||||
|
||||
__norefs=1
|
||||
|
||||
@@ -20,6 +20,15 @@ elsif ($action eq 'restart') {
|
||||
elsif ($action eq 'cnf') {
|
||||
return $text{'log_cnf'};
|
||||
}
|
||||
elsif ($action eq 'binlogs') {
|
||||
return $text{'log_binlogs'};
|
||||
}
|
||||
elsif ($action eq 'purgebinlogs') {
|
||||
return $text{'log_purgebinlogs'};
|
||||
}
|
||||
elsif ($action eq 'flushbinlogs') {
|
||||
return $text{'log_flushbinlogs'};
|
||||
}
|
||||
elsif ($action eq 'ssl') {
|
||||
return $text{'log_ssl'};
|
||||
}
|
||||
|
||||
@@ -370,6 +370,18 @@ if ($driver_handle && !$config{'nodbi'}) {
|
||||
local $dbh = $driver_handle->connect($cstr, $mysql_login, $mysql_pass,
|
||||
{ });
|
||||
$dbh || &error("DBI connect failed : ",$driver_handle->errstr);
|
||||
if ($disable_session_binlog) {
|
||||
# A point-in-time restore must not add its own SQL to the log that
|
||||
# it is replaying, because that would make later retries unsafe
|
||||
local $nologsql = "set session sql_log_bin = 0";
|
||||
local $nologcmd = $dbh->prepare($nologsql);
|
||||
if (!$nologcmd || !$nologcmd->execute()) {
|
||||
&error(&text('esql',
|
||||
"<tt>".&html_escape($nologsql)."</tt>",
|
||||
"<tt>".&html_escape($dbh->errstr)."</tt>"));
|
||||
}
|
||||
$nologcmd->finish();
|
||||
}
|
||||
if ($sql_charset) {
|
||||
# Switch to correct character set
|
||||
local $sql = "set names '$sql_charset'";
|
||||
@@ -411,6 +423,10 @@ else {
|
||||
$sql = &replace_sql_parameters($sql, @params);
|
||||
}
|
||||
open(TEMP, ">$temp");
|
||||
if ($disable_session_binlog) {
|
||||
# Keep point-in-time restore statements out of the binary log
|
||||
print TEMP "set session sql_log_bin = 0;\n";
|
||||
}
|
||||
if ($sql_charset) {
|
||||
print TEMP "set names '$sql_charset';\n";
|
||||
}
|
||||
@@ -928,6 +944,38 @@ if ($ver && $variant_ &&
|
||||
return ($rv, $variant);
|
||||
}
|
||||
|
||||
# get_binary_log_status()
|
||||
# Returns the current binary log file and position, if binary logging is
|
||||
# active on the server
|
||||
sub get_binary_log_status
|
||||
{
|
||||
# The statement name varies between MySQL and MariaDB releases
|
||||
foreach my $sql ("show master status", "show binary log status",
|
||||
"show binlog status") {
|
||||
my $d = eval {
|
||||
local $main::error_must_die = 1;
|
||||
&execute_sql($master_db, $sql);
|
||||
};
|
||||
next if ($@ || !$d);
|
||||
return ( ) if (!@{$d->{'data'}});
|
||||
return ($d->{'data'}->[0]->[0], $d->{'data'}->[0]->[1]);
|
||||
}
|
||||
return ( );
|
||||
}
|
||||
|
||||
# list_binary_logs()
|
||||
# Returns a list of binary log files on the server, each as a hash ref with
|
||||
# name and size keys
|
||||
sub list_binary_logs
|
||||
{
|
||||
my $d = eval {
|
||||
local $main::error_must_die = 1;
|
||||
&execute_sql($master_db, "show binary logs");
|
||||
};
|
||||
return ( ) if ($@ || !$d);
|
||||
return map { { 'name' => $_->[0], 'size' => $_->[1] } } @{$d->{'data'}};
|
||||
}
|
||||
|
||||
# save_mysql_version([number])
|
||||
# Update the saved local MySQL version number
|
||||
sub save_mysql_version
|
||||
@@ -1122,6 +1170,10 @@ my $cs = $sql_charset ? "--default-character-set=".quotemeta($sql_charset)
|
||||
my $temp = &transname();
|
||||
$file = &fix_collation($file);
|
||||
&open_tempfile(TEMP, ">$temp");
|
||||
if ($disable_session_binlog) {
|
||||
# Keep an imported backup or decoded binary log out of the source log
|
||||
&print_tempfile(TEMP, "set session sql_log_bin = 0;\n");
|
||||
}
|
||||
&print_tempfile(TEMP, "source ".$file.";\n");
|
||||
&close_tempfile(TEMP);
|
||||
&set_ownership_permissions(undef, undef, 0644, $temp);
|
||||
@@ -1207,6 +1259,81 @@ return $config{'host'} eq '' || $config{'host'} eq 'localhost' ||
|
||||
&to_ipaddress($config{'host'}) eq &to_ipaddress(&get_system_hostname());
|
||||
}
|
||||
|
||||
# supports_disable_session_binlog()
|
||||
# Returns 1 if restore callers can request that their SQL sessions not be
|
||||
# written to the server's binary log.
|
||||
sub supports_disable_session_binlog
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
# format_binlog_expire_days(seconds)
|
||||
# Converts a seconds-based binary log retention period to a days value with
|
||||
# enough decimal places to convert back to the exact same number of seconds
|
||||
sub format_binlog_expire_days
|
||||
{
|
||||
my ($secs) = @_;
|
||||
my $days = sprintf("%.6f", $secs / 86400);
|
||||
$days =~ s/0+$//;
|
||||
$days =~ s/\.$//;
|
||||
return $days;
|
||||
}
|
||||
|
||||
# parse_binlog_expire_days(days)
|
||||
# Converts a days-based retention period back to the whole number of seconds
|
||||
# stored on the server
|
||||
sub parse_binlog_expire_days
|
||||
{
|
||||
my ($days) = @_;
|
||||
return int($days * 86400 + 0.5);
|
||||
}
|
||||
|
||||
# get_mysql_variant_cached()
|
||||
# Like get_remote_mysql_variant, but falls back to the version detected at
|
||||
# module setup time when the server cannot be queried, such as when it is
|
||||
# stopped
|
||||
sub get_mysql_variant_cached
|
||||
{
|
||||
my ($ver, $variant) = &get_remote_mysql_variant();
|
||||
if (!$ver || $ver <= 0) {
|
||||
$ver = $mysql_version;
|
||||
$variant = $ver =~ /mariadb/i ? "mariadb" : "mysql";
|
||||
$ver =~ s/[^0-9\.].*$//;
|
||||
}
|
||||
return ($ver, $variant);
|
||||
}
|
||||
|
||||
# get_binlog_default_on()
|
||||
# Returns 1 if this database server enables binary logging by default when
|
||||
# no directive is present, as MySQL does from version 8.0 onwards
|
||||
sub get_binlog_default_on
|
||||
{
|
||||
my ($ver, $variant) = &get_mysql_variant_cached();
|
||||
return $variant eq "mysql" && $ver &&
|
||||
&compare_version_numbers($ver, "8.0") >= 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
# parse_binlog_max_size(number, units)
|
||||
# Converts a maximum log file size input to bytes, returning undef if it is
|
||||
# invalid or outside the range from 4 kB to 1 GB that servers support
|
||||
sub parse_binlog_max_size
|
||||
{
|
||||
my ($num, $units) = @_;
|
||||
$num =~ /^\d+$/ || return undef;
|
||||
my %mult = ( '' => 1, 'K' => 1024, 'M' => 1024*1024,
|
||||
'G' => 1024*1024*1024 );
|
||||
defined($mult{$units}) || return undef;
|
||||
my $bytes = $num * $mult{$units};
|
||||
return $bytes >= 4096 && $bytes <= 1024*1024*1024 ? $bytes : undef;
|
||||
}
|
||||
|
||||
# rotate_binary_logs()
|
||||
# Closes the binary log currently being written and starts a new one
|
||||
sub rotate_binary_logs
|
||||
{
|
||||
&execute_sql_logged($master_db, "flush binary logs");
|
||||
}
|
||||
|
||||
# get_mysql_config()
|
||||
# Returns the parsed my.cnf file
|
||||
sub get_mysql_config
|
||||
|
||||
25
mysql/purge_binlogs.cgi
Executable file
25
mysql/purge_binlogs.cgi
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/usr/local/bin/perl
|
||||
# Delete old binary logs from the server
|
||||
|
||||
require './mysql-lib.pl';
|
||||
$access{'perms'} == 1 || &error($text{'binlogs_ecannot'});
|
||||
&error_setup($text{'binlogs_purgeerr'});
|
||||
&ReadParse();
|
||||
|
||||
my ($curfile) = &get_binary_log_status();
|
||||
$curfile || &error($text{'binlogs_epurgeoff'});
|
||||
if ($in{'mode'} == 0) {
|
||||
# Purge logs older than some number of days
|
||||
$in{'days'} =~ /^\d+$/ && $in{'days'} > 0 ||
|
||||
&error($text{'binlogs_epurgedays'});
|
||||
&execute_sql_logged($master_db,
|
||||
"purge binary logs before date_sub(now(), interval ".
|
||||
$in{'days'}." day)");
|
||||
}
|
||||
else {
|
||||
# Purge all logs except the one currently being written
|
||||
&execute_sql_logged($master_db,
|
||||
"purge binary logs to '".$curfile."'");
|
||||
}
|
||||
&webmin_log("purgebinlogs");
|
||||
&redirect("edit_binlogs.cgi");
|
||||
160
mysql/save_binlogs.cgi
Executable file
160
mysql/save_binlogs.cgi
Executable file
@@ -0,0 +1,160 @@
|
||||
#!/usr/local/bin/perl
|
||||
# Save binary logging configuration options
|
||||
|
||||
require './mysql-lib.pl';
|
||||
$access{'perms'} == 1 || &error($text{'binlogs_ecannot'});
|
||||
&is_mysql_local() || &error($text{'binlogs_eremote'});
|
||||
&error_setup($text{'binlogs_err'});
|
||||
&ReadParse();
|
||||
|
||||
# Get the mysqld section
|
||||
foreach my $l (&get_all_mysqld_files()) {
|
||||
&lock_file($l);
|
||||
}
|
||||
$conf = &get_mysql_config();
|
||||
# Prefer the main server section over generic MariaDB sections that can
|
||||
# belong to a plugin-specific include file
|
||||
($mysqld) = grep { $_->{'name'} eq 'mysqld' } @$conf;
|
||||
($mysqld) = grep { $_->{'name'} eq 'mariadbd' } @$conf if (!$mysqld);
|
||||
($mysqld) = grep { $_->{'name'} eq 'mariadb' } @$conf if (!$mysqld);
|
||||
$mysqld || &error($text{'cnf_emysqld'});
|
||||
$mems = $mysqld->{'members'};
|
||||
|
||||
if ($in{'enabled'}) {
|
||||
# Work out the base directive, preserving an existing or active default
|
||||
# so that merely saving this page cannot start a new binary log chain.
|
||||
# On servers that log by default the directive is also left absent, as
|
||||
# writing one would change the default base name even when stopped
|
||||
my $lbdir = &find("log_bin", $mems) || &find("log-bin", $mems);
|
||||
my $baseargs;
|
||||
if ($in{'base_def'}) {
|
||||
if ($lbdir) {
|
||||
$baseargs = [ $lbdir->{'value'} // "" ];
|
||||
}
|
||||
else {
|
||||
my ($curfile) = &get_binary_log_status();
|
||||
$baseargs = $curfile || &get_binlog_default_on() ?
|
||||
[ ] : [ "mysql-bin" ];
|
||||
}
|
||||
}
|
||||
else {
|
||||
$in{'base'} =~ /^[a-zA-Z0-9\.\-\_\/]+$/ ||
|
||||
&error($text{'binlogs_ebase'});
|
||||
$baseargs = [ $in{'base'} ];
|
||||
}
|
||||
&save_directive($conf, $mysqld, "log-bin", [ ]);
|
||||
&save_directive($conf, $mysqld, "log_bin", $baseargs);
|
||||
|
||||
# Remove any directives that force binary logging off
|
||||
foreach my $s ("skip-log-bin", "skip_log_bin",
|
||||
"disable-log-bin", "disable_log_bin") {
|
||||
&save_directive($conf, $mysqld, $s, [ ]);
|
||||
}
|
||||
|
||||
# A unique server ID is required by MySQL for binary logging
|
||||
my $sid = &find_value("server_id", $mems);
|
||||
$sid = &find_value("server-id", $mems) if (!defined($sid));
|
||||
if ($in{'serverid_def'}) {
|
||||
if (!defined($sid)) {
|
||||
&save_directive($conf, $mysqld, "server_id", [ 1 ]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$in{'serverid'} =~ /^\d+$/ && $in{'serverid'} >= 1 &&
|
||||
$in{'serverid'} <= 4294967295 ||
|
||||
&error($text{'binlogs_eserverid'});
|
||||
&save_directive($conf, $mysqld, "server-id", [ ]);
|
||||
&save_directive($conf, $mysqld, "server_id",
|
||||
[ $in{'serverid'} ]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
# Remove the directive under both possible names, and force logging
|
||||
# off in case the server enables it by default (as MySQL 8+ does)
|
||||
&save_directive($conf, $mysqld, "log-bin", [ ]);
|
||||
&save_directive($conf, $mysqld, "log_bin", [ ]);
|
||||
foreach my $s ("skip_log_bin", "disable-log-bin",
|
||||
"disable_log_bin") {
|
||||
&save_directive($conf, $mysqld, $s, [ ]);
|
||||
}
|
||||
&save_directive($conf, $mysqld, "skip-log-bin", [ "" ]);
|
||||
}
|
||||
|
||||
# Save the binary log format
|
||||
if (defined($in{'format'})) {
|
||||
$in{'format'} =~ /^(|ROW|MIXED|STATEMENT)$/ ||
|
||||
&error($text{'binlogs_eformat'});
|
||||
&save_directive($conf, $mysqld, "binlog-format", [ ]);
|
||||
&save_directive($conf, $mysqld, "binlog_format",
|
||||
$in{'format'} ? [ $in{'format'} ] : [ ]);
|
||||
}
|
||||
|
||||
# Save the maximum size of one log file, which the server limits to the
|
||||
# range from 4 kB to 1 GB
|
||||
if ($in{'maxsize_def'}) {
|
||||
&save_directive($conf, $mysqld, "max-binlog-size", [ ]);
|
||||
&save_directive($conf, $mysqld, "max_binlog_size", [ ]);
|
||||
}
|
||||
else {
|
||||
$in{'maxsize'} =~ /^\d+$/ ||
|
||||
&error($text{'binlogs_emaxsize'});
|
||||
defined(&parse_binlog_max_size($in{'maxsize'},
|
||||
$in{'maxsize_units'})) ||
|
||||
&error($text{'binlogs_emaxsizerange'});
|
||||
&save_directive($conf, $mysqld, "max-binlog-size", [ ]);
|
||||
&save_directive($conf, $mysqld, "max_binlog_size",
|
||||
[ $in{'maxsize'}.$in{'maxsize_units'} ]);
|
||||
}
|
||||
|
||||
# Save the retention period, preferring the seconds-based variable on
|
||||
# servers that support it as it also allows fractional days
|
||||
my ($ver, $variant) = &get_mysql_variant_cached();
|
||||
my $newexpire = $variant eq "mysql" &&
|
||||
&compare_version_numbers($ver, "8.0") >= 0 ||
|
||||
$variant eq "mariadb" &&
|
||||
&compare_version_numbers($ver, "10.6") >= 0;
|
||||
if ($in{'expire_def'}) {
|
||||
&save_directive($conf, $mysqld, "binlog_expire_logs_seconds", [ ]);
|
||||
&save_directive($conf, $mysqld, "expire_logs_days", [ ]);
|
||||
}
|
||||
else {
|
||||
# Fractional days are allowed, and zero disables automatic deletion
|
||||
$in{'expire'} =~ /^\d+(\.\d+)?$/ ||
|
||||
&error($text{'binlogs_eexpire'});
|
||||
my $days = $in{'expire'} + 0;
|
||||
if ($newexpire) {
|
||||
# The seconds variable is limited to 32 bits
|
||||
my $secs = &parse_binlog_expire_days($days);
|
||||
$secs <= 4294967295 ||
|
||||
&error(&text('binlogs_eexpiremax',
|
||||
&format_binlog_expire_days(4294967295)));
|
||||
&save_directive($conf, $mysqld, "expire_logs_days", [ ]);
|
||||
&save_directive($conf, $mysqld, "binlog_expire_logs_seconds",
|
||||
[ $secs ]);
|
||||
}
|
||||
else {
|
||||
# The days variable is limited to 99 whole days on servers
|
||||
# without the seconds-based variable
|
||||
$days <= 99 ||
|
||||
&error(&text('binlogs_eexpiremax', 99));
|
||||
$days == int($days) ||
|
||||
&error($text{'binlogs_eexpireint'});
|
||||
&save_directive($conf, $mysqld, "binlog_expire_logs_seconds",
|
||||
[ ]);
|
||||
&save_directive($conf, $mysqld, "expire_logs_days",
|
||||
[ $days ]);
|
||||
}
|
||||
}
|
||||
|
||||
# Write out file, and restart if requested
|
||||
foreach my $l (&get_all_mysqld_files()) {
|
||||
&flush_file_lines($l, undef, 1);
|
||||
&unlock_file($l);
|
||||
}
|
||||
if ($in{'restart'} && &is_mysql_running() > 0) {
|
||||
&stop_mysql();
|
||||
$err = &start_mysql();
|
||||
&error($err) if ($err);
|
||||
}
|
||||
&webmin_log("binlogs");
|
||||
&redirect("edit_binlogs.cgi");
|
||||
@@ -11,9 +11,11 @@ foreach my $l (&get_all_mysqld_files()) {
|
||||
&lock_file($l);
|
||||
}
|
||||
$conf = &get_mysql_config();
|
||||
($mysqld) = grep { $_->{'name'} eq 'mysqld' ||
|
||||
$_->{'name'} eq 'mariadbd' ||
|
||||
$_->{'name'} eq 'mariadb' } @$conf;
|
||||
# Prefer the main server section over generic MariaDB sections that can
|
||||
# belong to a plugin-specific include file
|
||||
($mysqld) = grep { $_->{'name'} eq 'mysqld' } @$conf;
|
||||
($mysqld) = grep { $_->{'name'} eq 'mariadbd' } @$conf if (!$mysqld);
|
||||
($mysqld) = grep { $_->{'name'} eq 'mariadb' } @$conf if (!$mysqld);
|
||||
$mysqld || &error($text{'cnf_emysqld'});
|
||||
$mems = $mysqld->{'members'};
|
||||
|
||||
@@ -134,4 +136,3 @@ if ($in{'restart'} && &is_mysql_running() > 0) {
|
||||
}
|
||||
&webmin_log("cnf");
|
||||
&redirect("");
|
||||
|
||||
|
||||
141
t/mysql-lib.t
Normal file
141
t/mysql-lib.t
Normal file
@@ -0,0 +1,141 @@
|
||||
#!/usr/bin/perl
|
||||
# Unit tests for MySQL module binary logging helpers.
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
no warnings 'once';
|
||||
use Test::More;
|
||||
use File::Basename qw(dirname);
|
||||
use File::Spec;
|
||||
use Cwd qw(abs_path);
|
||||
|
||||
BEGIN {
|
||||
# mysql-lib.pl imports WebminCore and initializes module globals at
|
||||
# load time. Stub only those load-time dependencies so its pure helpers
|
||||
# can be tested without a Webmin installation.
|
||||
$INC{'WebminCore.pm'} = 1;
|
||||
$INC{'view-lib.pl'} = 1;
|
||||
package WebminCore;
|
||||
sub import
|
||||
{
|
||||
my $caller = caller;
|
||||
no strict 'refs';
|
||||
*{$caller.'::init_config'} = sub { };
|
||||
*{$caller.'::get_module_acl'} = sub { return (); };
|
||||
*{$caller.'::read_file_contents'} = sub { return "10.5.29-MariaDB\n"; };
|
||||
*{$caller.'::compare_version_numbers'} = sub {
|
||||
my ($left, $right) = @_;
|
||||
my @left = split(/\./, $left);
|
||||
my @right = split(/\./, $right);
|
||||
my $count = @left > @right ? scalar(@left) : scalar(@right);
|
||||
for(my $i = 0; $i < $count; $i++) {
|
||||
my $cmp = ($left[$i] || 0) <=> ($right[$i] || 0);
|
||||
return $cmp if ($cmp);
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
*{$caller.'::has_command'} = sub { return $_[0]; };
|
||||
*{$caller.'::quote_path'} = sub { return $_[0]; };
|
||||
*{$caller.'::backquote_command'} = sub { return ''; };
|
||||
}
|
||||
}
|
||||
|
||||
my $root = abs_path(File::Spec->catdir(dirname(__FILE__), '..'));
|
||||
chdir($root) or die "chdir($root): $!";
|
||||
do './mysql/mysql-lib.pl' or die $@ || $!;
|
||||
|
||||
subtest 'binary log retention display round-trips exactly' => sub {
|
||||
# The page shows seconds as days, and saving converts the shown days
|
||||
# back with int(days * 86400 + 0.5), which must reproduce the exact
|
||||
# stored seconds even for non-round values
|
||||
foreach my $secs (0, 1, 43200, 86400, 100000, 123456789, 2592000,
|
||||
4294967295) {
|
||||
my $days = main::format_binlog_expire_days($secs);
|
||||
like($days, qr/^\d+(\.\d+)?$/,
|
||||
"$secs seconds display as plain decimal ($days)");
|
||||
is(int($days * 86400 + 0.5), $secs,
|
||||
"$secs seconds round-trip through $days days");
|
||||
}
|
||||
is(main::format_binlog_expire_days(43200), '0.5',
|
||||
'half a day is shown without trailing zeros');
|
||||
is(main::format_binlog_expire_days(2592000), '30',
|
||||
'whole days are shown without a decimal point');
|
||||
};
|
||||
|
||||
subtest 'default logging state considers variant, version and downtime' => sub {
|
||||
no warnings 'redefine';
|
||||
my $live;
|
||||
local *main::get_remote_mysql_variant = sub {
|
||||
return $live ? @$live : (-1);
|
||||
};
|
||||
|
||||
$live = [ '8.0.36', 'mysql' ];
|
||||
ok(main::get_binlog_default_on(),
|
||||
'a running MySQL 8 logs by default');
|
||||
$live = [ '5.7.44', 'mysql' ];
|
||||
ok(!main::get_binlog_default_on(),
|
||||
'a running MySQL 5.7 does not log by default');
|
||||
$live = [ '10.11.6', 'mariadb' ];
|
||||
ok(!main::get_binlog_default_on(),
|
||||
'a running MariaDB does not log by default');
|
||||
|
||||
# When the server is stopped, the version cached at module setup
|
||||
# time must be used instead
|
||||
$live = undef;
|
||||
local $main::mysql_version = '8.0.36';
|
||||
ok(main::get_binlog_default_on(),
|
||||
'a stopped MySQL 8 still counts as logging by default');
|
||||
is_deeply([ main::get_mysql_variant_cached() ],
|
||||
[ '8.0.36', 'mysql' ],
|
||||
'a stopped MySQL 8 falls back to the cached version');
|
||||
local $main::mysql_version = '10.5.29-MariaDB';
|
||||
ok(!main::get_binlog_default_on(),
|
||||
'a stopped MariaDB does not count as logging by default');
|
||||
is_deeply([ main::get_mysql_variant_cached() ],
|
||||
[ '10.5.29', 'mariadb' ],
|
||||
'a stopped MariaDB falls back to the cached version');
|
||||
};
|
||||
|
||||
subtest 'maximum log file size units and range' => sub {
|
||||
is(main::parse_binlog_max_size(100, 'M'), 104857600,
|
||||
'100 MB converts to bytes');
|
||||
is(main::parse_binlog_max_size(4, 'K'), 4096,
|
||||
'the 4 kB minimum is accepted');
|
||||
is(main::parse_binlog_max_size(1, 'G'), 1073741824,
|
||||
'the 1 GB maximum is accepted');
|
||||
is(main::parse_binlog_max_size(8192, ''), 8192,
|
||||
'plain bytes are accepted');
|
||||
ok(!defined(main::parse_binlog_max_size(1, 'K')),
|
||||
'below the 4 kB minimum is rejected');
|
||||
ok(!defined(main::parse_binlog_max_size(2, 'G')),
|
||||
'above the 1 GB maximum is rejected');
|
||||
ok(!defined(main::parse_binlog_max_size('abc', 'M')),
|
||||
'a non-numeric size is rejected');
|
||||
ok(!defined(main::parse_binlog_max_size(100, 'T')),
|
||||
'an unknown unit is rejected');
|
||||
};
|
||||
|
||||
subtest 'rotation issues the expected statement' => sub {
|
||||
no warnings 'redefine';
|
||||
my @sql;
|
||||
local *main::execute_sql_logged = sub { push(@sql, [ @_ ]); };
|
||||
main::rotate_binary_logs();
|
||||
is_deeply(\@sql, [ [ 'mysql', 'flush binary logs' ] ],
|
||||
'rotation runs FLUSH BINARY LOGS on the master database');
|
||||
};
|
||||
|
||||
subtest 'binary log retention limit covers the full 32-bit range' => sub {
|
||||
# Saving validates the converted seconds against the variable's
|
||||
# 32-bit maximum, so the displayed maximum must be accepted while
|
||||
# anything above it is rejected
|
||||
my $maxdays = main::format_binlog_expire_days(4294967295);
|
||||
is($maxdays, '49710.269618', 'the 32-bit maximum displays in days');
|
||||
is(main::parse_binlog_expire_days($maxdays), 4294967295,
|
||||
'the displayed maximum converts back within the limit');
|
||||
cmp_ok(main::parse_binlog_expire_days('49710.27'), '>', 4294967295,
|
||||
'days just past the maximum exceed the seconds limit');
|
||||
cmp_ok(main::parse_binlog_expire_days(49710), '<=', 4294967295,
|
||||
'whole days below the maximum stay within the limit');
|
||||
};
|
||||
|
||||
done_testing();
|
||||
Reference in New Issue
Block a user