Browse Source

MDEV-30073 Wrong result on 2nd execution of PS for query with NOT EXISTS

Items allocated on execution memory are involved in permanent
optimizer transformations.  This commit reallocates these items
to statement memory.

Queries affected by this bug are numerous, but will always involve
1) 2nd execution of a prepared statement or procedure
2) a permanent transformation, such as a semi-join optimization

The fix mainly affects the operation of Item*::fix_fields
10.11-MDEV-30073-R1
Rex Johnston 2 months ago
parent
commit
a137eac8fc
  1. 2
      mysql-test/main/derived_cond_pushdown.test
  2. 4
      mysql-test/main/having_cond_pushdown.test
  3. 72
      mysql-test/main/opt_trace.test
  4. 92
      mysql-test/main/ps.result
  5. 85
      mysql-test/main/ps.test
  6. 3
      mysql-test/main/view.test
  7. 6
      mysql-test/suite/federated/federatedx_create_handlers.result
  8. 1
      mysql-test/suite/federated/federatedx_create_handlers.test
  9. 198
      sql/item.cc
  10. 42
      sql/item_subselect.cc
  11. 1
      sql/item_subselect.h
  12. 20
      sql/item_sum.cc
  13. 2
      sql/item_sum.h
  14. 7
      sql/opt_subselect.cc
  15. 29
      sql/sql_base.cc
  16. 2
      sql/sql_class.cc
  17. 10
      sql/sql_class.h
  18. 86
      sql/sql_select.cc
  19. 4
      sql/sql_union.cc
  20. 4
      sql/table.cc
  21. 1
      sql/table.h

2
mysql-test/main/derived_cond_pushdown.test

@ -1896,9 +1896,7 @@ DELIMITER ;$$
CALL p1('a');
DROP PROCEDURE p1;
--disable_ps2_protocol
SELECT a FROM (SELECT "aa" a) t WHERE REGEXP_INSTR(t.a, (SELECT MAX('aa') FROM DUAL LIMIT 1));
--enable_ps2_protocol
DELIMITER $$;
CREATE OR REPLACE FUNCTION f1(a VARCHAR(10), b VARCHAR(10)) RETURNS INT

4
mysql-test/main/having_cond_pushdown.test

@ -1451,11 +1451,9 @@ DROP TABLE t1,t2;
CREATE TABLE t1 (i int NOT NULL);
SELECT * FROM t1 GROUP BY i HAVING i IN ( i IS NULL);
#dublicate warning
--disable_view_protocol
#duplicate warning
SELECT * FROM t1 GROUP BY i HAVING i IN ( i IS NULL AND 'x' = 0);
SELECT * FROM t1 GROUP BY i HAVING i='1' IN ( i IS NULL AND 'x' = 0);
--enable_view_protocol
DROP TABLE t1;
--echo #

72
mysql-test/main/opt_trace.test

