Commit dafb466f authored by Igor Babaev's avatar Igor Babaev

Fixed LP bug #881318.

If a materialized derived table / view is empty then for this table
the value of file->ref is 0. This was not taken into account by
the function JOIN_CACHE::write_record_data. As a result a query
using an empty materialized derived tables as inner tables of outer
joins and IN subqueries in WHERE conditions could cause server crashes
when the optimizer employed join caches and duplicate elimination for
semi-joins.
parent 031ef305
......@@ -848,5 +848,32 @@ NULL x x
DROP TABLE t1, t2, t3, t4;
set join_cache_level= @tmp869001_jcl;
set optimizer_switch= @tmp869001_os;
#
# Bug #881318: join cache + duplicate elimination + left join
# with empty materialized derived inner table
#
CREATE TABLE t1 (b varchar(1)) ENGINE=InnoDB;
CREATE TABLE t2 (a varchar(1)) ENGINE=InnoDB;
INSERT INTO t2 VALUES ('a');
CREATE TABLE t3 (a varchar(1), b varchar(1)) ENGINE=InnoDB;
INSERT INTO t3 VALUES ('c','c');
CREATE TABLE t4 (b varchar(1)) ENGINE=InnoDB;
INSERT INTO t4 VALUES ('c'), ('b');
CREATE ALGORITHM=TEMPTABLE VIEW v1 AS SELECT * FROM t1;
EXPLAIN
SELECT * FROM t3 LEFT JOIN (v1,t2) ON t3.a = t2.a
WHERE t3.b IN (SELECT b FROM t4);
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t3 ALL NULL NULL NULL NULL 1 Start temporary
1 PRIMARY t2 ALL NULL NULL NULL NULL 1 Using where
1 PRIMARY <derived3> ALL NULL NULL NULL NULL 2
1 PRIMARY t4 ALL NULL NULL NULL NULL 2 Using where; End temporary; Using join buffer (flat, BNL join)
3 DERIVED t1 ALL NULL NULL NULL NULL 1
SELECT * FROM t3 LEFT JOIN (v1,t2) ON t3.a = t2.a
WHERE t3.b IN (SELECT b FROM t4);
a b b a
c c NULL NULL
DROP VIEW v1;
DROP TABLE t1,t2,t3,t4;
# This must be the last in the file:
set optimizer_switch=@subselect_sj2_tmp;
......@@ -859,6 +859,33 @@ NULL x x
DROP TABLE t1, t2, t3, t4;
set join_cache_level= @tmp869001_jcl;
set optimizer_switch= @tmp869001_os;
#
# Bug #881318: join cache + duplicate elimination + left join
# with empty materialized derived inner table
#
CREATE TABLE t1 (b varchar(1)) ENGINE=InnoDB;
CREATE TABLE t2 (a varchar(1)) ENGINE=InnoDB;
INSERT INTO t2 VALUES ('a');
CREATE TABLE t3 (a varchar(1), b varchar(1)) ENGINE=InnoDB;
INSERT INTO t3 VALUES ('c','c');
CREATE TABLE t4 (b varchar(1)) ENGINE=InnoDB;
INSERT INTO t4 VALUES ('c'), ('b');
CREATE ALGORITHM=TEMPTABLE VIEW v1 AS SELECT * FROM t1;
EXPLAIN
SELECT * FROM t3 LEFT JOIN (v1,t2) ON t3.a = t2.a
WHERE t3.b IN (SELECT b FROM t4);
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t3 ALL NULL NULL NULL NULL 1 Using where; Start temporary
1 PRIMARY t2 hash_ALL NULL #hash#$hj 5 test.t3.a 1 Using where; Using join buffer (flat, BNLH join)
1 PRIMARY <derived3> ALL NULL NULL NULL NULL 2 Using join buffer (incremental, BNL join)
1 PRIMARY t4 hash_ALL NULL #hash#$hj 5 test.t3.b 2 Using where; End temporary; Using join buffer (incremental, BNLH join)
3 DERIVED t1 ALL NULL NULL NULL NULL 1
SELECT * FROM t3 LEFT JOIN (v1,t2) ON t3.a = t2.a
WHERE t3.b IN (SELECT b FROM t4);
a b b a
c c NULL NULL
DROP VIEW v1;
DROP TABLE t1,t2,t3,t4;
# This must be the last in the file:
set optimizer_switch=@subselect_sj2_tmp;
set join_cache_level=default;
......
......@@ -860,6 +860,34 @@ NULL x x
DROP TABLE t1, t2, t3, t4;
set join_cache_level= @tmp869001_jcl;
set optimizer_switch= @tmp869001_os;
#
# Bug #881318: join cache + duplicate elimination + left join
# with empty materialized derived inner table
#
CREATE TABLE t1 (b varchar(1)) ENGINE=InnoDB;
CREATE TABLE t2 (a varchar(1)) ENGINE=InnoDB;
INSERT INTO t2 VALUES ('a');
CREATE TABLE t3 (a varchar(1), b varchar(1)) ENGINE=InnoDB;
INSERT INTO t3 VALUES ('c','c');
CREATE TABLE t4 (b varchar(1)) ENGINE=InnoDB;
INSERT INTO t4 VALUES ('c'), ('b');
CREATE ALGORITHM=TEMPTABLE VIEW v1 AS SELECT * FROM t1;
EXPLAIN
SELECT * FROM t3 LEFT JOIN (v1,t2) ON t3.a = t2.a
WHERE t3.b IN (SELECT b FROM t4);
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t3 ALL NULL NULL NULL NULL 1
1 PRIMARY t2 ALL NULL NULL NULL NULL 1 Using where
1 PRIMARY <derived3> ALL NULL NULL NULL NULL 2
1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 5 func 1
2 SUBQUERY t4 ALL NULL NULL NULL NULL 2
3 DERIVED t1 ALL NULL NULL NULL NULL 1
SELECT * FROM t3 LEFT JOIN (v1,t2) ON t3.a = t2.a
WHERE t3.b IN (SELECT b FROM t4);
a b b a
c c NULL NULL
DROP VIEW v1;
DROP TABLE t1,t2,t3,t4;
# This must be the last in the file:
set optimizer_switch=@subselect_sj2_tmp;
set optimizer_switch=default;
......
......@@ -1039,6 +1039,32 @@ DROP TABLE t1, t2, t3, t4;
set join_cache_level= @tmp869001_jcl;
set optimizer_switch= @tmp869001_os;
--echo #
--echo # Bug #881318: join cache + duplicate elimination + left join
--echo # with empty materialized derived inner table
--echo #
CREATE TABLE t1 (b varchar(1)) ENGINE=InnoDB;
CREATE TABLE t2 (a varchar(1)) ENGINE=InnoDB;
INSERT INTO t2 VALUES ('a');
CREATE TABLE t3 (a varchar(1), b varchar(1)) ENGINE=InnoDB;
INSERT INTO t3 VALUES ('c','c');
CREATE TABLE t4 (b varchar(1)) ENGINE=InnoDB;
INSERT INTO t4 VALUES ('c'), ('b');
CREATE ALGORITHM=TEMPTABLE VIEW v1 AS SELECT * FROM t1;
EXPLAIN
SELECT * FROM t3 LEFT JOIN (v1,t2) ON t3.a = t2.a
WHERE t3.b IN (SELECT b FROM t4);
SELECT * FROM t3 LEFT JOIN (v1,t2) ON t3.a = t2.a
WHERE t3.b IN (SELECT b FROM t4);
DROP VIEW v1;
DROP TABLE t1,t2,t3,t4;
--echo # This must be the last in the file:
set optimizer_switch=@subselect_sj2_tmp;
......@@ -1413,12 +1413,22 @@ uint JOIN_CACHE::write_record_data(uchar * link, bool *is_full)
TABLE *table= (TABLE *) copy->str;
copy->str= table->file->ref;
copy->length= table->file->ref_length;
if (!copy->str)
{
/*
If table is an empty inner table of an outer join and it is
a materialized derived table then table->file->ref == NULL.
*/
cp+= copy->length;
break;
}
}
/* fall through */
default:
/* Copy the entire image of the field from the record buffer */
DBUG_ASSERT(cp + copy->length <= buff + buff_size);
memcpy(cp, copy->str, copy->length);
if (copy->str)
memcpy(cp, copy->str, copy->length);
cp+= copy->length;
}
}
......@@ -1811,6 +1821,13 @@ uint JOIN_CACHE::read_record_field(CACHE_FIELD *copy, bool blob_in_rec_buff)
memset(copy->str+len, ' ', copy->length-len);
len+= 2;
break;
case CACHE_ROWID:
if (!copy->str)
{
len= copy->length;
break;
}
/* fall through */
default:
/* Copy the entire image of the field from the record buffer */
len= copy->length;
......
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