Skip to content

Commit 11d5f7e

Browse files
author
igor@olga.mysql.com
committed
Fixed bug #28375: a query with an NOT IN subquery predicate may cause
a crash when the left operand of the predicate is evaluated to NULL. It happens when the rows from the inner tables (tables from the subquery) are accessed by index methods with key values obtained by evaluation of the left operand of the subquery predicate. When this predicate is evaluated to NULL an alternative access with full table scan is used to check whether the result set returned by the subquery is empty or not. The crash was due to the fact the info about the access methods used for regular key values was not properly restored after a switch back from the full scan access method had occurred. The patch restores this info properly. The same problem existed for queries with IN subquery predicates if they were used not at the top level of the queries.
1 parent e3bd20b commit 11d5f7e

File tree

4 files changed

+67
-2
lines changed

4 files changed

+67
-2
lines changed

mysql-test/r/subselect3.result

+31
Original file line numberDiff line numberDiff line change
@@ -711,3 +711,34 @@ a
711711
1
712712
4
713713
DROP TABLE t1,t2;
714+
CREATE TABLE t1 (id int);
715+
CREATE TABLE t2 (id int PRIMARY KEY);
716+
CREATE TABLE t3 (id int PRIMARY KEY, name varchar(10));
717+
INSERT INTO t1 VALUES (2), (NULL), (3), (1);
718+
INSERT INTO t2 VALUES (234), (345), (457);
719+
INSERT INTO t3 VALUES (222,'bbb'), (333,'ccc'), (111,'aaa');
720+
EXPLAIN
721+
SELECT * FROM t1
722+
WHERE t1.id NOT IN (SELECT t2.id FROM t2,t3
723+
WHERE t3.name='xxx' AND t2.id=t3.id);
724+
id select_type table type possible_keys key key_len ref rows Extra
725+
1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where
726+
2 DEPENDENT SUBQUERY t2 eq_ref PRIMARY PRIMARY 4 func 1 Using where; Using index; Full scan on NULL key
727+
2 DEPENDENT SUBQUERY t3 eq_ref PRIMARY PRIMARY 4 func 1 Using where; Full scan on NULL key
728+
SELECT * FROM t1
729+
WHERE t1.id NOT IN (SELECT t2.id FROM t2,t3
730+
WHERE t3.name='xxx' AND t2.id=t3.id);
731+
id
732+
2
733+
NULL
734+
3
735+
1
736+
SELECT (t1.id IN (SELECT t2.id FROM t2,t3
737+
WHERE t3.name='xxx' AND t2.id=t3.id)) AS x
738+
FROM t1;
739+
x
740+
0
741+
0
742+
0
743+
0
744+
DROP TABLE t1,t2,t3;

mysql-test/t/subselect3.test

+25
Original file line numberDiff line numberDiff line change
@@ -546,3 +546,28 @@ SELECT a FROM t1, t2 WHERE a=b AND (b NOT IN (SELECT a FROM t1));
546546
SELECT a FROM t1, t2 WHERE a=b AND (b NOT IN (SELECT a FROM t1 WHERE a > 4));
547547

548548
DROP TABLE t1,t2;
549+
550+
#
551+
# Bug #28375: crash for NOT IN subquery predicate when left operand becomes NULL
552+
#
553+
554+
CREATE TABLE t1 (id int);
555+
CREATE TABLE t2 (id int PRIMARY KEY);
556+
CREATE TABLE t3 (id int PRIMARY KEY, name varchar(10));
557+
INSERT INTO t1 VALUES (2), (NULL), (3), (1);
558+
INSERT INTO t2 VALUES (234), (345), (457);
559+
INSERT INTO t3 VALUES (222,'bbb'), (333,'ccc'), (111,'aaa');
560+
561+
EXPLAIN
562+
SELECT * FROM t1
563+
WHERE t1.id NOT IN (SELECT t2.id FROM t2,t3
564+
WHERE t3.name='xxx' AND t2.id=t3.id);
565+
SELECT * FROM t1
566+
WHERE t1.id NOT IN (SELECT t2.id FROM t2,t3
567+
WHERE t3.name='xxx' AND t2.id=t3.id);
568+
569+
SELECT (t1.id IN (SELECT t2.id FROM t2,t3
570+
WHERE t3.name='xxx' AND t2.id=t3.id)) AS x
571+
FROM t1;
572+
573+
DROP TABLE t1,t2,t3;

sql/item_subselect.cc

+4-2
Original file line numberDiff line numberDiff line change
@@ -1829,6 +1829,8 @@ int subselect_single_select_engine::exec()
18291829
if (cond_guard && !*cond_guard)
18301830
{
18311831
/* Change the access method to full table scan */
1832+
tab->save_read_first_record= tab->read_first_record;
1833+
tab->save_read_record= tab->read_record.read_record;
18321834
tab->read_first_record= init_read_record_seq;
18331835
tab->read_record.record= tab->table->record[0];
18341836
tab->read_record.thd= join->thd;
@@ -1849,8 +1851,8 @@ int subselect_single_select_engine::exec()
18491851
JOIN_TAB *tab= *ptab;
18501852
tab->read_record.record= 0;
18511853
tab->read_record.ref_length= 0;
1852-
tab->read_first_record= join_read_always_key_or_null;
1853-
tab->read_record.read_record= join_read_next_same_or_null;
1854+
tab->read_first_record= tab->save_read_first_record;
1855+
tab->read_record.read_record= tab->save_read_record;
18541856
}
18551857
executed= 1;
18561858
thd->where= save_where;

sql/sql_select.h

+7
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,13 @@ typedef struct st_join_table {
159159
Read_record_func read_first_record;
160160
Next_select_func next_select;
161161
READ_RECORD read_record;
162+
/*
163+
Currently the following two fields are used only for a [NOT] IN subquery
164+
if it is executed by an alternative full table scan when the left operand of
165+
the subquery predicate is evaluated to NULL.
166+
*/
167+
Read_record_func save_read_first_record;/* to save read_first_record */
168+
int (*save_read_record) (READ_RECORD *);/* to save read_record.read_record */
162169
double worst_seeks;
163170
key_map const_keys; /* Keys with constant part */
164171
key_map checked_keys; /* Keys checked in find_best */

0 commit comments

Comments
 (0)