1
0
mirror of https://github.com/MariaDB/server.git synced 2026-10-23 18:49:29 +03:00

PQ: add mtr --ignore-parallel-diff

Parallel query execution appends _parallel to the 'type' column of
tabular EXPLAIN/ANALYZE, so an existing test whose plan gets
parallelized fails on that one column alone:

  -17    DERIVED r1      ALL             NULL NULL NULL NULL 2
  +17    DERIVED r1      ALL_parallel    NULL NULL NULL NULL 2

Running the whole suite with --parallel-worker-threads > 0 then buries
the failures that are worth looking at.

Add --ignore-parallel-diff, which makes the result comparison ignore
that suffix: once the two files are known to differ, strip the
suffix from both and compare again.

The text has to be exactly one of ALL, range or index followed
by _parallel, and it has to fill the span between two field
delimiters ('\t', '\n', or end of file).
FORMAT=JSON does not match this criteria, so is not affected.
This commit is contained in:
Oleg Smirnov
2026-09-03 22:31:17 +07:00
committed by Rex Johnston
parent df939fc32a
commit bbdeefd981
2 changed files with 128 additions and 0 deletions

View File

@@ -207,6 +207,9 @@ static char TMPDIR[FN_REFLEN];
static char global_subst_from[200];
static char global_subst_to[200];
static char *global_subst= NULL;
static my_bool opt_ignore_parallel_diff= 0;
/* Set once --ignore-parallel-diff has actually had to rewrite something. */
static my_bool ignored_parallel_diff= 0;
static char *read_command_buf= NULL;
static MEM_ROOT require_file_root;
static const my_bool my_true= 1;
@@ -2491,6 +2494,84 @@ enum compare_files_result_enum {
RESULT_LENGTH_MISMATCH= 2
};
/*
The access types the parallel suffix can be attached to. The server appends
"_parallel" to join_type_str[type] for a table the workers read, see
Explain_table_access::print_explain().
Currently, only 3 types of access are supported but when others get
parallelized, they should be added here too
*/
static const char *parallel_access_types[]= { "ALL", "range", "index", NULL };
/*
Remove the "_parallel" suffix from tabular EXPLAIN/ANALYZE 'type' values, so
that a result recorded without parallel query execution compares equal to one
produced with it. For --ignore-parallel-diff.
Only a whole field is rewritten: the text has to be exactly one of
parallel_access_types[] followed by the suffix, filling the span between two
field delimiters. A row is written as values separated by '\t' and terminated
by '\n' (with --vertical_results, as "name\tvalue\n"), so those two bound the
field. That is also what keeps FORMAT=JSON out of it: there the value is
quoted, so the suffix is followed by '"' and never by a delimiter.
ds is the raw content of a file and is not NUL terminated, so every read here
is bounded by ds->length.
@return how many suffixes were removed.
*/
static size_t strip_parallel_suffix(DYNAMIC_STRING *ds)
{
static const char suffix[]= "_parallel";
const size_t suffix_len= sizeof(suffix) - 1;
char *str= ds->str;
size_t pos= 0, removed= 0;
while (ds->length >= suffix_len && pos <= ds->length - suffix_len)
{
if (memcmp(str + pos, suffix, suffix_len))
{
pos++;
continue;
}
/* The suffix has to end the field: a delimiter, or the end of the file. */
const size_t end= pos + suffix_len;
if (end < ds->length && str[end] != '\t' && str[end] != '\n')
{
pos++;
continue;
}
/* Back to the start of the field. */
size_t start= pos;
while (start > 0 && str[start - 1] != '\t' && str[start - 1] != '\n')
start--;
/* What the field holds before the suffix has to be an access type, alone. */
const size_t name_len= pos - start;
uint i;
for (i= 0; parallel_access_types[i]; i++)
{
const char *name= parallel_access_types[i];
if (strlen(name) == name_len && !memcmp(str + start, name, name_len))
break;
}
if (!parallel_access_types[i])
{
pos++;
continue;
}
/* Drop the suffix. pos then holds the delimiter, nothing to rescan. */
memmove(str + pos, str + end, ds->length - end);
ds->length-= suffix_len;
removed++;
}
return removed;
}
/*
Compare two files, given a fd to the first file and
name of the second file
@@ -2535,6 +2616,21 @@ int compare_files2(File fd1, const char* filename2)
if (my_read(fd2, (uchar*) fd2_result.str, fd2_length, MYF(MY_WME | MY_NABP)))
die("Error when reading data from result file");
if (opt_ignore_parallel_diff &&
(fd1_result.length != fd2_result.length ||
memcmp(fd1_result.str, fd2_result.str, fd1_result.length)))
{
/*
Both sides, so that it does not matter which of the two was recorded
with parallel execution on. Only once the files already differ: a run
that matches pays nothing for this.
*/
const size_t stripped1= strip_parallel_suffix(&fd1_result);
const size_t stripped2= strip_parallel_suffix(&fd2_result);
if (stripped1 || stripped2)
ignored_parallel_diff= TRUE;
}
if (global_subst &&
(fd1_length != fd2_length ||
memcmp(fd1_result.str, fd2_result.str, fd1_length)))
@@ -2671,6 +2767,10 @@ int check_result()
case RESULT_OK:
if (!error_count)
{
if (ignored_parallel_diff)
fprintf(stderr, "Note: '%s' matched only after ignoring the "
"'_parallel' EXPLAIN suffix (--ignore-parallel-diff)\n",
result_file_name);
error= 0;
break; /* ok */
}
@@ -10231,6 +10331,14 @@ static struct my_option my_long_options[] =
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"prologue", 0, "Include SQL before each test case.", &opt_prologue,
&opt_prologue, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"ignore-parallel-diff", 0,
"Do not fail on tabular EXPLAIN/ANALYZE differences caused by parallel "
"query execution: the '_parallel' suffix on the 'type' column is ignored "
"on both sides of the comparison. A development aid for running existing "
"tests with parallel execution on; it can hide a real plan change, so it "
"cannot be combined with --record.",
&opt_ignore_parallel_diff, &opt_ignore_parallel_diff, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"logdir", OPT_LOG_DIR, "Directory for log files", &opt_logdir,
&opt_logdir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"mark-progress", 0,

