List:Commits« Previous MessageNext Message »
From:Sven Sandberg Date:November 9 2009 5:44pm
Subject:WL#5096 review
View as plain text  
Hi Luís,

Summary: Good start with the test cases. Much of the code is structured 
and easy to follow. I think the tests can be even better if we get rid 
of some code duplication. Also, I think some test scenarios are missing.

Suggestions:

(S1) Please state for each test (1) what server property is tested; (2) 
how is it tested (see comments inline)

(S2) I think a little refactoring of rpl_row_img_*.test would make the 
test even more transparent (see comments inline)

(S3) I refuse to review rpl_row_sanity :-) Seriously, I'm impressed that 
you could write something that looks so complex, but I think it is 
possible to refactor it to make it more easy to decipher. See comments 
inline.

(S4) I think some tests are missing. In particular, can you add tests 
for the following:

  (S4-1) In rpl_row_sanity, please make sure all cases listed in the 
tables in WL#5072 are tested. Currently, it misses e.g., the autoinc case.

  (S4-2) Test that a slave with missing columns does not write columns 
that it does not have

  (S4-3) Test that if master has binlog_row_image=minimal and slave has 
binlog_row_image=noblob|full, then slave logs the correct columns.

  (S4-4) Test that an error is generated when the slave does not have 
the columns included in the AI.

  (S4-5) Test that correct default values are inserted in extra slave 
columns.

  (S4-6) Test that extra master columns are correctly ignored by the 
slave as long as there is at least one usable column on the slave. E.g.:

   master> CREATE TABLE t1 (a INT NOT NULL, b INT, c INT
                            PRIMARY KEY (a,c), UNIQUE KEY (a));
   slave> CREATE TABLE t1 (a INT NOT NULL, b INT,
                           UNIQUE KEY (a));

AIUI, the above should always replicate correctly even with 
binlog_row_image=minimal?

  (S4-7) Test that the default value for binlog_row_image is 'full'.

(S5) Instead of the connect/disconnect hack in 
rpl_chained_3_hosts_diff_tables.inc , could you make 
include/diff_tables.inc capable of diffing tables in arbitrary 
connections? I think diff_tables.inc should also reset the original 
connection at the end. (Ah, finally! I've been waiting for a chance to 
scratch this itch ever since BUG#35701 was fixed :-)

(S6) Some tests seem redundant and/or it is unclear what is being 
tested. See comments inline.

(S7) Possible typo, see comments inline.

(S8) Remove DELAYED from INSERT DELAYED (see comments inline)

(S9) It looks like a lot of code is duplicated between 
rpl_record_find.test and rpl_row_img.test. Isn't rpl_record_find.test a 
special case of rpl_row_img.test, only with one server less? If not, 
please explain!


==== mysql-test/suite/rpl/t/rpl_row_img_*.test ====
> #
> # Test Description
> # ================
> #  
> #   See WL#5096.
> #

(S1) Please state: (1) the properties that we test; (2) how these 
properties are tested. I think this should be in the test case, it's not 
enough to refer to a WL because test cases and WLs may evolve 
independently over time. When you use a .inc file, you could also put 
the documentation in the .inc file and refer to the .inc file from the 
.test file.

> -- source suite/rpl/include/rpl_chained_3_hosts.inc
> -- source include/have_binlog_format_row.inc
> -- connection mysqld_a
> -- source include/have_innodb.inc
> -- connection mysqld_b
> -- source include/have_innodb.inc
> -- connection mysqld_c
> -- source include/have_innodb.inc
> -- connection mysqld_a
> 
> -- echo ####  Chained replication: A -> B -> C ####
> 
> -- let $row_image_param= 'FULL'
> -- source suite/rpl/include/rpl_row_img_set.inc
> 
> --echo #################################################################
> --echo # CASE 1: InnoDB - A, MyISAM - B,C
> --echo #################################################################

(S2) There is a lot of code duplication here. Why not use loops:

--let $engine_type_a= 2
--let $mysqld_a_engine= MyISAM
while ($engine_type_a) {
   --let $engine_type_b= 2
   --let $mysqld_b_engine= MyISAM
   while ($engine_type_b) {
     --let $engine_type_c= 2
     --let $mysqld_c_engine= MyISAM
     while ($engine_type_c) {

       ...

       --let $mysqld_c_engine= InnoDB
       --dec $engine_type_c
     }
     --let $mysqld_b_engine= InnoDB
     --dec $engine_type_b
   }
   --let $mysqld_a_engine= InnoDB
   --dec $engine_type_a
}

Then you can also merge all the 
rpl_row_img_[myisam|innodb][|_blobs|_indexes].test tests into one. And 
adding another engine becomes easy.

> 
> let $mysqld_a_engine= InnoDB;
> let $mysqld_b_engine= MyISAM;
> let $mysqld_c_engine= MyISAM;
> 
> -- source extra/rpl_tests/rpl_row_img.test
> 
> --echo #################################################################
> --echo # CASE 2: InnoDB - A,B,C
> --echo #################################################################
> 
> let $mysqld_a_engine= InnoDB;
> let $mysqld_b_engine= InnoDB;
> let $mysqld_c_engine= InnoDB;
> 
> -- source extra/rpl_tests/rpl_row_img.test
> 
> --echo #################################################################
> --echo # CASE 3: MyISAM - A, InnoDB - B,C
> --echo #################################################################
> 
> let $mysqld_a_engine= MyISAM;
> let $mysqld_b_engine= InnoDB;
> let $mysqld_c_engine= InnoDB;
> 
> -- source extra/rpl_tests/rpl_row_img.test
> 
> --echo #################################################################
> --echo # CASE 4: MyISAM - A,C, InnoDB - B
> --echo #################################################################
> 
> let $mysqld_a_engine= MyISAM;
> let $mysqld_b_engine= InnoDB;
> let $mysqld_c_engine= MyISAM;
> 
> -- source extra/rpl_tests/rpl_row_img.test
> 
> --echo #################################################################
> --echo # CASE 5: MyISAM - B, InnoDB - A,C
> --echo #################################################################
> 
> let $mysqld_a_engine= InnoDB;
> let $mysqld_b_engine= MyISAM;
> let $mysqld_c_engine= InnoDB;
> 
> -- source extra/rpl_tests/rpl_row_img.test
> 
> --echo #################################################################
> --echo # CASE 6: InnoDB - C, MyISAM - A,B
> --echo #################################################################
> 
> let $mysqld_a_engine= MyISAM;
> let $mysqld_b_engine= MyISAM;
> let $mysqld_c_engine= InnoDB;
> 
> -- source extra/rpl_tests/rpl_row_img.test
> 
> --echo #################################################################
> --echo # CASE 7: InnoDB - A,C MyISAM - C
> --echo #################################################################
> 
> let $mysqld_a_engine= InnoDB;
> let $mysqld_b_engine= InnoDB;
> let $mysqld_c_engine= MyISAM;
> 
> -- source extra/rpl_tests/rpl_row_img.test
> 
> -- echo ############## NOBLOB
> 
> -- let $row_image_param= 'NOBLOB'
> -- source suite/rpl/include/rpl_row_img_set.inc
> 
> --echo #################################################################
> --echo # CASE 8: InnoDB - A, MyISAM - B,C
> --echo #################################################################
> 
> let $mysqld_a_engine= InnoDB;
> let $mysqld_b_engine= MyISAM;
> let $mysqld_c_engine= MyISAM;
> 
> -- source extra/rpl_tests/rpl_row_img.test
> 
> --echo #################################################################
> --echo # CASE 9: InnoDB - A,B,C
> --echo #################################################################
> 
> let $mysqld_a_engine= InnoDB;
> let $mysqld_b_engine= InnoDB;
> let $mysqld_c_engine= InnoDB;
> 
> -- source extra/rpl_tests/rpl_row_img.test
> 
> --echo #################################################################
> --echo # CASE 10: MyISAM - A, InnoDB - B,C
> --echo #################################################################
> 
> let $mysqld_a_engine= MyISAM;
> let $mysqld_b_engine= InnoDB;
> let $mysqld_c_engine= InnoDB;
> 
> -- source extra/rpl_tests/rpl_row_img.test
> 
> --echo #################################################################
> --echo # CASE 11: MyISAM - A,C, InnoDB - B
> --echo #################################################################
> 
> let $mysqld_a_engine= MyISAM;
> let $mysqld_b_engine= InnoDB;
> let $mysqld_c_engine= MyISAM;
> 
> -- source extra/rpl_tests/rpl_row_img.test
> 
> --echo #################################################################
> --echo # CASE 12: MyISAM - B, InnoDB - A,C
> --echo #################################################################
> 
> let $mysqld_a_engine= InnoDB;
> let $mysqld_b_engine= MyISAM;
> let $mysqld_c_engine= InnoDB;
> 
> -- source extra/rpl_tests/rpl_row_img.test
> 
> --echo #################################################################
> --echo # CASE 13: InnoDB - C, MyISAM - A,B
> --echo #################################################################
> 
> let $mysqld_a_engine= MyISAM;
> let $mysqld_b_engine= MyISAM;
> let $mysqld_c_engine= InnoDB;
> 
> -- source extra/rpl_tests/rpl_row_img.test
> 
> --echo #################################################################
> --echo # CASE 14: InnoDB - A,C MyISAM - C
> --echo #################################################################
> 
> let $mysqld_a_engine= InnoDB;
> let $mysqld_b_engine= InnoDB;
> let $mysqld_c_engine= MyISAM;
> 
> -- source extra/rpl_tests/rpl_row_img.test
> 
> -- echo ############## MINIMAL
> 
> -- let $row_image_param= 'MINIMAL'
> -- source suite/rpl/include/rpl_row_img_set.inc
> 
> --echo #################################################################
> --echo # CASE 15: InnoDB - A, MyISAM - B,C
> --echo #################################################################
> 
> let $mysqld_a_engine= InnoDB;
> let $mysqld_b_engine= MyISAM;
> let $mysqld_c_engine= MyISAM;
> 
> -- source extra/rpl_tests/rpl_row_img.test
> 
> --echo #################################################################
> --echo # CASE 16: InnoDB - A,B,C
> --echo #################################################################
> 
> let $mysqld_a_engine= InnoDB;
> let $mysqld_b_engine= InnoDB;
> let $mysqld_c_engine= InnoDB;
> 
> -- source extra/rpl_tests/rpl_row_img.test
> 
> --echo #################################################################
> --echo # CASE 17: MyISAM - A, InnoDB - B,C
> --echo #################################################################
> 
> let $mysqld_a_engine= MyISAM;
> let $mysqld_b_engine= InnoDB;
> let $mysqld_c_engine= InnoDB;
> 
> -- source extra/rpl_tests/rpl_row_img.test
> 
> --echo #################################################################
> --echo # CASE 18: MyISAM - A,C, InnoDB - B
> --echo #################################################################
> 
> let $mysqld_a_engine= MyISAM;
> let $mysqld_b_engine= InnoDB;
> let $mysqld_c_engine= MyISAM;
> 
> -- source extra/rpl_tests/rpl_row_img.test
> 
> --echo #################################################################
> --echo # CASE 19: MyISAM - B, InnoDB - A,C
> --echo #################################################################
> 
> let $mysqld_a_engine= InnoDB;
> let $mysqld_b_engine= MyISAM;
> let $mysqld_c_engine= InnoDB;
> 
> -- source extra/rpl_tests/rpl_row_img.test
> 
> --echo #################################################################
> --echo # CASE 20: InnoDB - C, MyISAM - A,B
> --echo #################################################################
> 
> let $mysqld_a_engine= MyISAM;
> let $mysqld_b_engine= MyISAM;
> let $mysqld_c_engine= InnoDB;
> 
> -- source extra/rpl_tests/rpl_row_img.test
> 
> --echo #################################################################
> --echo # CASE 21: InnoDB - A,C MyISAM - C
> --echo #################################################################
> 
> let $mysqld_a_engine= InnoDB;
> let $mysqld_b_engine= InnoDB;
> let $mysqld_c_engine= MyISAM;
> 
> -- source extra/rpl_tests/rpl_row_img.test
> 
> ## clean up
> -- let $row_image_param= 'FULL'
> -- source suite/rpl/include/rpl_row_img_set.inc
> 



==== mysql-test/extra/rpl_tests/rpl_row_img.test ====
> #  WL#5096
> #  
> #  Description 
> #  ===========
> #
> #  This test case covers Requirements for different index structures
> #  on both master and slave. This covers requirements R1. R2 is
> #  covered in another test.

(S1) Please state the requirements explicitly in the test case!

> #
> #  Usage
> #  =====
> #
> #  Before including this file the following variables should be set:
> #    * $mysqld_a_engine
> #    * $mysqld_b_engine
> #    * $mysqld_c_engine
> #
> #  Example:
> #
> #     -- let $mysqld_a_engine= Falcon
> #     -- let $mysqld_b_engine= MyISAM
> #     -- let $mysqld_c_engine= InnoDB
> #
> #     -- source extra/rpl_tests/rpl_row_img.test
> # 
> 
> -- connection mysqld_a
> 
> -- disable_warnings
> DROP TABLE IF EXISTS t;
> -- enable_warnings
> -- source suite/rpl/include/rpl_chained_3_hosts_sync.inc
> 
> -- connection mysqld_a
> 
> let $i= 14;
> while($i)
> {
>   let $step= "";
> 
>   -- connection mysqld_a
>   SET SQL_LOG_BIN=0;
> 
>   -- connection mysqld_b
>   SET SQL_LOG_BIN=0;
> 
>   -- connection mysqld_c
>   SET SQL_LOG_BIN=0;
> 
>   if (`SELECT $i=1`) {
>     let $step= No keys;
>     --echo ******* TEST: $step
>     -- connection mysqld_a
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1)) engine=
> $mysqld_a_engine;
>     -- connection mysqld_b
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1)) engine=
> $mysqld_b_engine;
>     -- connection mysqld_c
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1)) engine=
> $mysqld_c_engine;
> 
>   }
>   if (`SELECT $i=2`)
>   { 
>     let $step= One key;
>     --echo ******* TEST: $step
>     -- connection mysqld_a
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), key(c1))
> engine= $mysqld_a_engine;
>     -- connection mysqld_b
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), key(c1))
> engine= $mysqld_b_engine;
>     -- connection mysqld_c
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), key(c1))
> engine= $mysqld_c_engine;
> 
>   }
>   if (`SELECT $i=3`)
>   {
>     let $step= One Composite key;
>     --echo ****** TEST: $step
>     -- connection mysqld_a
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), key(c1,c2))
> engine= $mysqld_a_engine;
>     -- connection mysqld_b
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), key(c1,c2))
> engine= $mysqld_b_engine;
>     -- connection mysqld_c
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), key(c1,c2))
> engine= $mysqld_c_engine;
>   }  
>   if (`SELECT $i=4`)
>   {
>     let $step= One Unique key;
>     --echo ****** TEST: $step
>     -- connection mysqld_a
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), unique
> key(c1)) engine= $mysqld_a_engine;
>     -- connection mysqld_b
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), unique
> key(c1)) engine= $mysqld_b_engine;
>     -- connection mysqld_c
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), unique
> key(c1)) engine= $mysqld_c_engine;
> 
>   }  
>   if (`SELECT $i=5`)
>   {
>     let $step= One Composite Unique key;
>     --echo ****** TEST: $step
>     -- connection mysqld_a
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), unique
> key(c1,c2)) engine= $mysqld_a_engine;
>     -- connection mysqld_b
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), unique
> key(c1,c2)) engine= $mysqld_b_engine;
>     -- connection mysqld_c
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), unique
> key(c1,c2)) engine= $mysqld_c_engine;
> 

(S6) For case 1, 2, 3, 4, 5, 8, 9, what is it you actually test? I 
thought that keys and nullable UKs had no impact on what is logged? If 
you want to test that it searches for the row in the correct way (e.g., 
according to the HLS of WL#5092), then there must be a way for the test 
case to check how the keys are used on the slaves (that would probably 
require some ifdebug code).

Case 11 looks like a duplicate of case 2 (only the index column differs).

>   }  
>   if (`SELECT $i=6`)
>   {
>     let $step= One Primary key;
>     --echo ****** TEST: $step
>     -- connection mysqld_a
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), primary
> key(c1)) engine= $mysqld_a_engine;
>     -- connection mysqld_b
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), primary
> key(c1)) engine= $mysqld_b_engine;
>     -- connection mysqld_c
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), primary
> key(c1)) engine= $mysqld_c_engine;
>   }  
>   if (`SELECT $i=7`)
>   {
>     let $step= One Composite Primary Key;
>     --echo ****** TEST: $step
>     -- connection mysqld_a
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), primary
> key(c1,c2)) engine= $mysqld_a_engine;
>     -- connection mysqld_b
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), primary
> key(c1,c2)) engine= $mysqld_b_engine;
>     -- connection mysqld_c
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), primary
> key(c1,c2)) engine= $mysqld_c_engine;
>   } 
>   if (`SELECT $i=8`)
>   {
>     let $step= One Composite key with holes;
>     --echo ****** TEST: $step
>     -- connection mysqld_a
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), key(c1,c3))
> engine= $mysqld_a_engine;
>     -- connection mysqld_b
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), key(c1,c3))
> engine= $mysqld_b_engine;
>     -- connection mysqld_c
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), key(c1,c3))
> engine= $mysqld_c_engine;
>   }  
>   if (`SELECT $i=9`)
>   {
>     let $step= One Composite Unique key with holes;
>     --echo ****** TEST: $step
>     -- connection mysqld_a
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), unique
> key(c1,c3)) engine= $mysqld_a_engine;
>     -- connection mysqld_b
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), unique
> key(c1,c3)) engine= $mysqld_b_engine;
>     -- connection mysqld_c
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), unique
> key(c1,c3)) engine= $mysqld_c_engine;
>   }  
>   if (`SELECT $i=10`)
>   {
>     let $step= One Composite Primary Key with holes;
>     --echo ****** TEST: $step
>     -- connection mysqld_a
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), primary
> key(c1,c3)) engine= $mysqld_a_engine;
>     -- connection mysqld_b
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), primary
> key(c1,c3)) engine= $mysqld_b_engine;
>     -- connection mysqld_c
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), primary
> key(c1,c3)) engine= $mysqld_c_engine;
>   } 
>   if (`SELECT $i=11`)
>   { 
>     let $step= One key;
>     --echo ******* TEST: $step
>     -- connection mysqld_a
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), key(c4))
> engine= $mysqld_a_engine;
>     -- connection mysqld_b
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), key(c4))
> engine= $mysqld_b_engine;
>     -- connection mysqld_c
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), key(c4))
> engine= $mysqld_c_engine;
>   }
>   if (`SELECT $i=12`)
>   {
>     let $step= One Composite NOT NULL Unique key with holes;
>     --echo ****** TEST: $step
>     -- connection mysqld_a
>     --eval CREATE TABLE t (c1 int NOT NULL, c2 char(1), c3 char(1) NOT NULL, c4
> char(1), unique key(c1,c3)) engine= $mysqld_a_engine;
>     -- connection mysqld_b
>     --eval CREATE TABLE t (c1 int NOT NULL, c2 char(1), c3 char(1) NOT NULL, c4
> char(1), unique key(c1,c3)) engine= $mysqld_b_engine;
>     -- connection mysqld_c
>     --eval CREATE TABLE t (c1 int NOT NULL, c2 char(1), c3 char(1) NOT NULL, c4
> char(1), unique key(c1,c3)) engine= $mysqld_c_engine;
>   }  
>   if (`SELECT $i=13`)
>   {
>     let $step= One Composite NOT NULL Unique key with holes in master, but not on
> NULLABLE on first slave;
>     --echo ****** TEST: $step
>     -- connection mysqld_a
>     --eval CREATE TABLE t (c1 int NOT NULL, c2 char(1), c3 char(1) NOT NULL, c4
> char(1), unique key(c1,c3)) engine= $mysqld_a_engine;
>     -- connection mysqld_b
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), unique
> key(c1,c3)) engine= $mysqld_b_engine;
>     -- connection mysqld_c
>     --eval CREATE TABLE t (c1 int NOT NULL, c2 char(1), c3 char(1) NOT NULL, c4
> char(1), unique key(c1,c3)) engine= $mysqld_c_engine;
>   }  
>   if (`SELECT $i=14`)
>   {
>     let $step= One Composite PK with holes in master, but no PK on first slave;
>     --echo ****** TEST: $step
>     -- connection mysqld_a
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1), primary
> key(c1,c3)) engine= $mysqld_a_engine;
>     -- connection mysqld_b
>     --eval CREATE TABLE t (c1 int, c2 char(1), c3 char(1), c4 char(1)) engine=
> $mysqld_b_engine;
>     -- connection mysqld_c
>     --eval CREATE TABLE t (c1 int NOT NULL, c2 char(1), c3 char(1) NOT NULL, c4
> char(1), primary key(c1,c3)) engine= $mysqld_c_engine;