@ -441,14 +441,11 @@ create table t1 ( a int, b int, key a_b(a,b));
insert into t1 select a,a from one_k;
set optimizer_trace='enabled=on';
#Enable after fix MDEV-32034
--disable_view_protocol
explain select * from t1 force index (a_b) where a=2 and b=4;
select JSON_DETAILED(JSON_EXTRACT(trace, '$**.analyzing_range_alternatives')) AS JS from INFORMATION_SCHEMA.OPTIMIZER_TRACE;
explain select * from t1 where a >= 900 and b between 10 and 20;
select JSON_DETAILED(JSON_EXTRACT(trace, '$**.analyzing_range_alternatives')) AS JS from INFORMATION_SCHEMA.OPTIMIZER_TRACE;
--enable_view_protocol
drop table t0,t1;
@ -457,10 +454,7 @@ create table t1 (start_date date, end_date date, filler char(100), key(start_dat
insert into t1 select date_add(now(), interval a day), date_add(now(), interval (a+7) day), 'data' from one_k;
--enable_warnings
explain select * from t1 force index(start_date) where start_date >= '2019-02-10' and end_date <'2019-04-01';
#Enable after fix MDEV-32034
--disable_view_protocol
select JSON_DETAILED(JSON_EXTRACT(trace, '$**.analyzing_range_alternatives')) AS JS from INFORMATION_SCHEMA.OPTIMIZER_TRACE;
--enable_view_protocol
drop table t1,one_k;
create table ten(a int);
@ -475,10 +469,7 @@ create table t1 (
insert into t1 select a,a, a,a from ten;
explain select * from t1 force index(a_b_c) where a between 1 and 4 and b < 50;
#Enable after fix MDEV-32034
--disable_view_protocol
select JSON_DETAILED(JSON_EXTRACT(trace, '$**.analyzing_range_alternatives')) AS JS from INFORMATION_SCHEMA.OPTIMIZER_TRACE;
--enable_view_protocol
drop table ten,t1;
--echo # Ported test from MYSQL for ranges involving Binary column
@ -487,14 +478,11 @@ CREATE TABLE t1(i INT PRIMARY KEY, b BINARY(16), INDEX i_b(b));
INSERT INTO t1 VALUES (1, x'D95B94336A9946A39CF5B58CFE772D8C');
INSERT INTO t1 VALUES (2, NULL);
#Enable after fix MDEV-32034
--disable_view_protocol
EXPLAIN SELECT * FROM t1 WHERE b IN (0xD95B94336A9946A39CF5B58CFE772D8C);
select JSON_DETAILED(JSON_EXTRACT(trace, '$**.analyzing_range_alternatives')) AS JS from INFORMATION_SCHEMA.OPTIMIZER_TRACE;
EXPLAIN SELECT * FROM t1 WHERE b IS NULL;
select JSON_DETAILED(JSON_EXTRACT(trace, '$**.analyzing_range_alternatives')) AS JS from INFORMATION_SCHEMA.OPTIMIZER_TRACE;
--enable_view_protocol
drop table t1;
@ -507,8 +495,6 @@ INSERT INTO t1 VALUES (1, 'ab\n');
INSERT INTO t1 VALUES (2, NULL);
set optimizer_trace=1;
EXPLAIN SELECT * FROM t1 WHERE b='ab\n';
#Enable after fix MDEV-32034
--disable_view_protocol
select JSON_DETAILED(JSON_EXTRACT(trace, '$**.analyzing_range_alternatives')) AS JS from INFORMATION_SCHEMA.OPTIMIZER_TRACE;
ALTER TABLE t1 modify column b BINARY(10) AFTER i;
@ -552,7 +538,6 @@ insert into t1 select date_add(now(), interval a day), date_add(now(), interval
--enable_warnings
explain format=json select * from t1 force index(start_date) where start_date >= '2019-02-10' and end_date <'2019-04-01';
select JSON_DETAILED(JSON_EXTRACT(trace, '$**.analyzing_range_alternatives')) AS JS from INFORMATION_SCHEMA.OPTIMIZER_TRACE;
--enable_view_protocol
drop table t1, t0, one_k;
--echo #
@ -584,19 +569,13 @@ set optimizer_trace=1;
--echo # but for joins using condition selectivity it is not as trivial. So,
--echo # now we are printing it)
explain select * from t0 A, one_k B where A.a<5 and B.a<800;
#Enable after fix MDEV-32034
--disable_view_protocol
select JSON_DETAILED(JSON_EXTRACT(trace, '$**.considered_execution_plans')) AS JS from INFORMATION_SCHEMA.OPTIMIZER_TRACE;
--enable_view_protocol
set join_cache_level=@tmp_jcl;
--echo # This shows post-join selectivity
explain select * from t0 A, one_k B where A.a=B.b and B.a<800;
#Enable after fix MDEV-32034
--disable_view_protocol
select JSON_DETAILED(JSON_EXTRACT(trace, '$**.considered_execution_plans')) AS JS from INFORMATION_SCHEMA.OPTIMIZER_TRACE;
--enable_view_protocol
drop table t0, one_k;
--echo #
@ -608,10 +587,7 @@ insert into t1 values ('foo'), ('bar');
EXPLAIN SELECT * FROM t1 WHERE a= REPEAT('a', 0);
SELECT * FROM t1 WHERE a= REPEAT('a', 0);
#Enable after fix MDEV-32034
--disable_view_protocol
select JSON_DETAILED(JSON_EXTRACT(trace, '$**.analyzing_range_alternatives')) AS JS from INFORMATION_SCHEMA.OPTIMIZER_TRACE;
--enable_view_protocol
DROP TABLE t1;
--echo #
@ -629,10 +605,7 @@ insert into t3 values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
explain
select * from t3 where (a,a) in (select t1.a, t2.a from t1, t2 where t1.b=t2.b);
#Enable after fix MDEV-32034
--disable_view_protocol
select JSON_DETAILED(JSON_EXTRACT(trace, '$**.semijoin_table_pullout')) from INFORMATION_SCHEMA.OPTIMIZER_TRACE;
--enable_view_protocol
drop table t1,t2,t3;
@ -644,10 +617,7 @@ create table t1 (kp1 int, kp2 int, key(kp1, kp2));
insert into t1 values (1,1),(1,5),(5,1),(5,5);
set optimizer_trace=1;
select * from t1 force index(kp1) where (kp1=2 and kp2 >=4);
#Enable after fix MDEV-32034
--disable_view_protocol
select JSON_DETAILED(JSON_EXTRACT(trace, '$**.range_scan_alternatives')) AS JS from INFORMATION_SCHEMA.OPTIMIZER_TRACE;
--enable_view_protocol
drop table t1;
--echo #
@ -661,10 +631,7 @@ INSERT INTO t2 SELECT seq, seq from seq_1_to_100;
SET OPTIMIZER_TRACE=1;
EXPLAIN SELECT * FROM t1, t2 WHERE t1.a=t2.a ORDER BY t2.b;
#Enable after fix MDEV-32034
--disable_view_protocol
select JSON_DETAILED(JSON_EXTRACT(trace, '$**.considered_execution_plans')) AS JS from INFORMATION_SCHEMA.OPTIMIZER_TRACE;
--enable_view_protocol
DROP TABLE t1,t2;
--echo #
@ -676,15 +643,12 @@ CREATE TABLE t1(a INT, b INT);
INSERT INTO t1 SELECT seq, seq from seq_1_to_100;
SET optimizer_trace=1;
ANALYZE TABLE t1 PERSISTENT FOR ALL;
#Enable after fix MDEV-32034
--disable_view_protocol
EXPLAIN EXTENDED SELECT * from t1 WHERE a between 1 and 5 and b <= 5;
select JSON_DETAILED(JSON_EXTRACT(trace, '$**.selectivity_for_columns')) AS JS from INFORMATION_SCHEMA.OPTIMIZER_TRACE;
EXPLAIN EXTENDED SELECT * from t1 WHERE a != 5;
select JSON_DETAILED(JSON_EXTRACT(trace, '$**.selectivity_for_columns')) AS JS from INFORMATION_SCHEMA.OPTIMIZER_TRACE;
EXPLAIN EXTENDED SELECT * from t1 WHERE b >= 10 and b < 25;
select JSON_DETAILED(JSON_EXTRACT(trace, '$**.selectivity_for_columns')) AS JS from INFORMATION_SCHEMA.OPTIMIZER_TRACE;
--enable_view_protocol
drop table t1;
--echo #
@ -695,10 +659,7 @@ drop table t1;
CREATE TABLE t1( a INT, b INT, PRIMARY KEY( a ) );
SELECT sum(b), row_number() OVER (order by b) FROM t1 WHERE a = 101;
UPDATE t1 SET b=10 WHERE a=1;
#Enable after fix MDEV-32034
--disable_view_protocol
SELECT JSON_DETAILED(JSON_EXTRACT(trace, '$**.range_scan_alternatives')) AS JS from INFORMATION_SCHEMA.OPTIMIZER_TRACE;
--enable_view_protocol
DROP TABLE t1;
set optimizer_trace='enabled=off';
@ -759,13 +720,10 @@ where
c5 in (1,2,3,4,5,6,7,8,9,10) and
c6 in (1,2,3,4);
#Enable after fix MDEV-32034
--disable_view_protocol
select
json_detailed(json_extract(trace, '$**.setup_range_conditions'))
from
information_schema.optimizer_trace;
--enable_view_protocol
drop table t1;
@ -874,14 +832,11 @@ set optimizer_trace=DEFAULT;
--echo # MDEV-29179 Condition pushdown from HAVING into WHERE is not shown in optimizer trace
--echo #
#Enable after fix MDEV-32034
--disable_view_protocol
CREATE TABLE t1 (a INT, b VARCHAR(1), KEY (a), KEY(b,a)) ENGINE=MEMORY;
INSERT INTO t1 VALUES (4,'n'),(1,'h'),(NULL,'w');
SET optimizer_trace= 'enabled=on';
SELECT b, a FROM t1 WHERE b <> 'p' OR a = 4 GROUP BY b, a HAVING a <= 7; SELECT json_detailed(json_extract(trace, '$**.steps[*].join_optimization.steps[*].condition_pushdown_from_having') ) exp1, JSON_VALID(trace) exp2 FROM information_schema.optimizer_trace;
DROP TABLE t1;
--enable_view_protocol
--echo #
--echo # MDEV-30334 Optimizer trace produces invalid JSON with WHERE subquery
@ -933,11 +888,8 @@ insert into t3 select a,a from t0;
explain
select * from t1 left join (t2 join t3 on t3.pk=1000) on t2.a=t1.a and t2.pk is null;
#Enable after fix MDEV-32034
--disable_view_protocol
select JSON_DETAILED(JSON_EXTRACT(trace, '$**.mark_join_nest_as_const')) as jd
from information_schema.optimizer_trace;
--disable_view_protocol
drop table t0, t1, t2, t3;
@ -953,27 +905,18 @@ set in_predicate_conversion_threshold=3;
explain select * from t0 where a in (1,2,3,4,5,6);
#Enable after fix MDEV-32034
--disable_view_protocol
select json_detailed(json_extract(trace, '$**.in_to_subquery_conversion')) as jd
from information_schema.optimizer_trace;
--enable_view_protocol
explain select * from t0 where a in (1,2,3,4,5,a+1);
#Enable after fix MDEV-32034
--disable_view_protocol
select json_detailed(json_extract(trace, '$**.in_to_subquery_conversion')) as jd
from information_schema.optimizer_trace;
--enable_view_protocol
explain select * from t0 where a in ('1','2','3','4','5','6');
#Enable after fix MDEV-32034
--disable_view_protocol
select json_detailed(json_extract(trace, '$**.in_to_subquery_conversion')) as jd
from information_schema.optimizer_trace;
--enable_view_protocol
set in_predicate_conversion_threshold=@tmp;
drop table t0;
@ -1081,13 +1024,10 @@ where a=3
group by b,b
having a+b < 10;
#Enable after fix MDEV-32034
--disable_view_protocol
select
json_detailed(json_extract(trace, '$**.substitute_best_equal'))
from
information_schema.optimizer_trace;
--enable_view_protocol
--echo # Check ON expression
explain
@ -1097,13 +1037,10 @@ from t1 left join t2 on t2.a=t1.a and t2.a<3
where
t1.b > 5555;
#Enable after fix MDEV-32034
--disable_view_protocol
select
json_detailed(json_extract(trace, '$**.substitute_best_equal'))
from
information_schema.optimizer_trace;
--enable_view_protocol
--echo # Check nested ON expression
explain
@ -1119,12 +1056,9 @@ select
json_detailed(json_extract(trace, '$**.substitute_best_equal'))
from
information_schema.optimizer_trace;
--enable_view_protocol
--echo # The next query is test for:
--echo # MDEV-23646: Optimizer trace: optimize_cond() should show ON expression processing
#Enable after fix MDEV-32034
--disable_view_protocol
select
json_detailed(json_extract(trace, '$**.condition_processing'))
from
@ -1169,13 +1103,10 @@ where t1.b < 3;
# Just show that choose_best_splitting function has coverage in the
# optimizer trace and re-optmization of child select inside it is distinct
# from the rest of join optimization.
#Check after fix MDEV-32034
--disable_view_protocol
select
json_detailed(json_extract(trace, '$**.choose_best_splitting'))
from
information_schema.optimizer_trace;
--enable_view_protocol
drop table t1,t2;
@ -1193,11 +1124,8 @@ SELECT * FROM t1 WHERE id IN
JSON_TABLE(f1, "$" COLUMNS (jf FOR ORDINALITY)) AS tbl);
--enable_view_protocol
#Enable after fix MDEV-32034
--disable_view_protocol
select json_detailed(json_extract(trace, '$**.best_join_order'))
from information_schema.OPTIMIZER_TRACE;
--enable_view_protocol
DROP TABLE t1;

92
mysql-test/main/ps.result

@ -6047,3 +6047,95 @@ a b c
1 1970-01-01 09:00:01 6
DROP TABLE t;
# End of 10.6 tests
#
# MDEV-30073 Wrong result on 2nd execution of PS for query with NOT EXISTS
#
CREATE TABLE t1 (
product_key int,
dealerid int
);
INSERT INTO t1 VALUES
(3569, 4),
(3569, 112);
CREATE TABLE t2 (
id int,
product_key int
);
INSERT INTO t2 VALUES
(16494, 3569),
(16494, 3569);
CREATE TABLE t3 (
dealerid int
);
INSERT INTO t3 VALUES (4), (5);
set optimizer_switch='subquery_cache=off';
EXPLAIN EXTENDED
SELECT * FROM t1
WHERE
! EXISTS
(
SELECT dt.id
FROM (SELECT id, product_key FROM t2) dt, t3
WHERE
dt.product_key = t1.product_key AND
t3.dealerid = t1.dealerid
);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 2 100.00 Using where
2 MATERIALIZED t2 ALL NULL NULL NULL NULL 2 100.00 Using where
2 MATERIALIZED t3 ALL NULL NULL NULL NULL 2 100.00 Using where; Using join buffer (flat, BNL join)
Warnings:
Note 1276 Field or reference 'test.t1.product_key' of SELECT #2 was resolved in SELECT #1
Note 1276 Field or reference 'test.t1.dealerid' of SELECT #2 was resolved in SELECT #1
Note 1003 /* select#1 */ select `test`.`t1`.`product_key` AS `product_key`,`test`.`t1`.`dealerid` AS `dealerid` from `test`.`t1` where !(<in_optimizer>((`test`.`t1`.`product_key`,`test`.`t1`.`dealerid`),(`test`.`t1`.`product_key`,`test`.`t1`.`dealerid`) in ( <materialize> (/* select#2 */ select `test`.`t2`.`product_key`,`test`.`t3`.`dealerid` from `test`.`t2` join `test`.`t3` where `test`.`t2`.`product_key` is not null and `test`.`t3`.`dealerid` is not null ), <primary_index_lookup>(`test`.`t1`.`product_key` in <temporary table> on distinct_key where `test`.`t1`.`product_key` = `<subquery2>`.`product_key` and `test`.`t1`.`dealerid` = `<subquery2>`.`dealerid`))) and `test`.`t1`.`dealerid` is not null and `test`.`t1`.`product_key` is not null)
SELECT * FROM t1
WHERE
! EXISTS
(
SELECT dt.id
FROM (SELECT id, product_key FROM t2) dt, t3
WHERE
dt.product_key = t1.product_key AND
t3.dealerid = t1.dealerid
);
product_key dealerid
3569 112
PREPARE stmt FROM "
SELECT * FROM t1
WHERE
! EXISTS
(
SELECT dt.id
FROM (SELECT id, product_key FROM t2) dt, t3
WHERE
dt.product_key = t1.product_key AND
t3.dealerid = t1.dealerid
)";
EXECUTE stmt;
product_key dealerid
3569 112
EXECUTE stmt;
product_key dealerid
3569 112
DEALLOCATE PREPARE stmt;
DROP TABLE t1,t2,t3;
CREATE TABLE t1 (a int, b int);
INSERT INTO t1 VALUES (3569, 4), (3569, 112);
CREATE TABLE t2 (c int, a int);
INSERT INTO t2 VALUES (16494, 3569), (16494, 3569);
PREPARE stmt FROM "
SELECT * FROM t1
WHERE EXISTS (SELECT dt.c FROM (SELECT c, a FROM t2) dt WHERE dt.a = t1.a)
";
EXECUTE stmt;
a b
3569 4
3569 112
EXECUTE stmt;
a b
3569 4
3569 112
DROP TABLE t1,t2;
#
# End of 10.11 tests
#

85
mysql-test/main/ps.test

@ -5498,3 +5498,88 @@ SELECT * FROM t;
DROP TABLE t;
--echo # End of 10.6 tests
--echo #
--echo # MDEV-30073 Wrong result on 2nd execution of PS for query with NOT EXISTS
--echo #
CREATE TABLE t1 (
product_key int,
dealerid int
);
INSERT INTO t1 VALUES
(3569, 4),
(3569, 112);
CREATE TABLE t2 (
id int,
product_key int
);
INSERT INTO t2 VALUES
(16494, 3569),
(16494, 3569);
CREATE TABLE t3 (
dealerid int
);
INSERT INTO t3 VALUES (4), (5);
set optimizer_switch='subquery_cache=off';
EXPLAIN EXTENDED
SELECT * FROM t1
WHERE
! EXISTS
(
SELECT dt.id
FROM (SELECT id, product_key FROM t2) dt, t3
WHERE
dt.product_key = t1.product_key AND
t3.dealerid = t1.dealerid
);
SELECT * FROM t1
WHERE
! EXISTS
(
SELECT dt.id
FROM (SELECT id, product_key FROM t2) dt, t3
WHERE
dt.product_key = t1.product_key AND
t3.dealerid = t1.dealerid
);
PREPARE stmt FROM "
SELECT * FROM t1
WHERE
! EXISTS
(
SELECT dt.id
FROM (SELECT id, product_key FROM t2) dt, t3
WHERE
dt.product_key = t1.product_key AND
t3.dealerid = t1.dealerid
)";
EXECUTE stmt;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
DROP TABLE t1,t2,t3;
CREATE TABLE t1 (a int, b int);
INSERT INTO t1 VALUES (3569, 4), (3569, 112);
CREATE TABLE t2 (c int, a int);
INSERT INTO t2 VALUES (16494, 3569), (16494, 3569);
PREPARE stmt FROM "
SELECT * FROM t1
WHERE EXISTS (SELECT dt.c FROM (SELECT c, a FROM t2) dt WHERE dt.a = t1.a)
";
EXECUTE stmt;
EXECUTE stmt;
DROP TABLE t1,t2;
--echo #
--echo # End of 10.11 tests
--echo #

3
mysql-test/main/view.test

@ -6507,8 +6507,6 @@ DROP TABLE t1, t2;
--echo # MDEV-23291: SUM column from a derived table returns invalid values
--echo #
#Enable ps2 protocol after fix MDEV-31175
--disable_ps2_protocol
CREATE TABLE t1(a INT, b INT);
INSERT INTO t1 VALUES (1,1), (2,2);
@ -6520,7 +6518,6 @@ SELECT sum(z) FROM v1;
DROP TABLE t1;
DROP VIEW v1;
--enable_ps2_protocol
--echo #
--echo # MDEV-26299: Some views force server (and mysqldump) to generate

6
mysql-test/suite/federated/federatedx_create_handlers.result

@ -137,13 +137,13 @@ SELECT *
FROM federated.t3, (SELECT * FROM federated.t1 WHERE id > 3) t
WHERE federated.t3.name=t.name;
name id name
xxx 4 xxx
yyy 5 yyy
yyy 7 yyy
yyy 5 yyy
yyy 7 yyy
xxx 4 xxx
yyy 5 yyy
yyy 7 yyy
yyy 7 yyy
yyy 7 yyy
EXPLAIN
SELECT *
FROM federated.t3, (SELECT * FROM federated.t1 WHERE id > 3) t

1
mysql-test/suite/federated/federatedx_create_handlers.test

@ -94,6 +94,7 @@ DEFAULT CHARSET=latin1;
INSERT INTO federated.t3 VALUES
('yyy'), ('www'), ('yyy'), ('xxx'), ('www'), ('yyy'), ('www');
--sorted_result
SELECT *
FROM federated.t3, (SELECT * FROM federated.t1 WHERE id > 3) t
WHERE federated.t3.name=t.name;

198
sql/item.cc

@ -6010,22 +6010,49 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
select->group_list.elements &&
(place == SELECT_LIST || place == IN_HAVING))
{
Item_outer_ref *rf;
/*
If an outer field is resolved in a grouping select then it
is replaced for an Item_outer_ref object. Otherwise an
Item_field object is used.
The new Item_outer_ref object is saved in the inner_refs_list of
the outer select. Here it is only created. It can be fixed only
after the original field has been fixed and this is done in the
fix_inner_refs() function.
*/
;
if (!(rf= new (thd->mem_root) Item_outer_ref(thd, context, this)))
return -1;
thd->change_item_tree(reference, rf);
select->inner_refs_list.push_back(rf, thd->mem_root);
rf->in_sum_func= thd->lex->in_sum_func;
This Item_field represents a reference to a column in some
outer select. Wrap this Item_field item into an Item_outer_ref
object if we are at the first execution of the query or at
processing the prepare command for it. Make this wrapping
permanent for the first query execution so that it could be used
for next executions of the query.
Add the wrapper item to the list st_select_lex::inner_refs_list
of the select against which this Item_field has been resolved.
*/
Query_arena::enum_state ps_state= thd->stmt_arena->state;
if (ps_state != Query_arena::STMT_EXECUTED)
{
Query_arena *arena= 0, backup;
Item_outer_ref *rf;
bool is_first_execution=
(ps_state != Query_arena::STMT_INITIALIZED);
if (is_first_execution &&
(ps_state != Query_arena::STMT_INITIALIZED))
arena= thd->activate_stmt_arena_if_needed(&backup);
/*
If an outer field is resolved in a grouping select then it
is replaced for an Item_outer_ref object. Otherwise an
Item_field object is used.
The new Item_outer_ref object is saved in the inner_refs_list of
the outer select. Here it is only created. It can be fixed only
after the original field has been fixed and this is done in the
fix_inner_refs() function.
*/
if ((rf= new (thd->mem_root) Item_outer_ref(thd, context, this)))
{
select->inner_refs_list.push_back(rf, thd->mem_root);
if (is_first_execution)
*reference= rf;
else
thd->change_item_tree(reference, rf);
}
if (arena)
thd->restore_active_arena(arena, &backup);
if (!rf)
return -1;
rf->in_sum_func= thd->lex->in_sum_func;
}
}
/*
A reference is resolved to a nest level that's outer or the same as
@ -6126,7 +6153,7 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
else if (ref != not_found_item)
{
Item *save;
Item_ref *rf;
Item_ref *rf= 0;
/* Should have been checked in resolve_ref_in_select_and_group(). */
DBUG_ASSERT(*ref && (*ref)->fixed());
@ -6138,35 +6165,64 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
*/
save= *ref;
*ref= NULL; // Don't call set_properties()
rf= (place == IN_HAVING ?
new (thd->mem_root)
Item_ref(thd, context, ref, table_name,
field_name, alias_name_used) :
(!select->group_list.elements ?
new (thd->mem_root)
Item_direct_ref(thd, context, ref, table_name,
field_name, alias_name_used) :
new (thd->mem_root)
Item_outer_ref(thd, context, ref, table_name,
field_name, alias_name_used)));
*ref= save;
if (!rf)
return -1;
if (place != IN_HAVING && select->group_list.elements)
{
outer_context->select_lex->inner_refs_list.push_back((Item_outer_ref*)rf,
thd->mem_root);
((Item_outer_ref*)rf)->in_sum_func= thd->lex->in_sum_func;
}
thd->change_item_tree(reference, rf);
/*
rf is Item_ref => never substitute other items (in this case)
during fix_fields() => we can use rf after fix_fields()
This Item_field represents a reference to a column in some outer select.
Wrap this Item_field item into an object of the Item_ref class or a class
derived from Item_ref we are at the first execution of the query or at
processing the prepare command for it. Make this wrapping permanent for
the first query execution so that it could be used for next executions
of the query. The type of the wrapper depends on the place where this
item field occurs or on type of the query. If it is in the the having clause
we use a wrapper of the Item_ref type. Otherwise we use a wrapper either
of Item_direct_ref type or of Item_outer_ref. The latter is used for
grouping queries. Such a wrapper is always added to the list inner_refs_list
for the select against which the outer reference has been resolved.
*/
DBUG_ASSERT(!rf->fixed()); // Assured by Item_ref()
if (rf->fix_fields(thd, reference) || rf->check_cols(1))
return -1;
if (thd->stmt_arena->state != Query_arena::STMT_EXECUTED)
{
Query_arena *arena= 0, backup;
bool is_first_execution= thd->is_first_query_execution();
if (is_first_execution &&
!thd->stmt_arena->is_conventional())
arena= thd->activate_stmt_arena_if_needed(&backup);
rf= (place == IN_HAVING ?
new (thd->mem_root)
Item_ref(thd, context, ref, table_name,
field_name, alias_name_used) :
(!select->group_list.elements ?
new (thd->mem_root)
Item_direct_ref(thd, context, ref, table_name,
field_name, alias_name_used) :
new (thd->mem_root)
Item_outer_ref(thd, context, ref, table_name,
field_name, alias_name_used)));
*ref= save;
if (rf)
{
if (place != IN_HAVING && select->group_list.elements)
{
outer_context->select_lex->inner_refs_list.push_back(
(Item_outer_ref*)rf, thd->mem_root);
((Item_outer_ref*)rf)->in_sum_func= thd->lex->in_sum_func;
}
if (is_first_execution)
*reference= rf;
else
thd->change_item_tree(reference, rf);
}
if (arena)
thd->restore_active_arena(arena, &backup);
if (!rf)
return -1;
/*
rf is Item_ref => never substitute other items (in this case)
during fix_fields() => we can use rf after fix_fields()
*/
DBUG_ASSERT(!rf->fixed()); // Assured by Item_ref()
if (rf->fix_fields(thd, reference) || rf->check_cols(1))
return -1;
}
/*
We can not "move" aggregate function in the place where
@ -6191,22 +6247,42 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
this, (Item_ident*)*reference, false);
if (last_checked_context->select_lex->having_fix_field)
{
/* │
This Item_field represents a reference to a column in having clause
of some outer select. Wrap this Item_field item into an Item_ref object
if we are at the first execution of the query or at processing the
prepare command for it. Make this wrapping permanent for the first query
execution so that it could be used for next executions of the query.
*/
Item_ref *rf;
rf= new (thd->mem_root) Item_ref(thd, context,
(*from_field)->table->s->db,
Lex_cstring_strlen((*from_field)->
if (thd->stmt_arena->state != Query_arena::STMT_EXECUTED)
{
Query_arena *arena= 0, backup;
bool is_first_execution= thd->is_first_query_execution();
if (is_first_execution && !thd->stmt_arena->is_conventional())
arena= thd->activate_stmt_arena_if_needed(&backup);
if ((rf= new (thd->mem_root) Item_ref(thd, context,
(*from_field)->table->s->db,
Lex_cstring_strlen((*from_field)->
table->alias.c_ptr()),
field_name);
if (!rf)
return -1;
thd->change_item_tree(reference, rf);
/*
rf is Item_ref => never substitute other items (in this case)
during fix_fields() => we can use rf after fix_fields()
*/
DBUG_ASSERT(!rf->fixed()); // Assured by Item_ref()
if (rf->fix_fields(thd, reference) || rf->check_cols(1))
return -1;
field_name)))
{
if (is_first_execution)
*reference= rf;
else
thd->change_item_tree(reference, rf);
}
thd->restore_active_arena(arena, &backup);
if (!rf)
return -1;
/*
rf is Item_ref => never substitute other items (in this case)
during fix_fields() => we can use rf after fix_fields()
*/
DBUG_ASSERT(!rf->fixed()); // Assured by Item_ref()
if (rf->fix_fields(thd, reference) || rf->check_cols(1))
return -1;
}
return 0;
}
}
@ -8534,6 +8610,14 @@ bool Item_ref::fix_fields(THD *thd, Item **reference)
goto error;
}
/*
We need fix_fields() for the second execution when on the first execution
Item_outer_ref was used.
Populate (*ref)->field before we call Item_direct_ref::fix_fields()
*/
if ((*ref)->fix_fields_if_needed(thd, reference))
goto error;
set_properties();
if ((*ref)->check_cols(1))