View File

@@ -294,6 +294,7 @@ my $opt_port_base= $ENV{'MTR_PORT_BASE'} || "auto";
my $build_thread= 0;
my $opt_record;
my $opt_ignore_parallel_diff;
our $opt_resfile= $ENV{'MTR_RESULT_FILE'} || 0;
@@ -1207,6 +1208,7 @@ sub command_line_setup {
# Test case authoring
'record' => \$opt_record,
'ignore-parallel-diff' => \$opt_ignore_parallel_diff,
'check-testcases!' => \$opt_check_testcases,
'mark-progress' => \$opt_mark_progress,
@@ -1605,6 +1607,14 @@ sub command_line_setup {
$opt_parallel= 1;
}
# Recording under --ignore-parallel-diff would write the '_parallel' suffix
# into the .result, which then fails for everyone running without parallel
# execution.
if ( $opt_record and $opt_ignore_parallel_diff )
{
mtr_error("--record cannot be combined with --ignore-parallel-diff");
}
# --------------------------------------------------------------------------
# Embedded server flag
# --------------------------------------------------------------------------
@@ -5824,6 +5834,11 @@ sub start_mysqltest ($) {
client_debug_arg($args, "mysqltest");
if ( $opt_ignore_parallel_diff )
{
mtr_add_arg($args, "--ignore-parallel-diff");
}
if ( $opt_record )
{
mtr_add_arg($args, "--record");
@@ -6043,6 +6058,11 @@ Options that specify ports
Options for test case authoring
record TESTNAME (Re)genereate the result file for TESTNAME
ignore-parallel-diff Ignore the '_parallel' suffix that parallel query
execution adds to the 'type' column of tabular
EXPLAIN/ANALYZE, so that results recorded without it
still compare equal. Development aid for running
existing tests with --parallel-worker-threads > 0
check-testcases Check testcases for sideeffects
mark-progress Log line number and elapsed time to <testname>.progress