332 lines
10 KiB
PHP
332 lines
10 KiB
PHP
# ==== Purpose ====
|
|
#
|
|
# Assert that the binary log contains a specific sequence of event types.
|
|
#
|
|
# ==== Usage ====
|
|
#
|
|
# --let $event_sequence= SEQUENCE_OF_EVENTS
|
|
# [--let $event_separator= CHAR]
|
|
# [--let $invert= 1]
|
|
# [--let $limit= [OFFSET,] ROW_COUNT]
|
|
# [--let $binlog_file= FILE]
|
|
# [--let $binlog_position= POSITION]
|
|
# [--let $relay_log= 1]
|
|
# [--let $include_header_events= 1]
|
|
# [--let $wait_for_binlog_events= 1]
|
|
# [--let $slave_timeout= 1]
|
|
# [--let $rpl_debug= 1]
|
|
# [--let $keep_temp_files= 1]
|
|
# [--let $dont_print_pattern= 1]
|
|
# --source include/assert_binlog_events.inc
|
|
#
|
|
# Parameters:
|
|
#
|
|
# $event_sequence
|
|
# This is a (perl) regular expression that will be matched with a
|
|
# sequence of event specifications. Here is an example:
|
|
#
|
|
# Query/BEGIN # Query/INSERT.* # Query # (Query/COMMIT|Xid)
|
|
#
|
|
# Match a sequence of a BEGIN event, followed by an
|
|
# Query_log_event where the query begins with INSERT, followed
|
|
# by any query, followed by either a Query_log_event with the
|
|
# query equal to COMMIT, or a Xid_log_event.
|
|
#
|
|
# A single event is specified using either just the 'Type' field
|
|
# of SHOW BINLOG EVENTS, or using both the 'Type' and the 'Info'
|
|
# fields:
|
|
#
|
|
# Query
|
|
# Match any query_log_event
|
|
# Query/BEGIN
|
|
# Match any query_log_event where the query is equal to BEGIN.
|
|
# Query/INSERT.*
|
|
# Match any query_log_event where the query begins with INSERT.
|
|
#
|
|
# A sequence of events is specified by separating multiple event
|
|
# specifications with a # character:
|
|
#
|
|
# Query/BEGIN # Query/INSERT.* # Query/COMMIT
|
|
#
|
|
# Regular expressions can span multiple events:
|
|
#
|
|
# Query/BEGIN # (Query/INSERT.*){1,2} # Query/COMMIT
|
|
# Allow either one or two INSERT statements
|
|
#
|
|
# The '.' wildcard does *not* match the special characters # or /.
|
|
# So the following is safe in the sense that the .* cannot
|
|
# 'swallow' multiple events:
|
|
#
|
|
# Query/INSERT.*
|
|
#
|
|
# The pattern must match from the beginning until the end. To
|
|
# allow arbitrary events after the pattern, append '(#.*)*' to the
|
|
# pattern.
|
|
#
|
|
# The following shortcuts are available as syntactic sugar:
|
|
#
|
|
# !Q(STATEMENT) -> Query/(use .*; )?STATEMENT
|
|
# !Begin -> !Q(BEGIN)
|
|
# !Commit -> (!Q(COMMIT) | Xid)
|
|
# !Insert -> (!Q(INSERT.*) | Table_map # Write_rows)
|
|
# !Update -> (!Q(UPDATE.*) | Table_map # Update_rows)
|
|
# !Delete -> (!Q(DELETE.*) | Table_map # Delete_rows)
|
|
# !Single_DML -> (!Insert | !Update | !Delete)
|
|
# !Multi_DML -> multiple DML statements, each possibly touching multiple tables
|
|
# !DML_transaction -> Begin # Multi_DML # Commit
|
|
# !DDL -> !Q(neither BEGIN or COMMIT)
|
|
# !Gtid_transaction -> Gtid # (DML_transaction | DDL)
|
|
# !Empty_gtid_transaction -> Gtid # Begin # Commit
|
|
# !Gtid_or_anon -> (Gtid | Anonymous_Gtid)
|
|
#
|
|
# Whitespace around / and # is ignored.
|
|
#
|
|
# $event_separator
|
|
# Use $event_separator instead of # to delimit events. This must
|
|
# only be one character long and should not be used elsewhere in
|
|
# the pattern.
|
|
#
|
|
# $invert
|
|
# By default, this script asserts that SHOW BINLOG EVENTS matches
|
|
# the specification. If $invert is set, this scripts instead asserts
|
|
# that SHOW BINLOG EVENTS does *not* match the specification.
|
|
#
|
|
# $limit
|
|
# Pass this as the LIMIT clause of SHOW BINLOG EVENTS.
|
|
#
|
|
# $binlog_position
|
|
# Pass this as the FROM clause of SHOW BINLOG EVENTS.
|
|
# Note that you can use include/save_binlog_position.inc to read
|
|
# both $binlog_file and $binlog_position.
|
|
#
|
|
# $binlog_file
|
|
# Pass this as the IN clause of SHOW BINLOG EVENTS.
|
|
# Note that you can use include/save_binlog_position.inc to read
|
|
# both $binlog_file and $binlog_position.
|
|
#
|
|
# $relay_log
|
|
# Use SHOW RELAYLOG EVENTS instead of SHOW BINLOG EVENTS
|
|
#
|
|
# $include_header_events
|
|
# By default, Format_desc, Rotate, and Previous_gtids events are
|
|
# ignore. If this parameter is enabled, the events are included
|
|
# in the match.
|
|
#
|
|
# $wait_for_binlog_events
|
|
# If this is true, the script retries until the binlog contains
|
|
# the expected events. The timeout is given by $slave_timeout.
|
|
#
|
|
# $slave_timeout
|
|
# Default timeout used if $wait_for_binlog_events is enabled.
|
|
# The default timeout is 300 seconds. You can change the timeout by
|
|
# setting $slave_timeout. The unit for time is seconds.
|
|
#
|
|
# $rpl_debug
|
|
# Print lots of debug info.
|
|
#
|
|
# $keep_temp_files
|
|
# Keep the two temporary files that this script uses. This is for
|
|
# debugging only and should not be used in a checked-in version of
|
|
# a test.
|
|
#
|
|
# $dont_print_pattern
|
|
# By default, the pattern is printed to the result log. If this
|
|
# variable is set, the pattern is not printed.
|
|
|
|
if ($dont_print_pattern)
|
|
{
|
|
--let $include_filename= assert_binlog_events.inc
|
|
}
|
|
if (!$dont_print_pattern)
|
|
{
|
|
--let $include_filename= assert_binlog_events.inc [$event_sequence]
|
|
}
|
|
--source include/begin_include_file.inc
|
|
|
|
if (!$event_sequence)
|
|
{
|
|
--die ERROR IN TEST: specify $event_sequence before sourcing assert_binlog_events.inc. To assert that nothing was generated, set $event_sequence= ()
|
|
}
|
|
|
|
# Execute statement, write result to file.
|
|
if (!$relay_log)
|
|
{
|
|
--let $statement= SHOW BINLOG EVENTS
|
|
}
|
|
if ($relay_log)
|
|
{
|
|
--let $statement= SHOW RELAYLOG EVENTS
|
|
}
|
|
if ($binlog_file)
|
|
{
|
|
--let $statement= $statement IN '$binlog_file'
|
|
}
|
|
if ($binlog_position)
|
|
{
|
|
--let $statement= $statement FROM $binlog_position
|
|
}
|
|
if ($limit != "")
|
|
{
|
|
--let $statement= $statement LIMIT $limit
|
|
}
|
|
|
|
if ($wait_for_binlog_events)
|
|
{
|
|
--let $_abe_counter= 0
|
|
--let $_abe_timeout= $slave_timeout
|
|
# Wait 300 seconds for binlog events
|
|
if (!$_abe_timeout)
|
|
{
|
|
--let $_abe_timeout= 300
|
|
}
|
|
}
|
|
|
|
--let $_abe_verdict=
|
|
while ($_abe_verdict != 'ok')
|
|
{
|
|
--let $output_file= GENERATE
|
|
--source include/write_result_to_file.inc
|
|
|
|
if ($rpl_debug)
|
|
{
|
|
--echo Wrote output to $output_file
|
|
}
|
|
|
|
# Set environment variables used in perl.
|
|
--let _ABE_FILE= $output_file
|
|
--let _ABE_EVENT_SEQUENCE= $event_sequence
|
|
--let _ABE_INVERT= $invert
|
|
--let _ABE_DEBUG= $rpl_debug
|
|
--let _ABE_EVENT_SEPARATOR= $event_separator
|
|
--let _ABE_INCLUDE_HEADER_EVENTS= $include_header_events
|
|
|
|
############################################################################
|
|
perl;
|
|
my $event_sequence = $ENV{'_ABE_EVENT_SEQUENCE'};
|
|
my $file = $ENV{'_ABE_FILE'};
|
|
my $invert = $ENV{'_ABE_INVERT'};
|
|
my $debug = $ENV{'_ABE_DEBUG'};
|
|
my $include_header_events = $ENV{'_ABE_INCLUDE_HEADER_EVENTS'};
|
|
my $event_separator= $ENV{'_ABE_EVENT_SEPARATOR'};
|
|
if ($event_separator == '')
|
|
{
|
|
$event_separator = '#';
|
|
}
|
|
|
|
$event_sequence =~ s/$event_separator/\n/g;
|
|
|
|
# Ignore whitespace at beginning, end, and around separators.
|
|
$event_sequence =~ s{^\s*}{};
|
|
$event_sequence =~ s{\s*$}{};
|
|
$event_sequence =~ s{\s*\n\s*}{\n}g;
|
|
$event_sequence =~ s{\s*/\s*}{/}g;
|
|
|
|
# Expand syntactic sugar definitions.
|
|
$event_sequence =~ s{!Gtid_transaction}{Gtid\n(!DDL|!DML_transaction)}g;
|
|
$event_sequence =~ s{!Gtid_or_anon}{(Gtid|Anonymous_Gtid)}g;
|
|
$event_sequence =~ s{!Empty_gtid_transaction}{Gtid\n!Begin\n!Commit}g;
|
|
$event_sequence =~ s{!DML_transaction}{!Begin\n!Multi_DML\n!Commit}g;
|
|
$event_sequence =~ s{!DDL}{Query/(?!BEGIN|COMMIT).*}g;
|
|
$event_sequence =~ s{!Single_DML}{(?:Query|Table_map\n(?:Write|Update|Delete)_rows)}g;
|
|
$event_sequence =~ s{!Multi_DML}{(?:Query|(?:Table_map\n)+(?:(?:Write|Update|Delete)_rows))(?:\nTable_map|\n(?:Write|Update|Delete)_rows|\nQuery)*}g;
|
|
$event_sequence =~ s{!Begin}{Query/BEGIN}g;
|
|
$event_sequence =~ s{!Commit}{(?:Query/COMMIT|Xid/COMMIT.*)}g;
|
|
$event_sequence =~ s{!Insert}{(?:!Q(INSERT.*)|Table_map\nWrite_rows)}g;
|
|
$event_sequence =~ s{!Update}{(?:!Q(UPDATE.*)|Table_map\nUpdate_rows)}g;
|
|
$event_sequence =~ s{!Delete}{(?:!Q(DELETE.*)|Table_map\nDelete_rows)}g;
|
|
$event_sequence =~ s{!Q\(([^\n]+)\)}{Query/(?:use.*; )?$1}g;
|
|
|
|
# Allow matching 'Type' without 'Info'
|
|
# (e.g., 'Query' instead of 'Query/xyz').
|
|
$event_sequence =~ s{\n}{(?:/[^\n]*)?\n}g;
|
|
|
|
# Allow 'Type' without 'Info' at the end. Require match until the
|
|
# end. Allow missing \n at the end.
|
|
$event_sequence .= "(?:/[^\n]*)?\n?" . '$';
|
|
|
|
# Require match from the beginning.
|
|
$event_sequence = '^' . $event_sequence;
|
|
|
|
if ($debug)
|
|
{
|
|
print "Regex: $event_sequence\n";
|
|
}
|
|
|
|
# Read and filter file.
|
|
my $result= '';
|
|
open FILE, "< $file" or die "Error $? opening $file: $!";
|
|
my $line_number= 1;
|
|
while (<FILE>)
|
|
{
|
|
if ($line_number > 1)
|
|
{
|
|
# Six tab-separated fields; pick number 3 and number 6.
|
|
s{^[^\t]+\t[^\t]+\t([^\t]+)\t[^\t]+\t[^\t]+\t([^\t]*)$}{$1/$2}
|
|
or die "Unexpected line format in output line $line_number: $_";
|
|
if ($include_header_events or
|
|
($1 ne 'Format_desc' && $1 ne 'Rotate' && $1 ne 'Previous_gtids'))
|
|
{
|
|
chomp;
|
|
$result .= $_;
|
|
$result .= "\n";
|
|
}
|
|
}
|
|
$line_number++;
|
|
}
|
|
close FILE or die "Error $? closing $file: $!";
|
|
|
|
if ($debug)
|
|
{
|
|
print "Formatted output: $result\n";
|
|
}
|
|
|
|
# Check if there is a match
|
|
my $matches = eval("\$result =~ m{$event_sequence}");
|
|
my $is_ok = ($matches == !$invert);
|
|
|
|
# Write 'ok', or write some debug info.
|
|
open FILE, "> $file.verdict" or die "Error $? opening $file.verdict: $!";
|
|
print FILE ($is_ok ? 'ok' :
|
|
"Regex:\n$event_sequence\nFile contents:\n$result")
|
|
or die "Error $? writing to $file.verdict: $!";
|
|
close FILE or die "Error $? writing to $file.verdict: $!";
|
|
EOF
|
|
############################################################################
|
|
|
|
--let $_abe_verdict= `SELECT LOAD_FILE('$output_file.verdict')`
|
|
if ($_abe_verdict != 'ok')
|
|
{
|
|
--let $_abe_fail= 1
|
|
if ($wait_for_binlog_events)
|
|
{
|
|
--sleep 1
|
|
--inc $_abe_counter
|
|
if ($_abe_counter < $_abe_timeout)
|
|
{
|
|
--let $_abe_fail= 0
|
|
}
|
|
}
|
|
if ($_abe_fail)
|
|
{
|
|
--source include/show_rpl_debug_info.inc
|
|
--echo event_sequence=$event_sequence
|
|
--echo $_abe_verdict
|
|
--echo statement=$statement
|
|
--echo invert=$invert
|
|
--echo include_header_events=$include_header_events
|
|
--echo event_separator=$_ABE_EVENT_SEPARATOR
|
|
--die Binlog contents did not match expected pattern.
|
|
}
|
|
}
|
|
|
|
if (!$keep_temp_files)
|
|
{
|
|
--remove_file $output_file
|
|
--remove_file $output_file.verdict
|
|
}
|
|
--let $output_file=
|
|
}
|
|
|
|
--let $include_filename= assert_binlog_events.inc
|
|
--source include/end_include_file.inc
|