42
sql/item_subselect.cc

@ -1045,6 +1045,8 @@ void Item_subselect::update_used_tables()
{
if (!forced_const)
{
if (!unit->thd->is_first_query_execution())
return;
recalc_used_tables(parent_select, FALSE);
if (!(engine->uncacheable() & ~UNCACHEABLE_EXPLAIN))
{
@ -1722,17 +1724,23 @@ bool Item_exists_subselect::fix_length_and_dec()
(unit->global_parameters()->limit_params.select_limit->basic_const_item() &&
unit->global_parameters()->limit_params.select_limit->val_int() > 1))
{
/*
We need only 1 row to determine existence (i.e. any EXISTS that is not
an IN always requires LIMIT 1)
*/
Item *item= new (thd->mem_root) Item_int(thd, (int32) 1);
if (!item)
DBUG_RETURN(TRUE);
thd->change_item_tree(&unit->global_parameters()->limit_params.select_limit,
item);
unit->global_parameters()->limit_params.explicit_limit= 1; // we set the limit
DBUG_PRINT("info", ("Set limit to 1"));
if (thd->is_first_query_execution())
{
/*
We need only 1 row to determine existence (i.e. any EXISTS that is not
an IN always requires LIMIT 1)
This is a permanent transformation and should be allocated on statement
memory and not reversed.
*/
Query_arena backup, *arena= thd->activate_stmt_arena_if_needed(&backup);
Item *item= new (thd->mem_root) Item_int(thd, (int32) 1);
thd->restore_active_arena(arena, &backup);
if (!item)
DBUG_RETURN(TRUE);
unit->global_parameters()->limit_params.select_limit= item;
unit->global_parameters()->limit_params.explicit_limit= 1; // we set the limit
DBUG_PRINT("info", ("Set limit to 1"));
}
}
DBUG_RETURN(FALSE);
}
@ -2184,6 +2192,9 @@ bool Item_allany_subselect::transform_into_max_min(JOIN *join)
item= new (thd->mem_root) Item_sum_min(thd,
select_lex->ref_pointer_array[0]);
}
if (!item)
DBUG_RETURN(TRUE);
if (upper_item)
upper_item->set_sum_test(item);
thd->change_item_tree(&select_lex->ref_pointer_array[0], item);
@ -2198,6 +2209,15 @@ bool Item_allany_subselect::transform_into_max_min(JOIN *join)
save_allow_sum_func= thd->lex->allow_sum_func;
thd->lex->allow_sum_func.set_bit(thd->lex->current_select->nest_level);
/*
init_sum_func_check below has been restricted from resetting some
attributes as it is assumed that the item is allocated on statement
memory. Here this assumption may be FALSE.
We override the check and force reset
*/
item->init_sum_func_check(thd, TRUE);
/*
Item_sum_(max|min) can't substitute other item => we can use 0 as
reference, also Item_sum_(max|min) can't be fixed after creation, so

1
sql/item_subselect.h

@ -206,7 +206,6 @@ public:
void make_const()
{
used_tables_cache= 0;
const_item_cache= 0;
forced_const= TRUE;
}
virtual bool fix_length_and_dec();

20
sql/item_sum.cc

@ -89,7 +89,7 @@ static void store_bit_fields_as_bigint_in_tempory_table(List<Item> *list)
FALSE otherwise
*/
bool Item_sum::init_sum_func_check(THD *thd)
bool Item_sum::init_sum_func_check(THD *thd, bool force)
{
SELECT_LEX *curr_sel= thd->lex->current_select;
if (curr_sel && curr_sel->name_visibility_map.is_clear_all())
@ -111,11 +111,17 @@ bool Item_sum::init_sum_func_check(THD *thd)
/* Save a pointer to object to be used in items for nested set functions */
thd->lex->in_sum_func= this;
nest_level= thd->lex->current_select->nest_level;
ref_by= 0;
aggr_level= -1;
aggr_sel= NULL;
max_arg_level= -1;
max_sum_func_level= -1;
if (force ||
(thd->stmt_arena->state != Query_arena::STMT_EXECUTED) ||
(thd->active_stmt_arena_to_use()->state ==
Query_arena::STMT_SP_QUERY_ARGUMENTS))
{
ref_by= NULL;
aggr_level= -1;
aggr_sel= NULL;
max_arg_level= -1;
max_sum_func_level= -1;
}
outer_fields.empty();
return FALSE;
}
@ -432,7 +438,7 @@ bool Item_sum::register_sum_func(THD *thd, Item **ref)
sl= sl->master_unit()->outer_select() )
sl->master_unit()->item->with_flags|= item_with_t::SUM_FUNC;
}
if (aggr_sel)
if (aggr_sel && aggr_sel != thd->lex->current_select)
thd->lex->current_select->mark_as_dependent(thd, aggr_sel, NULL);
if ((thd->lex->describe & DESCRIBE_EXTENDED) && aggr_sel)

