Commit 268bb69b authored by Sergei Petrunia's avatar Sergei Petrunia

MDEV-7691: Assertion `outer_context || !*from_field || *from_field == not_found_field' ...

The bug occurred when a subquery
- has a reference to outside, to grand-parent query or further up
- is converted to a semi-join (i.e. merged into its parent).

Then the reference to outside had form Item_ref(Item_field(...)).
- Conversion to semi-join would call item->fix_after_pullout() for the
  outside reference.
- Item_ref::fix_after_pullout would call Item_field->fix_after_pullout
- The Item_field would construct a new Name_resolution_context object
  This process ignored the fact that the Item_field does not belong to
  any of the subselects being flattened.
The result was crash in the next call to Item_field::fix_fields(), where
we would try to use an invalid Name_resolution_context object.

Fixed by not creating Name_resolution_context object if the Item_field's
context does not belong to the subselect(s) that were flattened.
parent 19896d4b
...@@ -2410,5 +2410,44 @@ SELECT x FROM t1 WHERE id > (SELECT MAX(id) - 1000 FROM t1) ORDER BY x LIMIT 1; ...@@ -2410,5 +2410,44 @@ SELECT x FROM t1 WHERE id > (SELECT MAX(id) - 1000 FROM t1) ORDER BY x LIMIT 1;
x x
0 0
drop table t1; drop table t1;
#
# MDEV-7691: Assertion `outer_context || !*from_field || *from_field == not_found_field' ...
#
set optimizer_switch=default;
CREATE TABLE t1 (a INT) ENGINE=MyISAM;
INSERT INTO t1 VALUES (4),(6);
CREATE TABLE t2 (b INT) ENGINE=MyISAM;
INSERT INTO t2 VALUES (1),(8);
PREPARE stmt FROM "
SELECT * FROM t2
HAVING 0 IN (
SELECT a FROM t1
WHERE a IN (
SELECT a FROM t1
WHERE b = a
)
)
";
EXECUTE stmt;
b
EXECUTE stmt;
b
# Alternative test case, without HAVING
CREATE TABLE t3 (i INT) ENGINE=MyISAM;
INSERT INTO t3 VALUES (4),(6);
PREPARE stmt FROM "
SELECT * FROM t3 AS t10
WHERE EXISTS (
SELECT * FROM t3 AS t20 WHERE t10.i IN (
SELECT i FROM t3
)
)";
EXECUTE stmt;
i
6
EXECUTE stmt;
i
6
drop table t1, t2, t3;
SET optimizer_switch= @@global.optimizer_switch; SET optimizer_switch= @@global.optimizer_switch;
set @@tmp_table_size= @@global.tmp_table_size; set @@tmp_table_size= @@global.tmp_table_size;
...@@ -1959,5 +1959,46 @@ SELECT x FROM t1 WHERE id > (SELECT MAX(id) - 1000 FROM t1) ORDER BY x LIMIT 1; ...@@ -1959,5 +1959,46 @@ SELECT x FROM t1 WHERE id > (SELECT MAX(id) - 1000 FROM t1) ORDER BY x LIMIT 1;
drop table t1; drop table t1;
--echo #
--echo # MDEV-7691: Assertion `outer_context || !*from_field || *from_field == not_found_field' ...
--echo #
set optimizer_switch=default;
CREATE TABLE t1 (a INT) ENGINE=MyISAM;
INSERT INTO t1 VALUES (4),(6);
CREATE TABLE t2 (b INT) ENGINE=MyISAM;
INSERT INTO t2 VALUES (1),(8);
PREPARE stmt FROM "
SELECT * FROM t2
HAVING 0 IN (
SELECT a FROM t1
WHERE a IN (
SELECT a FROM t1
WHERE b = a
)
)
";
EXECUTE stmt;
EXECUTE stmt;
--echo # Alternative test case, without HAVING
CREATE TABLE t3 (i INT) ENGINE=MyISAM;
INSERT INTO t3 VALUES (4),(6);
PREPARE stmt FROM "
SELECT * FROM t3 AS t10
WHERE EXISTS (
SELECT * FROM t3 AS t20 WHERE t10.i IN (
SELECT i FROM t3
)
)";
EXECUTE stmt;
EXECUTE stmt;
drop table t1, t2, t3;
SET optimizer_switch= @@global.optimizer_switch; SET optimizer_switch= @@global.optimizer_switch;
set @@tmp_table_size= @@global.tmp_table_size; set @@tmp_table_size= @@global.tmp_table_size;
...@@ -2777,6 +2777,44 @@ void Item_field::fix_after_pullout(st_select_lex *new_parent, Item **ref) ...@@ -2777,6 +2777,44 @@ void Item_field::fix_after_pullout(st_select_lex *new_parent, Item **ref)
depended_from= NULL; depended_from= NULL;
if (context) if (context)
{ {
bool need_change= false;
/*
Suppose there are nested selects:
select_id=1
select_id=2
select_id=3 <----+
select_id=4 -+
select_id=5 --+
Suppose, pullout operation has moved anything that had select_id=4 or 5
in to select_id=3.
If this Item_field had a name resolution context pointing into select_lex
with id=4 or id=5, it needs a new name resolution context.
However, it could also be that this object is a part of outer reference:
Item_ref(Item_field(field in select with select_id=1))).
- The Item_ref object has a context with select_id=5, and so needs a new
name resolution context.
- The Item_field object has a context with select_id=1, and doesn't need
a new name resolution context.
So, the following loop walks from Item_field's current context upwards.
If we find that the select we've been pulled out to is up there, we
create the new name resolution context. Otherwise, we don't.
*/
for (Name_resolution_context *ct= context; ct; ct= ct->outer_context)
{
if (new_parent == ct->select_lex)
{
need_change= true;
break;
}
}
if (!need_change)
return;
Name_resolution_context *ctx= new Name_resolution_context(); Name_resolution_context *ctx= new Name_resolution_context();
if (context->select_lex == new_parent) if (context->select_lex == new_parent)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment