From 6fadbf8ebf30db6f29b8323054ffc9d52db9302c Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Fri, 2 Feb 2024 20:33:40 -0800 Subject: [PATCH] MDEV-31361 Wrong result on 2nd execution of PS for query with derived table This bug led to wrong result sets returned by the second execution of prepared statements from selects using mergeable derived tables pushed into external engine. Such derived tables are always materialized. The decision that they have to be materialized is taken late in the function mysql_derived_optimized(). For regular derived tables this decision is usually taken at the prepare phase. However in some cases for some derived tables this decision is made in mysql_derived_optimized() too. It can be seen in the code of mysql_derived_fill() that for such a derived table it's critical to change its translation table to tune it to the fields of the temporary table used for materialization of the derived table and this must be done after each refill of the derived table. The same actions are needed for derived tables pushed into external engines. Approved by Oleksandr Byelkin --- .../federatedx_create_handlers.result | 65 +++++++++++++++++++ .../federated/federatedx_create_handlers.test | 61 ++++++++++++++++- sql/sql_derived.cc | 3 + 3 files changed, 126 insertions(+), 3 deletions(-) diff --git a/mysql-test/suite/federated/federatedx_create_handlers.result b/mysql-test/suite/federated/federatedx_create_handlers.result index 6ba3b23bfb2..7fd4422b1be 100644 --- a/mysql-test/suite/federated/federatedx_create_handlers.result +++ b/mysql-test/suite/federated/federatedx_create_handlers.result @@ -496,6 +496,23 @@ use federated; SELECT * FROM (SELECT * FROM (SELECT * FROM (SELECT * FROM t1 where id=3) dt3 WHERE id=2) dt2) dt; id name +PREPARE stmt FROM " +SELECT * FROM (SELECT * FROM (SELECT * FROM (SELECT * FROM t1 where id=3) dt3 + WHERE id=3) dt2) dt; +"; +EXECUTE stmt; +id name +3 xxx +EXECUTE stmt; +id name +3 xxx +DEALLOCATE PREPARE stmt; +EXPLAIN +SELECT * FROM (SELECT * FROM (SELECT * FROM (SELECT * FROM t1 where id=3) dt3 +WHERE id=3) dt2) dt; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY ALL NULL NULL NULL NULL 5 Using where +4 PUSHED DERIVED NULL NULL NULL NULL NULL NULL NULL NULL connection slave; CREATE TABLE federated.t10 (a INT,b INT); CREATE TABLE federated.t11 (a INT, b INT); @@ -516,6 +533,54 @@ WHERE id=2) dt2) dt a b a b id name 1 1 NULL NULL NULL NULL 2 2 NULL NULL NULL NULL +# +# MDEV-31361: Second execution of PS for query with derived table +# +connection slave; +DROP TABLE IF EXISTS federated.t1; +CREATE TABLE federated.t1 ( +id int(20) NOT NULL, +name varchar(16) NOT NULL default '' +) +DEFAULT CHARSET=latin1; +INSERT INTO federated.t1 VALUES +(3,'xxx'), (7,'yyy'), (4,'xxx'), (1,'zzz'), (5,'yyy'); +connection master; +DROP TABLE IF EXISTS federated.t1; +CREATE TABLE federated.t1 ( +id int(20) NOT NULL, +name varchar(16) NOT NULL default '' +) +ENGINE="FEDERATED" DEFAULT CHARSET=latin1 +CONNECTION='mysql://root@127.0.0.1:SLAVE_PORT/federated/t1'; +use federated; +SELECT * FROM +(SELECT * FROM +(SELECT * FROM +(SELECT * FROM t1 where id>3) dt3 +WHERE id>3) dt2 +) dt; +id name +7 yyy +4 xxx +5 yyy +PREPARE stmt FROM "SELECT * FROM +(SELECT * FROM +(SELECT * FROM +(SELECT * FROM t1 where id>3) dt3 +WHERE id>3) dt2 +) dt"; +EXECUTE stmt; +id name +7 yyy +4 xxx +5 yyy +EXECUTE stmt; +id name +7 yyy +4 xxx +5 yyy +DEALLOCATE PREPARE stmt; set global federated_pushdown=0; connection master; DROP TABLE IF EXISTS federated.t1; diff --git a/mysql-test/suite/federated/federatedx_create_handlers.test b/mysql-test/suite/federated/federatedx_create_handlers.test index 19af71e02a9..8e504590282 100644 --- a/mysql-test/suite/federated/federatedx_create_handlers.test +++ b/mysql-test/suite/federated/federatedx_create_handlers.test @@ -94,12 +94,9 @@ DEFAULT CHARSET=latin1; INSERT INTO federated.t3 VALUES ('yyy'), ('www'), ('yyy'), ('xxx'), ('www'), ('yyy'), ('www'); -#Enable after fix MDEV-31361 ---disable_ps2_protocol SELECT * FROM federated.t3, (SELECT * FROM federated.t1 WHERE id > 3) t WHERE federated.t3.name=t.name; ---enable_ps2_protocol EXPLAIN SELECT * @@ -351,6 +348,18 @@ use federated; SELECT * FROM (SELECT * FROM (SELECT * FROM (SELECT * FROM t1 where id=3) dt3 WHERE id=2) dt2) dt; +PREPARE stmt FROM " +SELECT * FROM (SELECT * FROM (SELECT * FROM (SELECT * FROM t1 where id=3) dt3 + WHERE id=3) dt2) dt; +"; +EXECUTE stmt; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +EXPLAIN +SELECT * FROM (SELECT * FROM (SELECT * FROM (SELECT * FROM t1 where id=3) dt3 + WHERE id=3) dt2) dt; + connection slave; CREATE TABLE federated.t10 (a INT,b INT); CREATE TABLE federated.t11 (a INT, b INT); @@ -376,6 +385,52 @@ SELECT * FROM t10 LEFT JOIN WHERE id=2) dt2) dt ) ON t10.a=t11.a; +--echo # +--echo # MDEV-31361: Second execution of PS for query with derived table +--echo # + +connection slave; +DROP TABLE IF EXISTS federated.t1; + +CREATE TABLE federated.t1 ( + id int(20) NOT NULL, + name varchar(16) NOT NULL default '' +) +DEFAULT CHARSET=latin1; + +INSERT INTO federated.t1 VALUES + (3,'xxx'), (7,'yyy'), (4,'xxx'), (1,'zzz'), (5,'yyy'); + +connection master; +DROP TABLE IF EXISTS federated.t1; + +--replace_result $SLAVE_MYPORT SLAVE_PORT +eval +CREATE TABLE federated.t1 ( + id int(20) NOT NULL, + name varchar(16) NOT NULL default '' +) +ENGINE="FEDERATED" DEFAULT CHARSET=latin1 +CONNECTION='mysql://root@127.0.0.1:$SLAVE_MYPORT/federated/t1'; + +use federated; + +let $q= +SELECT * FROM + (SELECT * FROM + (SELECT * FROM + (SELECT * FROM t1 where id>3) dt3 + WHERE id>3) dt2 + ) dt; + +eval $q; + +eval PREPARE stmt FROM "$q"; +EXECUTE stmt; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + + set global federated_pushdown=0; source include/federated_cleanup.inc; diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index ed671b386b4..e0f3ec18bf0 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -1217,7 +1217,9 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived) /* Execute the query that specifies the derived table by a foreign engine */ res= derived->pushdown_derived->execute(); unit->executed= true; + if (res) DBUG_RETURN(res); + goto after_exec; } if (unit->executed && !derived_is_recursive && @@ -1278,6 +1280,7 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived) derived_result, unit, first_select); } + after_exec: if (!res && !derived_is_recursive) { if (derived_result->flush())