2
sql/item_sum.h

@ -532,7 +532,7 @@ public:
return create_tmp_field(root, param->group(), table);
}
bool collect_outer_ref_processor(void *param) override;
bool init_sum_func_check(THD *thd);
bool init_sum_func_check(THD *thd, bool force= FALSE);
bool check_sum_func(THD *thd, Item **ref);
bool register_sum_func(THD *thd, Item **ref);
st_select_lex *depended_from()

7
sql/opt_subselect.cc

@ -1907,7 +1907,7 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
if (!item_eq)
goto restore_tl_and_exit;
if (left_exp_orig != left_exp)
thd->change_item_tree(item_eq->arguments(), left_exp);
*item_eq->arguments()= left_exp;
item_eq->in_equality_no= 0;
sj_nest->sj_on_expr= and_items(thd, sj_nest->sj_on_expr, item_eq);
}
@ -1930,8 +1930,7 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
DBUG_ASSERT(left_exp->element_index(i)->fixed());
if (left_exp_orig->element_index(i) !=
left_exp->element_index(i))
thd->change_item_tree(item_eq->arguments(),
left_exp->element_index(i));
*item_eq->arguments()= left_exp->element_index(i);
item_eq->in_equality_no= i;
sj_nest->sj_on_expr= and_items(thd, sj_nest->sj_on_expr, item_eq);
}
@ -1955,7 +1954,7 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
for (uint i= 0; i < row->cols(); i++)
{
if (row->element_index(i) != subq_lex->ref_pointer_array[i])
thd->change_item_tree(row->addr(i), subq_lex->ref_pointer_array[i]);
*row->addr(i)= subq_lex->ref_pointer_array[i];
}
item_eq->in_equality_no= 0;
sj_nest->sj_on_expr= and_items(thd, sj_nest->sj_on_expr, item_eq);