(S7) Why use NOT NULL on the last slave? Is it a typo?

>   }  
> 
>   -- connection mysqld_a
>   SET SQL_LOG_BIN=1;
> 
>   -- connection mysqld_b
>   SET SQL_LOG_BIN=1;
> 
>   -- connection mysqld_c
>   SET SQL_LOG_BIN=1;
> 
>   -- connection mysqld_a
>   INSERT INTO t VALUES (1, '1', '1', '1' );
>   INSERT INTO t VALUES (4, '4', '4', '4' );
>   INSERT INTO t VALUES (7, '7', '7', '7' );
> 
>   INSERT INTO t VALUES (9, '9', '9', NULL );
> 
>   INSERT INTO t VALUES (2, '1', '2', '2' );
>   INSERT INTO t VALUES (3, '1', '3', '2' );
>  
>   -- source suite/rpl/include/rpl_chained_3_hosts_sync.inc
> 
>   -- connection mysqld_a
>   UPDATE t SET c4 = '7';
> 
>   UPDATE t SET c4 = '5' WHERE c1 = 1;
>   UPDATE t SET c2 = '5' WHERE c1 = 1;
>   UPDATE t SET c1 = '5' WHERE c1 = 1;
> 
>   UPDATE t SET c4 = '8' WHERE c2 = '4';
>   UPDATE t SET c1 = '8' WHERE c2 = '4';
>   UPDATE t SET c2 = '8' WHERE c2 = '4';
> 
>   UPDATE t SET c3 = '0' WHERE c4 = NULL;
>   UPDATE t SET c2 = '0' WHERE c4 = '0';
> 
>   UPDATE t SET c2 = '2' WHERE c4 = '2';

Can you comment on the purpose of the inserts and updates above? Can 
some insert/update be eliminated without removing any test scenario? 
When the result file is as big as this, it helps to minimize things that 
are done repeatedly...

> 
>   -- source suite/rpl/include/rpl_chained_3_hosts_sync.inc
> 
>   -- let $diff_table=test.t
>   -- source suite/rpl/include/rpl_chained_3_hosts_diff_tables.inc
> 
>   -- connection mysqld_a
> 
>   DELETE FROM t WHERE c1 = 7;
>   DELETE FROM t WHERE c1 = 8;
>   DELETE FROM t;
> 
>   -- source suite/rpl/include/rpl_chained_3_hosts_sync.inc
> 
>   -- let $diff_table=test.t
>   -- source suite/rpl/include/rpl_chained_3_hosts_diff_tables.inc
> 
>   -- connection mysqld_a
> 
>   DROP TABLE t;
> 
>   -- source suite/rpl/include/rpl_chained_3_hosts_sync.inc
> 
>   dec $i;
> }


