1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
|
# ==== Purpose ====
#
# Assert that the error log contains a given set of messages, and
# nothing else. The messages are given as a sequence of regular
# expressions. As a special case, if the list is set to NONE, the
# script asserts that no error was generated. The script inspects
# only the tail of the error log generated from the last invocation of
# include/save_error_log_position.inc, or from the last invocation of
# this script; whatever happened most recently.
#
# ==== Suppressed messages are excluded from the check ====
#
# This script does not inspect messages that have been suppressed,
# either globally or by the test, i.e.:
#
# - Globally suppressed messages are listed in the
# mtr.global_suppressions table, which is created and populated in
# mtr_warnings.sql.
#
# - Per-test suppressed messages are listed in the
# mtr.test_suppressions table, which is created in mtr_warnings.sql
# and populated when the test either invokes mtr.add_suppression or
# sources include/suppress_messages.inc.
#
# If you need to use this script with a message that has been
# suppressed globally, you need to ignore the global suppression: set
# $suppress_mode=IGNORE_GLOBAL_SUPPRESSION and source
# include/suppress_message.inc (see suppress_message.inc for details).
#
# ==== This script adds suppressions ====
#
# When this script finds an expected message in the log, it
# automatically adds a suppression for the message. This prevents
# failure of the the check-testcase script that mtr runs after the
# test has finished. In case the same message appears several times
# in the log, only the instances that were found by this script are
# suppressed, and others may be reported by the check-testcase script.
# (The suppression uses the full error log line, which includes a
# unique timestamp.)
#
# ==== Usage ====
#
# --source include/save_error_log_position.inc
# ... generate some errors...
# let $error_pattern = {
# ERROR_1_REGEX
# ERROR_2_REGEX
# ...
# | SHOW | NONE | SAVEPOS | DEBUG_STATUS };
# [--let $assert_dont_fail = 1]
# [--let $keep_old_error_log_position = 1]
# [--let $error_log_server_number = NUMBER]
# --source include/assert_error_log.inc
#
# Parameters:
#
# $error_pattern
#
# Normally this is a string containing a regular expression,
# matching the expected error.
#
# This can be a multi-line string: the first line matches the
# first error, the second line matches the second error, and so
# on.
#
# It can also be NONE, indicating that the expectation is to
# have no error in the log.
#
# The special value SHOW is used by show_error_log.inc. It should not
# normally be used in any other context.
#
# The special value SAVEPOS is used by
# save_error_log_position.inc It should not normally be used in
# any other context.
#
# The special value DEBUG_STATUS can be used to show the internal
# state of the error message assertion framework.
#
# $keep_old_error_log_position
#
# Normally, this script saves the current error log position after
# reading the error log, so that the next invocation of
# assert_error_log.inc starts after the current position.
#
# To keep the old position, set this variable to 1.
#
# $assert_dont_fail
#
# If set to 1, failure to match the regular expression does not
# make the test fail immediately. The output will fail with result
# mismatch but the test can complete. This can be used during
# test development and debugging, but must not be used in tests
# that are pushed to a main branch.
#
# $error_log_server_number
#
# By default, this script operates on the error log for the server
# of the current connection. To use this script from a connection
# on a different server, set this to the number of the server
# whose log should be inspected.
#
# $ignore_global_suppressions
#
# By default, this script takes into account both the global
# suppressions defined in include/mtr_warnings.sql, and any
# per-test suppressions that the test may have added through
# mtr.add_suppression or include/suppress_message.inc. This
# option removes the check for global suppressions, allowing you
# to check for those errors. Notes:
# - Enabling this parameter may remove too many suppressions. If
# that is the case, you can ignore individual global
# suppressions using include/suppress_messages.inc with
# $suppress_mode=IGNORE_GLOBAL_SUPPRESSIONS.
# - Enabling this parameter will not ignore per-suppressions added
# by mtr.add_suppression or include/suppress_message.inc. If
# you need that, just remove the per-test suppression from the
# test.
# Get server number
--let $_ael_server_number = $error_log_server_number
if (!$_ael_server_number) {
--let $_ael_server_number = `SELECT @@global.server_id`
}
--let $include_filename = assert_error_log.inc [server: $_ael_server_number, pattern: $error_pattern]
--source include/begin_include_file.inc
--source include/disable_binlog.inc
if (!$rpl_debug) {
--disable_query_log
}
if ($error_pattern != DEBUG_STATUS) {
# Get error log filename
--let $_ael_error_log_filename_save = $error_log_filename
if (!$error_log_filename) {
if ($error_log_server_number) {
--let $error_log_filename = $MYSQLTEST_VARDIR/log/mysqld.$_ael_server_number.err
}
if (!$error_log_server_number) {
--let $error_log_filename = `SELECT IF(@@global.log_error = 'stderr', CONCAT('$MYSQLTEST_VARDIR/log/mysqld.', $_ael_server_number, '.err'), @@global.log_error)`
}
}
# Get line count to skip
if (!$_ael_positions) {
--let $_ael_positions = {"1":-1,"2":-1,"3":-1,"4":-1,"5":-1,"6":-1,"7":-1,"8":-1,"9":-1,"10":-1}
}
--let $json_object = $_ael_positions
--let $json_key = $_ael_server_number
--source include/json_lookup.inc
--let $ignore_line_count = $json_value
if ($ignore_line_count == -1) {
if ($error_pattern != SAVEPOS) {
if ($error_pattern != SHOW) {
--die !!!ERROR IN TEST: use save_error_log_position.inc before the first call to assert_error_log.inc. The error log may contain errors from earlier test runs.
}
}
--let $ignore_line_count = 0
}
# Load error log from file into temporary table
--source include/load_error_log.inc
if ($rpl_debug) {
SELECT * FROM mtr.error_log;
}
# Compute number of errors.
--let $_ael_error_count = `SELECT COUNT(*) FROM mtr.error_log`
if ($rpl_debug) {
--echo # ignored $ignore_line_count lines from the error log
--echo # found $_ael_error_count new lines after that position
}
# Update position
--expr $_ael_new_offset = $ignore_line_count + $_ael_error_count
if (!$keep_old_error_log_position) {
--let $_ael_positions = `SELECT JSON_SET('$json_object', '$."$_ael_server_number"', $_ael_new_offset)`
if ($rpl_debug) {
--echo _ael_positions=<$_ael_positions>
}
}
if ($error_pattern != SAVEPOS) {
# For check-warnings, mtr pre-filters the log, including only Note and
# Warning (and some other patterns related to Valgrind and stack traces,
# but those are not relevant in this case).
UPDATE mtr.error_log SET suspicious = 0 WHERE line NOT REGEXP '\\[(Error|Warning)\\]';
if (!$ignore_global_suppressions) {
CALL mtr.filter_global_suppressed_warnings();
}
CALL mtr.filter_test_suppressed_warnings();
# Move filtered-in rows to a a new table in order to recompute the
# autoincrement column so that it becomes consecutive and can be
# joined with the pattern table.
CREATE TEMPORARY TABLE mtr.filtered_error_log (id INT PRIMARY KEY AUTO_INCREMENT, message TEXT);
INSERT INTO mtr.filtered_error_log(message) SELECT line FROM mtr.error_log WHERE suspicious = 1 ORDER BY id;
if ($rpl_debug) {
SELECT * FROM mtr.filtered_error_log;
}
--let $_ael_included_error_count = `SELECT COUNT(*) FROM mtr.filtered_error_log`
if ($error_pattern != SHOW) {
# Load expected error patterns from $error_pattern into temporary table.
CREATE TEMPORARY TABLE mtr.error_patterns (id INT PRIMARY KEY AUTO_INCREMENT, pattern TEXT);
if ($error_pattern != NONE) {
# tmp_error_patterns contains all rows of $error_pattern, including comments and empty lines.
CREATE TEMPORARY TABLE mtr.tmp_error_patterns (id INT PRIMARY KEY AUTO_INCREMENT, pattern TEXT);
# Convert a multi-line string into a multi-row INSERT, by replacing
# newlines by:
# '),('
# Apply QUOTE before the newline-to-multirow conversion, so that
# quotes within each message remain escaped but quotes that surround
# each row remain unescaped.
--let $_ael_pattern_escaped = escape(\',$error_pattern)
--let $_ael_pattern_list = `SELECT REPLACE(QUOTE('$_ael_pattern_escaped'), '\n', "'),('")`
eval INSERT INTO mtr.tmp_error_patterns(pattern) VALUES($_ael_pattern_list);
# Filter out empty lines and comments
INSERT INTO mtr.error_patterns(pattern) SELECT pattern FROM mtr.tmp_error_patterns WHERE pattern NOT REGEXP '^ *(#.*)?$' ORDER BY id;
DROP TEMPORARY TABLE mtr.tmp_error_patterns;
}
--let $_ael_pattern_count = `SELECT COUNT(*) FROM mtr.error_patterns`
# Check if the number of errors is as expected
if ($_ael_included_error_count != $_ael_pattern_count) {
--expr $_ael_excluded_error_count = $_ael_error_count - $_ael_included_error_count
--echo Expected $_ael_pattern_count errors in the error log, but got $_ael_included_error_count.
--echo We excluded the first $ignore_line_count lines. From the remaining $_ael_error_count lines (up to line $_ael_new_offset), we excluded $_ael_excluded_error_count lines that matched a suppression pattern.
--let $_ael_failure = 1
}
# Check if the error texts match the patterns
if ($_ael_included_error_count == $_ael_pattern_count) {
--let $_ael_find_mismatch = FROM mtr.filtered_error_log e, mtr.error_patterns p WHERE e.id = p.id AND NOT e.message REGEXP p.pattern
--let $_ael_mismatch_count = `SELECT COUNT(*) $_ael_find_mismatch`
if ($_ael_mismatch_count) {
--echo There were $_ael_mismatch_count error log lines which did not match the expected pattern:
--let $_ael_offset = 0
while ($_ael_offset < $_ael_mismatch_count) {
--let $_ael_error = `SELECT e.message $_ael_find_mismatch LIMIT $_ael_offset, 1`
--let $_ael_pattern = `SELECT p.pattern $_ael_find_mismatch LIMIT $_ael_offset, 1`
--inc $_ael_offset
--echo Mismatch $_ael_offset: error: <$_ael_error>
--echo Mismatch $_ael_offset: pattern: <$_ael_pattern>
}
--let $_ael_failure = 1
}
}
# Report error
if ($_ael_failure) {
--echo Error patterns:
SELECT * FROM mtr.error_patterns;
--echo Errors found in log on server $_ael_server_number:
SELECT * FROM mtr.filtered_error_log;
if (!$assert_dont_fail) {
--die Errors did not match the expected list of patterns.
}
}
# Note: suppression of asserted messages is based on string equality,
# not regex search, in order to catch cases where the error is generated
# without being checked.
--disable_warnings
INSERT IGNORE INTO mtr.asserted_test_suppressions SELECT message FROM mtr.filtered_error_log;
if ($rpl_debug) {
SELECT * FROM mtr.asserted_test_suppressions;
}
--enable_warnings
DROP TEMPORARY TABLE mtr.error_patterns;
}
if ($error_pattern == SHOW) {
if (!$assert_error_log_printed_do_not_check_in_this_line) {
--let $assert_error_log_printed_do_not_check_in_this_line = 1
--echo DO_NOT_CHECK_IN_THIS_LINE: include/show_error_log.inc should only be used for debugging. Never check in a test that calls it on success.
}
--echo # There were $_ael_included_error_count new errors on server $_ael_server_number:
if ($_ael_included_error_count) {
SELECT * FROM mtr.filtered_error_log;
}
}
DROP TEMPORARY TABLE mtr.filtered_error_log;
}
DROP TEMPORARY TABLE mtr.error_log;
}
if ($error_pattern == DEBUG_STATUS) {
--enable_query_log
--echo # Static suppression patterns defined in mtr_warnings.sql
SELECT * FROM mtr.global_suppressions;
--echo
--echo # Suppression patterns added by the test case, using
--echo # mtr.add_suppression or include/suppress_messages.inc.
SELECT * FROM mtr.test_suppressions;
--echo
--echo # Messages that were matched by an expected pattern in a previous
--echo # invocation of assert_error_log.inc. These are excluded from the
--echo # check that runs after the test has finished.
SELECT * FROM mtr.asserted_test_suppressions;
--echo
--echo # Patterns added by the test case to make exceptions from
--echo # mtr.global_suppressions, using include/suppress_messages.inc with
--echo # suppress_mode=IGNORE_GLOBAL_SUPPRESSIONS. This may be added
--echo # by tests that need to assert the existence of a message that is
--echo # included in mtr.global_suppressions.
SELECT * FROM mtr.test_ignored_global_suppressions;
}
--let $error_log_filename = $_ael_error_log_filename_save
--source include/restore_binlog.inc
--let $include_filename = assert_error_log.inc
--source include/end_include_file.inc
|