29
sql/sql_base.cc

@ -6230,17 +6230,14 @@ find_field_in_view(THD *thd, TABLE_LIST *table_list,
{
if (!my_strcasecmp(system_charset_info, field_it.name()->str, name))
{
// in PS use own arena or data will be freed after prepare
if (register_tree_change &&
thd->stmt_arena->is_stmt_prepare_or_first_stmt_execute())
if (register_tree_change)
arena= thd->activate_stmt_arena_if_needed(&backup);
/*
create_item() may, or may not create a new Item, depending on
the column reference. See create_view_field() for details.
*/
Item *item= field_it.create_item(thd);
if (arena)
thd->restore_active_arena(arena, &backup);
thd->restore_active_arena(arena, &backup);
if (!item)
DBUG_RETURN(0);
@ -6252,11 +6249,25 @@ find_field_in_view(THD *thd, TABLE_LIST *table_list,
the replacing item.
*/
if (*ref && (*ref)->is_explicit_name())
{
// allocate any name on same mem_root as item above
arena=0;
if (thd->is_first_query_execution())
arena= thd->activate_stmt_arena_if_needed(&backup);
item->set_name(thd, (*ref)->name);
if (register_tree_change)
thd->change_item_tree(ref, item);
else
*ref= item;
thd->restore_active_arena(arena, &backup);
}
if (item != *ref)
{
/*
Prepare or 2nd+ execution should be rolled back to 1st execution
transformation
*/
if (thd->stmt_arena->state != Query_arena::STMT_INITIALIZED)
*ref= item;
else
thd->change_item_tree(ref, item);
}
DBUG_RETURN((Field*) view_ref_found);
}
}

2
sql/sql_class.cc

@ -4136,6 +4136,8 @@ void THD::set_n_backup_active_arena(Query_arena *set, Query_arena *backup)
void THD::restore_active_arena(Query_arena *set, Query_arena *backup)
{
if (!set)
return;
DBUG_ENTER("THD::restore_active_arena");
DBUG_ASSERT(backup->is_backup_arena);
set->set_query_arena(this);

10
sql/sql_class.h

@ -2864,6 +2864,14 @@ public:
return static_cast<PSI_thread*>(my_atomic_loadptr((void*volatile*)&m_psi));
}
/* True only during the first execution of the statement */
inline bool is_first_query_execution()
{
return
stmt_arena->state != Query_arena::STMT_EXECUTED &&
stmt_arena->state != Query_arena::STMT_INITIALIZED; // needed for PS
}
private:
unsigned int m_current_stage_key;
@ -4635,6 +4643,8 @@ public:
void change_item_tree(Item **place, Item *new_value)
{
if (*place == new_value)
return;
DBUG_ENTER("THD::change_item_tree");
DBUG_PRINT("enter", ("Register: %p (%p) <- %p",
*place, place, new_value));

86
sql/sql_select.cc

@ -702,6 +702,25 @@ fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select,
the found_in_group_by field of the references from the list.
*/
List_iterator_fast <Item_outer_ref> ref_it(select->inner_refs_list);
/*
For subsequent executions, we need added fields pushed into
all_fields so they are subsequently added to ref_pointer_array
for join execution
*/
if (thd->stmt_arena->state == Query_arena::STMT_EXECUTED)
{
while ((ref= ref_it++))
{
if (ref->found_in_select_list)
continue;
int el= all_fields.elements;
all_fields.push_front(ref_pointer_array[el], thd->mem_root);
}
return false;
}
for (ORDER *group= select->join->group_list; group; group= group->next)
{
(*group->item)->walk(&Item::check_inner_refs_processor, TRUE, &ref_it);
@ -754,16 +773,36 @@ fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select,
else if (ref->found_in_group_by)
direct_ref= TRUE;
new_ref= direct_ref ?
new (thd->mem_root) Item_direct_ref(thd, ref->context, item_ref, ref->table_name,
ref->field_name, ref->alias_name_used) :
new (thd->mem_root) Item_ref(thd, ref->context, item_ref, ref->table_name,
ref->field_name, ref->alias_name_used);
if (!new_ref)
return TRUE;
ref->outer_ref= new_ref;
ref->ref= &ref->outer_ref;
/*
These late fixed items need to be allocated on statement memory during the
first execution, if this is the 2nd execution, they need to be reversed
*/
bool is_first_execution= thd->is_first_query_execution();
if (is_first_execution ||
thd->stmt_arena->is_stmt_prepare())
{
Query_arena *arena, backup;
arena= 0;
if (is_first_execution)
arena= thd->activate_stmt_arena_if_needed(&backup);
new_ref= direct_ref ?
new (thd->mem_root) Item_direct_ref(thd, ref->context,
item_ref, ref->table_name,
ref->field_name,
ref->alias_name_used) :
new (thd->mem_root) Item_ref(thd, ref->context,
item_ref, ref->table_name,
ref->field_name,
ref->alias_name_used);
thd->restore_active_arena(arena, &backup);
if (!new_ref)
return TRUE;
if (is_first_execution)
ref->outer_ref= new_ref;
else
thd->change_item_tree(&ref->outer_ref, new_ref);
ref->ref= &ref->outer_ref;
}
if (ref->fix_fields_if_needed(thd, 0))
return TRUE;
thd->lex->used_tables|= item->used_tables();
@ -27166,8 +27205,22 @@ find_order_in_list(THD *thd, Ref_ptr_array ref_pointer_array,
return FALSE;
}
/* Lookup the current GROUP/ORDER field in the SELECT clause. */
select_item= find_item_in_list(order_item, fields, &counter,
REPORT_EXCEPT_NOT_FOUND, &resolution);
// if this is the first real execution
if (thd->stmt_arena->state != Query_arena::STMT_EXECUTED)
{
order->orig_item= order_item;
select_item= find_item_in_list(order_item,
fields, &counter,
REPORT_EXCEPT_NOT_FOUND, &resolution);
}
else
{
select_item= find_item_in_list(order->orig_item,
fields, &counter,
REPORT_EXCEPT_NOT_FOUND, &resolution);
}
if (!select_item)
return TRUE; /* The item is not unique, or some other error occurred. */
@ -27200,6 +27253,15 @@ find_order_in_list(THD *thd, Ref_ptr_array ref_pointer_array,
&view_ref, IGNORE_ERRORS, FALSE, FALSE);
if (!from_field)
from_field= (Field*) not_found_field;
/*
for the 2nd execution, view_ref returned by create_view_field is wrong
view_ref needs to point to what we allocated on statement mem during
the first execution
*/
if ((thd->stmt_arena->state == Query_arena::STMT_EXECUTED) &&
from_field == view_ref_found)
view_ref= ref_pointer_array[counter];
}
if (from_field == not_found_field ||

4
sql/sql_union.cc

@ -2810,6 +2810,9 @@ bool st_select_lex::cleanup()
if (join)
{
// do NOT empty this list for 2nd execution
if (join->thd->stmt_arena->is_stmt_prepare())
inner_refs_list.empty();
List_iterator<TABLE_LIST> ti(leaf_tables);
TABLE_LIST *tbl;
while ((tbl= ti++))
@ -2839,7 +2842,6 @@ bool st_select_lex::cleanup()
continue;
error= (bool) ((uint) error | (uint) lex_unit->cleanup());
}
inner_refs_list.empty();
exclude_from_table_unique_test= FALSE;
hidden_bit_fields= 0;
DBUG_RETURN(error);

4
sql/table.cc

@ -7245,6 +7245,10 @@ Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref,
{
DBUG_RETURN(field);
}
if (thd->stmt_arena->state == Query_arena::STMT_EXECUTED)
DBUG_RETURN(field);
Name_resolution_context *context= (view->view ?
&view->view->first_select_lex()->context:
&thd->lex->first_select_lex()->context);

1
sql/table.h

@ -243,6 +243,7 @@ typedef struct st_order {
fast_field_copier fast_field_copier_func;
/* Field for which above optimizer function setup */
Field *fast_field_copier_setup;
Item *orig_item; /* copy of *item for find_order_in_list */
int counter; /* position in SELECT list, correct
only if counter_used is true*/
enum enum_order {

Loading…
Cancel
Save