Bug #85825 | re-reading changed MTR source files is not safe in mysqltest | ||
---|---|---|---|
Submitted: | 6 Apr 2017 8:01 | Modified: | 2 May 2017 8:51 |
Reporter: | Laurynas Biveinis (OCA) | Email Updates: | |
Status: | Verified | Impact on me: | |
Category: | MySQL Server: Tests | Severity: | S7 (Test Cases) |
Version: | 5.6+ | OS: | Any |
Assigned to: | CPU Architecture: | Any | |
Tags: | mtr, mysql test framework, mysqltest |
[6 Apr 2017 8:01]
Laurynas Biveinis
[6 Apr 2017 8:29]
MySQL Verification Team
Hello Laurynas, Thank you for the report. Thanks, Umesh
[27 Apr 2017 6:22]
Pavan Naik
This looks like not an issue with 'source' command, I was able reproduce the issue with following test case. Test case: ---------- let $counter = 2; while ($counter) { --exec echo "let \$foo = $counter;" > $MYSQL_TMP_DIR/foo.inc --source $MYSQL_TMP_DIR/foo.inc echo foo = $foo; dec $counter; } Output: ------- foo = 2 foo = 2 main.temp [ pass ] 2
[27 Apr 2017 6:23]
Pavan Naik
In the first iteration, mysqltest reads the test cases from a test file and stores them in a Prealloced_array object. Content of pre-allocated array after the first iteration: --------------------------------------------------------- let $counter = 2 while ($counter) { exec echo "let \$foo = $counter;" > $MYSQL_TMP_DIR/foo.inc source $MYSQL_TMP_DIR/foo.inc let $foo = 2 =======> $foo = 2, right operand is a value echo foo = $foo dec $counter } During the second iteration, instead of reading the test cases from the test file, mysqltest reads them from pre-allocated array. Because of this 'let $foo = 2' gets executed again and hence the output value is 2. This can be fixed by modifying the test, in the exec command, "$counter" should be escaped i.e "\$counter". Modified test: -------------- let $counter = 2; while ($counter) { --exec echo "let \$foo = \$counter;" > $MYSQL_TMP_DIR/foo.inc --source $MYSQL_TMP_DIR/foo.inc echo foo = $foo; dec $counter; } Contents of pre-allocated array: -------------------------------- let $counter = 2 while ($counter) { exec echo "let \$foo = \$counter;" > $MYSQL_TMP_DIR/foo.inc source $MYSQL_TMP_DIR/foo.inc let $foo = $counter ====> Note: Here the right operand is not a hard-coded value, instead its a variable which gets parsed to the correct value in do_let() method echo foo = $foo dec $counter } Output: ------- bash > ./mtr --mem main.temp ... ... ... ============================================================================== TEST RESULT TIME (ms) or COMMENT -------------------------------------------------------------------------- worker[1] Using MTR_BUILD_THREAD 300, with reserved ports 13000..13009 foo = 2 foo = 1 main.temp [ pass ] 2 -------------------------------------------------------------------------- The servers were restarted 0 times Spent 0.002 of 14 seconds executing testcases Completed: All 1 tests were successful.
[2 May 2017 7:44]
Laurynas Biveinis
I see the bug has been set to "Need Feedback" - what feedback should I provide? If it is about the proposed fix of quoting $counter to \$counter, then I believe it is not a fix here, but only a very specific workaround for this particular example. The workaround exploits the fact that the $counter is updated correctly between the loop iterations and so makes the sourced MTR file immutable between them. This workaround is not always accessible.
[14 Jun 2019 14:16]
Yura Sorokin
I encountered another similar scenario for which, unfortunately, the suggested workaround is not applicable. --let $restart_counter = 0 while($restart_counter < 2) { --echo *** Iteration $restart_counter *** # Determining server PID file name. --let $pid_file = `SELECT @@global.pid_file` --echo $pid_file # Extracting mysqld PID from $pid_file to an MTR variable $pid_no. --let $pid_no_include_file = $MYSQL_TMP_DIR/pid_no_include_file.inc$restart_counter --exec echo --let \$pid_no = `cat $pid_file` > $pid_no_include_file --echo $pid_no_include_file --cat_file $pid_no_include_file --source $pid_no_include_file --remove_file $pid_no_include_file --echo $pid_no --source include/restart_mysqld.inc --inc $restart_counter } ****************************************************************** For the 'Iteration 1' the result is not expected. *** Iteration 0 *** <build_dir>/mysql-test/var/run/mysqld.1.pid <build_dir>/mysql-test/var/tmp/pid_no_include_file.inc0 --let $pid_no = 9343 9343 # restart *** Iteration 1 *** <build_dir>/mysql-test/var/run/mysqld.1.pid <build_dir>/mysql-test/var/tmp/pid_no_include_file.inc1 --let $pid_no = 9379 9343 # restart ****************************************************************** Here, I am even using different temporary include file names on each iteration, but result is still invalid.