==== rpl_row_img_sanity.test ====
> #
> #  Description
> #  ===========
> #
> #  This test case checks whether binlog files contain
> #  Before and After image values as expected.
> #
> #  Configuration is done using the --binlog-row-image
> #  option. 
> #
> #  How it works
> #  ============
> #
> #  The test case is implemented such that master and slave
> #  create a table with different types of keys. Then, deletes
> #  and updates are performed in the master. The resulting
> #  output of mysqlbinlog -v issued on the binlogs generated 
> #  is then searched for the pattern expected wrt before and 
> #  after images.
> #
> #  See also WL#5096.

(S3) I think this file is too big and unstructured! It is very hard to 
check that all the perl re's are correct, and that all test scenarios 
are covered.

I would suggest to create a subroutine (i.e., .inc file) that takes as 
input the expected binlog contents in a human-readable form, and makes 
the corresponding checks. For example:

   UPDATE t1 SET c1=7, c4=7 WHERE c1=1;
   # This means: the BI contains columns 1, 2, 4, and the AI contains
   # the columns 1, 4. Since both BI and AI are specified, the event
   # is an update event.
   --let $expected_image= 'BI: 1 2 4 AI: 1 4'
   --source extra/rpl_tests/rpl_row_image_test.inc

   INSERT INTO t1 SET c1=7, c4=7;
   # This means: there is no BI, and the AI contains the columns 1, 4.
   # Since only AI is specified, the event is a write event.
   --let $expected_image= 'AI: 1 4'
   --source extra/rpl_tests/rpl_row_image_test.inc

If needed, you could also make the .inc file accept an 
$expected_slave_image parameter to check what the slave logs etc.

The .inc file can keep track of binlog coordinates internally (i.e., 
save the end position of the binlog at the end, and start from that 
position next time it starts).

> 
> 
> -- let $TMP_FILE= $MYSQLTEST_VARDIR/tmp/rpl_row_img_sanity.tmp
> 
> -- source include/master-slave.inc
> -- source include/have_binlog_format_row.inc
> -- connection master
> 
> -- echo ###################################
> -- echo # NO KEYS
> -- echo ###################################
> 
> -- source include/master-slave-reset.inc
> -- connection master
> 
> CREATE TABLE t (c1 int, c2 int, c3 int);
> INSERT INTO t VALUES (1,2,3);
> INSERT INTO t(c1,c2) VALUES (10,20);
> INSERT DELAYED INTO t(c1,c2) VALUES (100,200);

(S8) Why do you test INSERT DELAYED? I don't think there is anything 
special about DELAYED w.r.t. WL#5096? If there is, please add a comment 
about it.

