Bug #90225 thd->derived_tables_processing is not restored by TABLE_LIST::resolve_derived
Submitted: 27 Mar 2018 8:21 Modified: 2 May 2018 4:39
Reporter: Nikolai Ikhalainen Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: Optimizer Severity:S3 (Non-critical)
Version:5.7.19, 5.7.21 OS:Any
Assigned to: CPU Architecture:Any

[27 Mar 2018 8:21] Nikolai Ikhalainen
Description:
resolve_derived function called from fill_schema_table_by_open at https://github.com/mysql/mysql-server/blob/5.7/sql/sql_show.cc#L3950
could return errors.

This is causing that thd->derived_tables_processing became true and previous value is not restored to original value.
Saved here:
https://github.com/mysql/mysql-server/blob/5.7/sql/sql_derived.cc#L52
Restored only if there is no errors:
https://github.com/mysql/mysql-server/blob/5.7/sql/sql_derived.cc#L89

How to repeat:
On production system, according to gdb operation on I_S.columns causing invalid output for further select statements inside stored procedure.

If I adding thd->derived_tables_processing= true; at https://github.com/mysql/mysql-server/blob/5.7/sql/sql_show.cc#L3954 the same behavior could be seen with test case:
--source include/have_innodb.inc

CREATE TABLE `t1` (
  `name` varchar(64) NOT NULL DEFAULT '',
  `value` bigint(20) unsigned NOT NULL,
  PRIMARY KEY (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=DYNAMIC;

insert into t1 values ('a',1),('b',2),('c',3);

delimiter |; 
CREATE DEFINER=`root`@`localhost` PROCEDURE `test`() 
    SQL SECURITY INVOKER 
BEGIN 
        select count(*) from information_schema.columns c; 
select * from t1;
select * from t1 where t1.name='a'; 
END |
delimiter ;|

call test;

select * from t1;

drop procedure test;
drop table t1;

Patch (applied to git 5.7 f5ed5d58ffae0ff4c668830376faa411987915a1):
iff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 8c8963d..024d924 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -3951,6 +3951,7 @@ fill_schema_table_by_open(THD *thd, MEM_ROOT *mem_root,
     if (!result)
       result= table_list->setup_materialized_derived(thd);
   }
+  thd->derived_tables_processing= true;
   /*
     Restore old value of sql_command back as it is being looked at in
     process_table() function.

The modification is not a patch to fix an issue, it's just an attempt to reproduce the picture visible in gdb.

Test results:
CREATE TABLE `t1` (
`name` varchar(64) NOT NULL DEFAULT '',
`value` bigint(20) unsigned NOT NULL,
PRIMARY KEY (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=DYNAMIC;
insert into t1 values ('a',1),('b',2),('c',3);
CREATE DEFINER=`root`@`localhost` PROCEDURE `test`() 
SQL SECURITY INVOKER 
BEGIN 
select count(*) from information_schema.columns c; 
select * from t1;
select * from t1 where t1.name='a'; 
END |
call test;
count(*)
3081
name    value
c       3  <- this is Bad: select returned last row multiple times instead of all rows
c       3
c       3
name    value
a       3
select * from t1;
name    value
a       1
b       2
c       3
drop procedure test;
drop table t1;

Suggested fix:
Expected results:
CREATE TABLE `t1` (
`name` varchar(64) NOT NULL DEFAULT '',
`value` bigint(20) unsigned NOT NULL,
PRIMARY KEY (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=DYNAMIC;
insert into t1 values ('a',1),('b',2),('c',3);
CREATE DEFINER=`root`@`localhost` PROCEDURE `test`() 
SQL SECURITY INVOKER 
BEGIN 
select count(*) from information_schema.columns c; 
select * from t1;
select * from t1 where t1.name='a'; 
END |
call test;
count(*)
3081
name    value
a       1
b       2
c       3
name    value
a       3
select * from t1;
name    value
a       1
b       2
c       3
drop procedure test;
drop table t1;
[27 Mar 2018 13:31] MySQL Verification Team
Hello Nikolai,

Thank you for the report and feedback!

Thanks,
Umesh
[2 May 2018 4:39] Karthik Kamath Koteshwar
Fixed in 5.7.23, 8.0.12.