> UPDATE t SET c1=2 WHERE c1=1;
> DELETE FROM t WHERE c2=2;
> DROP TABLE t;
> 
> -- sync_slave_with_master
> -- connection master
> 
> -- let $MYSQLD_DATADIR= `select @@datadir;`
> -- exec $MYSQL_BINLOG -v $MYSQLD_DATADIR/master-bin.000001 > $TMP_FILE
> -- let BINLOG_CONTENTS= $TMP_FILE
> 
> perl;
>   $binlog= $ENV{'BINLOG_CONTENTS'};
>   open(FILE, "$binlog") or die("Unable to open $log_error: $!\n");
>   my $contents = do { local $/; <FILE> };
>   print "master: Unexpected columns in binlog (write row event)\n" if not ($contents
> =~ /### INSERT INTO test\.t\n### SET\n###   \@1=1\n###   \@2=2\n###   \@3=3\n# at/m);
>   print "master: Unexpected columns in binlog (partial write row event)\n" if not
> ($contents =~ /### INSERT INTO test\.t\n### SET\n###   \@1=10\n###   \@2=20\n###  
> \@3=NULL\n# at/m);
>   print "master: Unexpected columns in binlog (delayed write row event)\n" if not
> ($contents =~ /### INSERT INTO test\.t\n### SET\n###   \@1=100\n###   \@2=200\n###  
> \@3=NULL\n# at/m);
> 
>   print "master: Unexpected columns in binlog (update row event)\n" if not ($contents
> =~ /### UPDATE test\.t\n### WHERE\n###   \@1=1\n###   \@2=2\n###   \@3=3\n### SET\n###  
> \@1=2\n###   \@2=2\n###   \@3=3\n# at/m);
>   print "master: Unexpected columns in binlog (delete row event)\n" if not ($contents
> =~ /### DELETE FROM test\.t\n### WHERE\n###   \@1=2\n###   \@2=2\n###   \@3=3\n# at/m);
>   close(FILE);
> EOF
> 
> -- connection slave
> 
> -- let $MYSQLD_DATADIR= `select @@datadir;`
> -- exec $MYSQL_BINLOG -v $MYSQLD_DATADIR/slave-bin.000001 > $TMP_FILE
> -- let BINLOG_CONTENTS= $TMP_FILE
> 
> perl;
>   $binlog= $ENV{'BINLOG_CONTENTS'};
>   open(FILE, "$binlog") or die("Unable to open $log_error: $!\n");
>   my $contents = do { local $/; <FILE> };
>   print "slave: Unexpected columns in binlog (write row event)\n" if not ($contents
> =~ /### INSERT INTO test\.t\n### SET\n###   \@1=1\n###   \@2=2\n###   \@3=3\n# at/m);
>   print "slave: Unexpected columns in binlog (partial write row event)\n" if not
> ($contents =~ /### INSERT INTO test\.t\n### SET\n###   \@1=10\n###   \@2=20\n###  
> \@3=NULL\n# at/m);
>   print "slave: Unexpected columns in binlog (delayed write row event)\n" if not
> ($contents =~ /### INSERT INTO test\.t\n### SET\n###   \@1=100\n###   \@2=200\n###  
> \@3=NULL\n# at/m);
>   print "slave: Unexpected columns in binlog (update row event)\n" if not ($contents
> =~ /### UPDATE test\.t\n### WHERE\n###   \@1=1\n###   \@2=2\n###   \@3=3\n### SET\n###  
> \@1=2\n###   \@2=2\n###   \@3=3\n# at/m);
>   print "slave: Unexpected columns in binlog (delete row event)\n" if not ($contents
> =~ /### DELETE FROM test\.t\n### WHERE\n###   \@1=2\n###   \@2=2\n###   \@3=3\n# at/m);
>   close(FILE);
> EOF
> 
> -- echo ###################################
> -- echo # KEYS
> -- echo ###################################
> 
> -- source include/master-slave-reset.inc
> -- connection master
> 
> CREATE TABLE t (c1 int, c2 int, c3 int, key(c1));
> INSERT INTO t VALUES (1,2,3);
> INSERT INTO t(c1,c2) VALUES (10,20);
> INSERT DELAYED INTO t(c1,c2) VALUES (100,200);
> UPDATE t SET c1=2 WHERE c1=1;
> DELETE FROM t WHERE c2=2;
> DROP TABLE t;
> 
> -- sync_slave_with_master
> -- connection master
> 
> -- let $MYSQLD_DATADIR= `select @@datadir;`
> -- exec $MYSQL_BINLOG -v $MYSQLD_DATADIR/master-bin.000001 > $TMP_FILE
> -- let BINLOG_CONTENTS= $TMP_FILE
> 
> perl;
>   $binlog= $ENV{'BINLOG_CONTENTS'};
>   open(FILE, "$binlog") or die("Unable to open $log_error: $!\n");
>   my $contents = do { local $/; <FILE> };
>   print "master: Unexpected columns in binlog (write row event)\n" if not ($contents
> =~ /### INSERT INTO test\.t\n### SET\n###   \@1=1\n###   \@2=2\n###   \@3=3\n# at/m);
>   print "master: Unexpected columns in binlog (partial write row event)\n" if not
> ($contents =~ /### INSERT INTO test\.t\n### SET\n###   \@1=10\n###   \@2=20\n###  
> \@3=NULL\n# at/m);
>   print "master: Unexpected columns in binlog (delayed write row event)\n" if not
> ($contents =~ /### INSERT INTO test\.t\n### SET\n###   \@1=100\n###   \@2=200\n###  
> \@3=NULL\n# at/m);
>   print "master: Unexpected columns in binlog (update row event)\n" if not ($contents
> =~ /### UPDATE test\.t\n### WHERE\n###   \@1=1\n###   \@2=2\n###   \@3=3\n### SET\n###  
> \@1=2\n###   \@2=2\n###   \@3=3\n# at/m);
>   print "master: Unexpected columns in binlog (delete row event)\n" if not ($contents
> =~ /### DELETE FROM test\.t\n### WHERE\n###   \@1=2\n###   \@2=2\n###   \@3=3\n# at/m);
>   close(FILE);
> EOF
> 
> -- connection slave
> 
> -- let $MYSQLD_DATADIR= `select @@datadir;`
> -- exec $MYSQL_BINLOG -v $MYSQLD_DATADIR/slave-bin.000001 > $TMP_FILE
> -- let BINLOG_CONTENTS= $TMP_FILE
> 
> perl;
>   $binlog= $ENV{'BINLOG_CONTENTS'};
>   open(FILE, "$binlog") or die("Unable to open $log_error: $!\n");
>   my $contents = do { local $/; <FILE> };
>   print "slave: Unexpected columns in binlog (write row event)\n" if not ($contents
> =~ /### INSERT INTO test\.t\n### SET\n###   \@1=1\n###   \@2=2\n###   \@3=3\n# at/m);
>   print "slave: Unexpected columns in binlog (partial write row event)\n" if not
> ($contents =~ /### INSERT INTO test\.t\n### SET\n###   \@1=10\n###   \@2=20\n###  
> \@3=NULL\n# at/m);
>   print "slave: Unexpected columns in binlog (delayed write row event)\n" if not
> ($contents =~ /### INSERT INTO test\.t\n### SET\n###   \@1=100\n###   \@2=200\n###  
> \@3=NULL\n# at/m);
>   print "slave: Unexpected columns in binlog (update row event)\n" if not ($contents
> =~ /### UPDATE test\.t\n### WHERE\n###   \@1=1\n###   \@2=2\n###   \@3=3\n### SET\n###  
> \@1=2\n###   \@2=2\n###   \@3=3\n# at/m);
>   print "slave: Unexpected columns in binlog (delete row event)\n" if not ($contents
> =~ /### DELETE FROM test\.t\n### WHERE\n###   \@1=2\n###   \@2=2\n###   \@3=3\n# at/m);
>   close(FILE);
> EOF
> 
> -- echo ###################################
> -- echo # UNIQUE KEY NULLABLE
> -- echo ###################################
> 
> -- source include/master-slave-reset.inc
> -- connection master
> 
> CREATE TABLE t (c1 int, c2 int, c3 int, unique key(c1));
> INSERT INTO t VALUES (1,2,3);
> INSERT INTO t(c1,c2) VALUES (10,20);
> INSERT DELAYED INTO t(c1,c2) VALUES (100,200);
> UPDATE t SET c1=2 WHERE c1=1;
> DELETE FROM t WHERE c2=2;
> DROP TABLE t;
> 
> -- sync_slave_with_master
> -- connection master
> 
> -- let $MYSQLD_DATADIR= `select @@datadir;`
> -- exec $MYSQL_BINLOG -v $MYSQLD_DATADIR/master-bin.000001 > $TMP_FILE
> -- let BINLOG_CONTENTS= $TMP_FILE
> 
> perl;
>   $binlog= $ENV{'BINLOG_CONTENTS'};
>   open(FILE, "$binlog") or die("Unable to open $log_error: $!\n");
>   my $contents = do { local $/; <FILE> };
>   print "master: Unexpected columns in binlog (write row event)\n" if not ($contents
> =~ /### INSERT INTO test\.t\n### SET\n###   \@1=1\n###   \@2=2\n###   \@3=3\n# at/m);
>   print "master: Unexpected columns in binlog (partial write row event)\n" if not
> ($contents =~ /### INSERT INTO test\.t\n### SET\n###   \@1=10\n###   \@2=20\n###  
> \@3=NULL\n# at/m);
>   print "master: Unexpected columns in binlog (delayed write row event)\n" if not
> ($contents =~ /### INSERT INTO test\.t\n### SET\n###   \@1=100\n###   \@2=200\n###  
> \@3=NULL\n# at/m);
>   print "master: Unexpected columns in binlog (update row event)\n" if not ($contents
> =~ /### UPDATE test\.t\n### WHERE\n###   \@1=1\n###   \@2=2\n###   \@3=3\n### SET\n###  
> \@1=2\n###   \@2=2\n###   \@3=3\n# at/m);
>   print "master: Unexpected columns in binlog (delete row event)\n" if not ($contents
> =~ /### DELETE FROM test\.t\n### WHERE\n###   \@1=2\n###   \@2=2\n###   \@3=3\n# at/m);
>   close(FILE);
> EOF
> 
> -- connection slave
> 
> -- let $MYSQLD_DATADIR= `select @@datadir;`
> -- exec $MYSQL_BINLOG -v $MYSQLD_DATADIR/slave-bin.000001 > $TMP_FILE
> -- let BINLOG_CONTENTS= $TMP_FILE
> 
> perl;
>   $binlog= $ENV{'BINLOG_CONTENTS'};
>   open(FILE, "$binlog") or die("Unable to open $log_error: $!\n");
>   my $contents = do { local $/; <FILE> };
>   print "slave: Unexpected columns in binlog (write row event)\n" if not ($contents
> =~ /### INSERT INTO test\.t\n### SET\n###   \@1=1\n###   \@2=2\n###   \@3=3\n# at/m);
>   print "slave: Unexpected columns in binlog (partial write row event)\n" if not
> ($contents =~ /### INSERT INTO test\.t\n### SET\n###   \@1=10\n###   \@2=20\n###  
> \@3=NULL\n# at/m);
>   print "slave: Unexpected columns in binlog (delayed write row event)\n" if not
> ($contents =~ /### INSERT INTO test\.t\n### SET\n###   \@1=100\n###   \@2=200\n###  
> \@3=NULL\n# at/m);
>   print "slave: Unexpected columns in binlog (update row event)\n" if not ($contents
> =~ /### UPDATE test\.t\n### WHERE\n###   \@1=1\n###   \@2=2\n###   \@3=3\n### SET\n###  
> \@1=2\n###   \@2=2\n###   \@3=3\n# at/m);
>   print "slave: Unexpected columns in binlog (delete row event)\n" if not ($contents
> =~ /### DELETE FROM test\.t\n### WHERE\n###   \@1=2\n###   \@2=2\n###   \@3=3\n# at/m);
>   close(FILE);
> EOF
> 
> -- echo ###################################
> -- echo # PRIMARY KEY
> -- echo ###################################
> 
> -- source include/master-slave-reset.inc
> -- connection master
> 
> CREATE TABLE t (c1 int, c2 int, c3 int, primary key(c1));
> INSERT INTO t VALUES (1,2,3);
> INSERT INTO t(c1,c2) VALUES (10,20);
> INSERT DELAYED INTO t(c1,c2) VALUES (100,200);
> UPDATE t SET c1=2 WHERE c1=1;
> DELETE FROM t WHERE c2=2;
> DROP TABLE t;
> 
> -- sync_slave_with_master
> -- connection master
> 
> -- let $MYSQLD_DATADIR= `select @@datadir;`
> -- exec $MYSQL_BINLOG -v $MYSQLD_DATADIR/master-bin.000001 > $TMP_FILE
> -- let BINLOG_CONTENTS= $TMP_FILE
> 
> perl;
>   $binlog= $ENV{'BINLOG_CONTENTS'};
>   open(FILE, "$binlog") or die("Unable to open $log_error: $!\n");
>   my $contents = do { local $/; <FILE> };
>   print "master: Unexpected columns in binlog (write row event)\n" if not ($contents
> =~ /### INSERT INTO test\.t\n### SET\n###   \@1=1\n###   \@2=2\n###   \@3=3\n# at/m);
>   print "master: Unexpected columns in binlog (partial write row event)\n" if not
> ($contents =~ /### INSERT INTO test\.t\n### SET\n###   \@1=10\n###   \@2=20\n###  
> \@3=NULL\n# at/m);
>   print "master: Unexpected columns in binlog (delayed write row event)\n" if not
> ($contents =~ /### INSERT INTO test\.t\n### SET\n###   \@1=100\n###   \@2=200\n###  
> \@3=NULL\n# at/m);
>   print "master: Unexpected columns in binlog (update row event)\n" if not ($contents
> =~ /### UPDATE test\.t\n### WHERE\n###   \@1=1\n###   \@2=2\n###   \@3=3\n### SET\n###  
> \@1=2\n###   \@2=2\n###   \@3=3\n# at/m);
>   print "master: Unexpected columns in binlog (delete row event)\n" if not ($contents
> =~ /### DELETE FROM test\.t\n### WHERE\n###   \@1=2\n###   \@2=2\n###   \@3=3\n# at/m);
>   close(FILE);
> EOF
> 
> -- connection slave
> 
> -- let $MYSQLD_DATADIR= `select @@datadir;`
> -- exec $MYSQL_BINLOG -v $MYSQLD_DATADIR/slave-bin.000001 > $TMP_FILE
> -- let BINLOG_CONTENTS= $TMP_FILE
> 
> perl;
>   $binlog= $ENV{'BINLOG_CONTENTS'};
>   open(FILE, "$binlog") or die("Unable to open $log_error: $!\n");
>   my $contents = do { local $/; <FILE> };
>   print "slave: Unexpected columns in binlog (write row event)\n" if not ($contents
> =~ /### INSERT INTO test\.t\n### SET\n###   \@1=1\n###   \@2=2\n###   \@3=3\n# at/m);
>   print "slave: Unexpected columns in binlog (partial write row event)\n" if not
> ($contents =~ /### INSERT INTO test\.t\n### SET\n###   \@1=10\n###   \@2=20\n###  
> \@3=NULL\n# at/m);
>   print "slave: Unexpected columns in binlog (delayed write row event)\n" if not
> ($contents =~ /### INSERT INTO test\.t\n### SET\n###   \@1=100\n###   \@2=200\n###  
> \@3=NULL\n# at/m);
>   print "slave: Unexpected columns in binlog (update row event)\n" if not ($contents
> =~ /### UPDATE test\.t\n### WHERE\n###   \@1=1\n###   \@2=2\n###   \@3=3\n### SET\n###  
> \@1=2\n###   \@2=2\n###   \@3=3\n# at/m);
>   print "slave: Unexpected columns in binlog (delete row event)\n" if not ($contents
> =~ /### DELETE FROM test\.t\n### WHERE\n###   \@1=2\n###   \@2=2\n###   \@3=3\n# at/m);
>   close(FILE);
> EOF
> 
> -- echo ###################################
> -- echo # UNIQUE KEY NOT NULL
> -- echo ###################################
> 
> -- source include/master-slave-reset.inc
> -- connection master
> 
> CREATE TABLE t (c1 int NOT NULL, c2 int, c3 int, unique key(c1));
> INSERT INTO t VALUES (1,2,3);
> INSERT INTO t(c1,c2) VALUES (10,20);
> INSERT DELAYED INTO t(c1,c2) VALUES (100,200);
> UPDATE t SET c1=2 WHERE c1=1;
> DELETE FROM t WHERE c2=2;
> DROP TABLE t;
> 
> -- sync_slave_with_master
> -- connection master
> 
> -- let $MYSQLD_DATADIR= `select @@datadir;`
> -- exec $MYSQL_BINLOG -v $MYSQLD_DATADIR/master-bin.000001 > $TMP_FILE
> -- let BINLOG_CONTENTS= $TMP_FILE
> 
> perl;
>   $binlog= $ENV{'BINLOG_CONTENTS'};
>   open(FILE, "$binlog") or die("Unable to open $log_error: $!\n");
>   my $contents = do { local $/; <FILE> };
>   print "master: Unexpected columns in binlog (write row event)\n" if not ($contents
> =~ /### INSERT INTO test\.t\n### SET\n###   \@1=1\n###   \@2=2\n###   \@3=3\n# at/m);
>   print "master: Unexpected columns in binlog (partial write row event)\n" if not
> ($contents =~ /### INSERT INTO test\.t\n### SET\n###   \@1=10\n###   \@2=20\n###  
> \@3=NULL\n# at/m);
>   print "master: Unexpected columns in binlog (delayed write row event)\n" if not
> ($contents =~ /### INSERT INTO test\.t\n### SET\n###   \@1=100\n###   \@2=200\n###  
> \@3=NULL\n# at/m);
>   print "master: Unexpected columns in binlog (update row event)\n" if not ($contents
> =~ /### UPDATE test\.t\n### WHERE\n###   \@1=1\n###   \@2=2\n###   \@3=3\n### SET\n###  
> \@1=2\n###   \@2=2\n###   \@3=3\n# at/m);
>   print "master: Unexpected columns in binlog (delete row event)\n" if not ($contents
> =~ /### DELETE FROM test\.t\n### WHERE\n###   \@1=2\n###   \@2=2\n###   \@3=3\n# at/m);
>   close(FILE);
> EOF
> 
> -- connection slave
> 
> -- let $MYSQLD_DATADIR= `select @@datadir;`
> -- exec $MYSQL_BINLOG -v $MYSQLD_DATADIR/slave-bin.000001 > $TMP_FILE
> -- let BINLOG_CONTENTS= $TMP_FILE
> 
> perl;
>   $binlog= $ENV{'BINLOG_CONTENTS'};
>   open(FILE, "$binlog") or die("Unable to open $log_error: $!\n");
>   my $contents = do { local $/; <FILE> };
>   print "slave: Unexpected columns in binlog (write row event)\n" if not ($contents
> =~ /### INSERT INTO test\.t\n### SET\n###   \@1=1\n###   \@2=2\n###   \@3=3\n# at/m);
>   print "slave: Unexpected columns in binlog (partial write row event)\n" if not
> ($contents =~ /### INSERT INTO test\.t\n### SET\n###   \@1=10\n###   \@2=20\n###  
> \@3=NULL\n# at/m);
>   print "slave: Unexpected columns in binlog (delayed write row event)\n" if not
> ($contents =~ /### INSERT INTO test\.t\n### SET\n###   \@1=100\n###   \@2=200\n###  
> \@3=NULL\n# at/m);
>   print "slave: Unexpected columns in binlog (update row event)\n" if not ($contents
> =~ /### UPDATE test\.t\n### WHERE\n###   \@1=1\n###   \@2=2\n###   \@3=3\n### SET\n###  
> \@1=2\n###   \@2=2\n###   \@3=3\n# at/m);
>   print "slave: Unexpected columns in binlog (delete row event)\n" if not ($contents
> =~ /### DELETE FROM test\.t\n### WHERE\n###   \@1=2\n###   \@2=2\n###   \@3=3\n# at/m);
>   close(FILE);
> EOF
> 
> -- remove_file $TMP_FILE
> 


-- 
Sven Sandberg, Software Engineer
MySQL AB, www.mysql.com
Thread
WL#5096 reviewSven Sandberg9 Nov
  • Re: WL#5096 reviewLuís Soares24 Nov
    • Re: WL#5096 reviewLuís Soares25 Nov
    • Re: WL#5096 reviewSven Sandberg2 Dec
      • Re: WL#5096 reviewLuís Soares26 Mar
        • Re: WL#5096 reviewSven Sandberg30 Mar
          • Re: WL#5096 reviewLuís Soares30 Mar