From 4fc9dc84b017cf9f30585bcdef0663f9425fe460 Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Fri, 18 Apr 2025 12:14:23 +0200 Subject: [PATCH] MDEV-32086 (part 2) Server crash when inserting from derived table containing insert target table Get rid of need of matherialization for usual INSERT (cache results in Item_cache* if needed) - subqueries in VALUE do not see new records in the table we are inserting to - subqueries in RETIRNING prohibited to use the table we are inserting to --- mysql-test/main/insert.result | 72 +++++++- mysql-test/main/insert.test | 56 +++++- mysql-test/main/insert_returning.result | 2 + mysql-test/main/insert_returning.test | 2 + mysql-test/main/lowercase_view.result | 12 -- mysql-test/main/lowercase_view.test | 12 -- mysql-test/main/merge.result | 17 +- mysql-test/main/merge.test | 17 +- mysql-test/main/subselect.result | 20 ++- mysql-test/main/subselect.test | 10 +- mysql-test/main/subselect_elimination.result | 12 +- mysql-test/main/subselect_elimination.test | 7 +- .../main/subselect_no_exists_to_in.result | 20 ++- mysql-test/main/subselect_no_mat.result | 20 ++- mysql-test/main/subselect_no_opts.result | 20 ++- mysql-test/main/subselect_no_scache.result | 20 ++- mysql-test/main/subselect_no_semijoin.result | 20 ++- mysql-test/main/view.result | 49 ++++-- mysql-test/main/view.test | 30 ++-- mysql-test/suite/sql_sequence/other.result | 1 - mysql-test/suite/sql_sequence/other.test | 1 - sql/item.h | 12 ++ sql/item_subselect.cc | 24 +++ sql/item_subselect.h | 1 + sql/sql_base.cc | 95 +++++++---- sql/sql_base.h | 3 +- sql/sql_insert.cc | 102 +++++++++-- sql/sql_insert.h | 2 +- sql/sql_lex.cc | 42 +++++ sql/sql_lex.h | 4 + sql/sql_prepare.cc | 4 +- sql/table.h | 2 + tests/mysql_client_test.c | 159 ++++++++++++++++++ 33 files changed, 705 insertions(+), 165 deletions(-) diff --git a/mysql-test/main/insert.result b/mysql-test/main/insert.result index 586dbbff153..49ebd7406d2 100644 --- a/mysql-test/main/insert.result +++ b/mysql-test/main/insert.result @@ -806,5 +806,75 @@ a 8 drop table t1; # -# End of 10.5 tests +# MDEV-32086 Server crash when inserting from derived table containing insert target table +# (part 2) # +create table t1 (pk int, id int); +insert into t1 values (2,2), (3,3), (4,4); +select * from t1; +pk id +2 2 +3 3 +4 4 +select 101+count(*) +from +( +select dt2.id +from (select id from t1) dt2, t1 t where t.id=dt2.id +) dt +where dt.id<1000; +101+count(*) +104 +prepare s from ' +insert into t1 values( + (select 101+count(*) + from + ( + select dt2.id + from (select id from t1) dt2, t1 t where t.id=dt2.id + ) dt + where dt.id<1000 + ), 123 +) +'; +execute s; +select * from t1; +pk id +2 2 +3 3 +4 4 +104 123 +select 101+count(*) +from +( +select dt2.id +from (select id from t1) dt2, t1 t where t.id=dt2.id +) dt +where dt.id<1000; +101+count(*) +105 +execute s; +select * from t1; +pk id +2 2 +3 3 +4 4 +104 123 +105 123 +drop table t1; +# +# Try this: INSERT INTO t1 VALUES ... reference to t1 +# RETURNING (subquery not touching t1) +create table t1 (a int, b int); +create table t2 (a int, b int); +# This is accepted: +insert into t1 (a) values +(3), +((select max(a) from t1)) +returning +a, b, (select max(a) from t2); +a b (select max(a) from t2) +3 NULL NULL +NULL NULL NULL +drop table t1,t2; +# End of 10.5 tests diff --git a/mysql-test/main/insert.test b/mysql-test/main/insert.test index 4b5a5181f09..ec100cb114e 100644 --- a/mysql-test/main/insert.test +++ b/mysql-test/main/insert.test @@ -675,5 +675,59 @@ select * from t1; drop table t1; --echo # ---echo # End of 10.5 tests +--echo # MDEV-32086 Server crash when inserting from derived table containing insert target table +--echo # (part 2) --echo # + +create table t1 (pk int, id int); +insert into t1 values (2,2), (3,3), (4,4); +select * from t1; +select 101+count(*) + from + ( + select dt2.id + from (select id from t1) dt2, t1 t where t.id=dt2.id + ) dt + where dt.id<1000; +prepare s from ' +insert into t1 values( + (select 101+count(*) + from + ( + select dt2.id + from (select id from t1) dt2, t1 t where t.id=dt2.id + ) dt + where dt.id<1000 + ), 123 +) +'; +execute s; +select * from t1; +select 101+count(*) + from + ( + select dt2.id + from (select id from t1) dt2, t1 t where t.id=dt2.id + ) dt + where dt.id<1000; +execute s; +select * from t1; + +drop table t1; + +--echo # +--echo # Try this: INSERT INTO t1 VALUES ... reference to t1 +--echo # RETURNING (subquery not touching t1) +create table t1 (a int, b int); +create table t2 (a int, b int); + +--echo # This is accepted: +insert into t1 (a) values + (3), + ((select max(a) from t1)) +returning + a, b, (select max(a) from t2); + +drop table t1,t2; + +--echo # End of 10.5 tests diff --git a/mysql-test/main/insert_returning.result b/mysql-test/main/insert_returning.result index b2ed9c90e51..35c096f865e 100644 --- a/mysql-test/main/insert_returning.result +++ b/mysql-test/main/insert_returning.result @@ -498,6 +498,8 @@ t1 WHERE id1=1) 5 6 INSERT INTO t2(id2,val2) VALUES(5,'f') RETURNING (SELECT id2 FROM t2); ERROR HY000: Table 't2' is specified twice, both as a target for 'INSERT' and as a separate source for data +INSERT INTO t2(id2,val2) VALUES(5,'f') RETURNING (SELECT 1 UNION SELECT id2 FROM t2); +ERROR HY000: Table 't2' is specified twice, both as a target for 'INSERT' and as a separate source for data INSERT INTO t2 (id2, val2) VALUES (6,'f') RETURNING t1.*; ERROR 42S02: Unknown table 'test.t1' # diff --git a/mysql-test/main/insert_returning.test b/mysql-test/main/insert_returning.test index 837d61d2c16..c2ad613aed0 100644 --- a/mysql-test/main/insert_returning.test +++ b/mysql-test/main/insert_returning.test @@ -199,6 +199,8 @@ INSERT INTO t2(id2,val2) VALUES(5,'e') RETURNING id2, (SELECT id1+id2 FROM t1 WHERE id1=1); --error ER_UPDATE_TABLE_USED INSERT INTO t2(id2,val2) VALUES(5,'f') RETURNING (SELECT id2 FROM t2); +--error ER_UPDATE_TABLE_USED +INSERT INTO t2(id2,val2) VALUES(5,'f') RETURNING (SELECT 1 UNION SELECT id2 FROM t2); --error ER_BAD_TABLE_ERROR INSERT INTO t2 (id2, val2) VALUES (6,'f') RETURNING t1.*; diff --git a/mysql-test/main/lowercase_view.result b/mysql-test/main/lowercase_view.result index af53f67869d..845df4d647d 100644 --- a/mysql-test/main/lowercase_view.result +++ b/mysql-test/main/lowercase_view.result @@ -16,29 +16,17 @@ create view v1Aa as select * from t1aA; create view v2aA as select * from v1aA; create view v3Aa as select v2Aa.col1 from v2aA,t2Aa where v2Aa.col1 = t2aA.col1; insert into v2Aa values ((select max(col1) from v1aA)); -ERROR HY000: The definition of table 'v1aA' prevents operation INSERT on table 'v2Aa' insert into t1aA values ((select max(col1) from v1Aa)); -ERROR HY000: The definition of table 'v1Aa' prevents operation INSERT on table 't1aA' insert into v2aA values ((select max(col1) from v1aA)); -ERROR HY000: The definition of table 'v1aA' prevents operation INSERT on table 'v2aA' insert into v2Aa values ((select max(col1) from t1Aa)); -ERROR HY000: The definition of table 'v2Aa' prevents operation INSERT on table 'v2Aa' insert into t1aA values ((select max(col1) from t1Aa)); -ERROR HY000: Table 't1aA' is specified twice, both as a target for 'INSERT' and as a separate source for data insert into v2aA values ((select max(col1) from t1aA)); -ERROR HY000: The definition of table 'v2aA' prevents operation INSERT on table 'v2aA' insert into v2Aa values ((select max(col1) from v2aA)); -ERROR HY000: Table 'v2Aa' is specified twice, both as a target for 'INSERT' and as a separate source for data insert into t1Aa values ((select max(col1) from v2Aa)); -ERROR HY000: The definition of table 'v2Aa' prevents operation INSERT on table 't1Aa' insert into v2aA values ((select max(col1) from v2Aa)); -ERROR HY000: Table 'v2aA' is specified twice, both as a target for 'INSERT' and as a separate source for data insert into v3Aa (col1) values ((select max(col1) from v1Aa)); -ERROR HY000: The definition of table 'v1Aa' prevents operation INSERT on table 'v3Aa' insert into v3aA (col1) values ((select max(col1) from t1aA)); -ERROR HY000: The definition of table 'v3aA' prevents operation INSERT on table 'v3aA' insert into v3Aa (col1) values ((select max(col1) from v2aA)); -ERROR HY000: The definition of table 'v2aA' prevents operation INSERT on table 'v3Aa' drop view v3aA,v2Aa,v1aA; drop table t1Aa,t2Aa; create table t1Aa (col1 int); diff --git a/mysql-test/main/lowercase_view.test b/mysql-test/main/lowercase_view.test index cdd0256d639..52aae7b2b3c 100644 --- a/mysql-test/main/lowercase_view.test +++ b/mysql-test/main/lowercase_view.test @@ -23,29 +23,17 @@ create table t2aA (col1 int); create view v1Aa as select * from t1aA; create view v2aA as select * from v1aA; create view v3Aa as select v2Aa.col1 from v2aA,t2Aa where v2Aa.col1 = t2aA.col1; --- error 1443 insert into v2Aa values ((select max(col1) from v1aA)); --- error 1443 insert into t1aA values ((select max(col1) from v1Aa)); --- error 1443 insert into v2aA values ((select max(col1) from v1aA)); --- error 1443 insert into v2Aa values ((select max(col1) from t1Aa)); --- error 1093 insert into t1aA values ((select max(col1) from t1Aa)); --- error 1443 insert into v2aA values ((select max(col1) from t1aA)); --- error 1093 insert into v2Aa values ((select max(col1) from v2aA)); --- error 1443 insert into t1Aa values ((select max(col1) from v2Aa)); --- error 1093 insert into v2aA values ((select max(col1) from v2Aa)); --- error 1443 insert into v3Aa (col1) values ((select max(col1) from v1Aa)); --- error 1443 insert into v3aA (col1) values ((select max(col1) from t1aA)); --- error 1443 insert into v3Aa (col1) values ((select max(col1) from v2aA)); drop view v3aA,v2Aa,v1aA; drop table t1Aa,t2Aa; diff --git a/mysql-test/main/merge.result b/mysql-test/main/merge.result index 6722dd38b9c..e0ed3a31004 100644 --- a/mysql-test/main/merge.result +++ b/mysql-test/main/merge.result @@ -3689,33 +3689,22 @@ insert into tmp (b) values (1); insert into t1 (a) values (1); insert into t3 (b) values (1); insert into m1 (a) values ((select max(a) from m1)); -ERROR HY000: Table 'm1' is specified twice, both as a target for 'INSERT' and as a separate source for data insert into m1 (a) values ((select max(a) from m2)); -ERROR HY000: Table 'm1' is specified twice, both as a target for 'INSERT' and as a separate source for data insert into m1 (a) values ((select max(a) from t1)); -ERROR HY000: Table 'm1' is specified twice, both as a target for 'INSERT' and as a separate source for data insert into m1 (a) values ((select max(a) from t2)); -ERROR HY000: Table 'm1' is specified twice, both as a target for 'INSERT' and as a separate source for data insert into m1 (a) values ((select max(a) from t3, m1)); -ERROR HY000: Table 'm1' is specified twice, both as a target for 'INSERT' and as a separate source for data insert into m1 (a) values ((select max(a) from t3, m2)); -ERROR HY000: Table 'm1' is specified twice, both as a target for 'INSERT' and as a separate source for data insert into m1 (a) values ((select max(a) from t3, t1)); -ERROR HY000: Table 'm1' is specified twice, both as a target for 'INSERT' and as a separate source for data insert into m1 (a) values ((select max(a) from t3, t2)); -ERROR HY000: Table 'm1' is specified twice, both as a target for 'INSERT' and as a separate source for data insert into m1 (a) values ((select max(a) from tmp, m1)); -ERROR HY000: Table 'm1' is specified twice, both as a target for 'INSERT' and as a separate source for data insert into m1 (a) values ((select max(a) from tmp, m2)); -ERROR HY000: Table 'm1' is specified twice, both as a target for 'INSERT' and as a separate source for data insert into m1 (a) values ((select max(a) from tmp, t1)); -ERROR HY000: Table 'm1' is specified twice, both as a target for 'INSERT' and as a separate source for data insert into m1 (a) values ((select max(a) from tmp, t2)); -ERROR HY000: Table 'm1' is specified twice, both as a target for 'INSERT' and as a separate source for data insert into m1 (a) values ((select max(a) from v1)); -ERROR HY000: The definition of table 'v1' prevents operation INSERT on table 'm1' insert into m1 (a) values ((select max(a) from tmp, v1)); -ERROR HY000: The definition of table 'v1' prevents operation INSERT on table 'm1' +select count(*) from m1; +count(*) +15 drop view v1; drop temporary table tmp; drop table t1, t2, t3, m1, m2; diff --git a/mysql-test/main/merge.test b/mysql-test/main/merge.test index f64f0c1d099..ad6268667e0 100644 --- a/mysql-test/main/merge.test +++ b/mysql-test/main/merge.test @@ -2704,37 +2704,24 @@ insert into tmp (b) values (1); insert into t1 (a) values (1); insert into t3 (b) values (1); ---error ER_UPDATE_TABLE_USED insert into m1 (a) values ((select max(a) from m1)); ---error ER_UPDATE_TABLE_USED insert into m1 (a) values ((select max(a) from m2)); ---error ER_UPDATE_TABLE_USED insert into m1 (a) values ((select max(a) from t1)); ---error ER_UPDATE_TABLE_USED insert into m1 (a) values ((select max(a) from t2)); ---error ER_UPDATE_TABLE_USED insert into m1 (a) values ((select max(a) from t3, m1)); ---error ER_UPDATE_TABLE_USED insert into m1 (a) values ((select max(a) from t3, m2)); ---error ER_UPDATE_TABLE_USED insert into m1 (a) values ((select max(a) from t3, t1)); ---error ER_UPDATE_TABLE_USED insert into m1 (a) values ((select max(a) from t3, t2)); ---error ER_UPDATE_TABLE_USED insert into m1 (a) values ((select max(a) from tmp, m1)); ---error ER_UPDATE_TABLE_USED insert into m1 (a) values ((select max(a) from tmp, m2)); ---error ER_UPDATE_TABLE_USED insert into m1 (a) values ((select max(a) from tmp, t1)); ---error ER_UPDATE_TABLE_USED insert into m1 (a) values ((select max(a) from tmp, t2)); - ---error ER_VIEW_PREVENT_UPDATE + insert into m1 (a) values ((select max(a) from v1)); ---error ER_VIEW_PREVENT_UPDATE insert into m1 (a) values ((select max(a) from tmp, v1)); +select count(*) from m1; drop view v1; diff --git a/mysql-test/main/subselect.result b/mysql-test/main/subselect.result index 6f110d1d603..e8b2f7eea31 100644 --- a/mysql-test/main/subselect.result +++ b/mysql-test/main/subselect.result @@ -679,22 +679,24 @@ create table t3 (b int); insert into t2 values (1); insert into t3 values (1),(2); INSERT INTO t1 (x) VALUES ((SELECT x FROM t1)); -ERROR HY000: Table 't1' is specified twice, both as a target for 'INSERT' and as a separate source for data INSERT INTO t1 (x) VALUES ((SELECT b FROM t3)); ERROR 21000: Subquery returns more than 1 row INSERT INTO t1 (x) VALUES ((SELECT a FROM t2)); select * from t1; x +NULL 1 insert into t2 values (1); INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(a) FROM t2)); select * from t1; x +NULL 1 2 INSERT INTO t1 (x) select (SELECT SUM(a)+1 FROM t2) FROM t2; select * from t1; x +NULL 1 2 3 @@ -702,6 +704,7 @@ x INSERT INTO t1 (x) select (SELECT SUM(x)+2 FROM t1) FROM t2; select * from t1; x +NULL 1 2 3 @@ -711,6 +714,7 @@ x INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(a) FROM t2)); select * from t1; x +NULL 1 2 3 @@ -727,7 +731,7 @@ insert into t3 values (1),(2); select * from t1; x y replace into t1 (x, y) VALUES ((SELECT x FROM t1), (SELECT a+1 FROM t2)); -ERROR HY000: Table 't1' is specified twice, both as a target for 'INSERT' and as a separate source for data +ERROR 23000: Column 'x' cannot be null replace into t1 (x, y) VALUES ((SELECT a FROM t3), (SELECT a+1 FROM t2)); ERROR 21000: Subquery returns more than 1 row replace into t1 (x, y) VALUES ((SELECT a FROM t2), (SELECT a+1 FROM t2)); @@ -795,13 +799,21 @@ SELECT * FROM t2 WHERE id IN (SELECT 5 UNION SELECT 2); id 2 INSERT INTO t2 VALUES ((SELECT * FROM t2)); -ERROR HY000: Table 't2' is specified twice, both as a target for 'INSERT' and as a separate source for data +ERROR 21000: Subquery returns more than 1 row INSERT INTO t2 VALUES ((SELECT id FROM t2)); -ERROR HY000: Table 't2' is specified twice, both as a target for 'INSERT' and as a separate source for data +ERROR 21000: Subquery returns more than 1 row +select * from t2; +id +1 +2 +INSERT INTO t2 VALUES ((SELECT count(*) FROM t2)); +INSERT INTO t2 VALUES ((SELECT max(id) FROM t2)); SELECT * FROM t2; id 1 2 +2 +2 CREATE TABLE t1 (id int(11) default NULL, KEY id (id)) ENGINE=MyISAM CHARSET=latin1; INSERT INTO t1 values (1),(1); UPDATE t2 SET id=(SELECT * FROM t1); diff --git a/mysql-test/main/subselect.test b/mysql-test/main/subselect.test index 89e7d18b2b2..9c4f69177e7 100644 --- a/mysql-test/main/subselect.test +++ b/mysql-test/main/subselect.test @@ -415,7 +415,6 @@ create table t2 (a int) ENGINE=MyISAM; create table t3 (b int); insert into t2 values (1); insert into t3 values (1),(2); --- error ER_UPDATE_TABLE_USED INSERT INTO t1 (x) VALUES ((SELECT x FROM t1)); -- error ER_SUBQUERY_NO_1_ROW INSERT INTO t1 (x) VALUES ((SELECT b FROM t3)); @@ -450,7 +449,7 @@ create table t3 (a int); insert into t2 values (1); insert into t3 values (1),(2); select * from t1; --- error ER_UPDATE_TABLE_USED +-- error ER_BAD_NULL_ERROR replace into t1 (x, y) VALUES ((SELECT x FROM t1), (SELECT a+1 FROM t2)); -- error ER_SUBQUERY_NO_1_ROW replace into t1 (x, y) VALUES ((SELECT a FROM t3), (SELECT a+1 FROM t2)); @@ -486,10 +485,13 @@ EXPLAIN EXTENDED SELECT * FROM t2 WHERE id IN (SELECT 1+(select 1)); EXPLAIN EXTENDED SELECT * FROM t2 WHERE id IN (SELECT 1 UNION SELECT 3); SELECT * FROM t2 WHERE id IN (SELECT 5 UNION SELECT 3); SELECT * FROM t2 WHERE id IN (SELECT 5 UNION SELECT 2); --- error ER_UPDATE_TABLE_USED +-- error ER_SUBQUERY_NO_1_ROW INSERT INTO t2 VALUES ((SELECT * FROM t2)); --- error ER_UPDATE_TABLE_USED +-- error ER_SUBQUERY_NO_1_ROW INSERT INTO t2 VALUES ((SELECT id FROM t2)); +select * from t2; +INSERT INTO t2 VALUES ((SELECT count(*) FROM t2)); +INSERT INTO t2 VALUES ((SELECT max(id) FROM t2)); SELECT * FROM t2; CREATE TABLE t1 (id int(11) default NULL, KEY id (id)) ENGINE=MyISAM CHARSET=latin1; INSERT INTO t1 values (1),(1); diff --git a/mysql-test/main/subselect_elimination.result b/mysql-test/main/subselect_elimination.result index 17400e490bc..e2bbbe1b6f2 100644 --- a/mysql-test/main/subselect_elimination.result +++ b/mysql-test/main/subselect_elimination.result @@ -136,12 +136,22 @@ DROP TABLE t1; # access within null pointer CREATE TABLE x (x INT) ENGINE=InnoDB; INSERT INTO x (x) VALUES (0); +select NULL IN (SELECT (SELECT x FROM (SELECT x FROM +(SELECT 0 IN (SELECT x=0 FROM (SELECT x FROM (SELECT (SELECT (SELECT (SELECT +(SELECT 0 AS x) FROM x AS x) IN (SELECT 0 AS x) AS x) FROM x AS x) IN +(SELECT x WHERE x=0) AS x FROM x AS x) AS x) AS x GROUP BY x) AS x FROM x) AS x) +AS x) IN (SELECT 0 AS x) AS x FROM x) as exp; +exp +NULL INSERT INTO x (x) VALUES (x IN (SELECT (SELECT x FROM (SELECT x FROM (SELECT 0 IN (SELECT x=0 FROM (SELECT x FROM (SELECT (SELECT (SELECT (SELECT (SELECT 0 AS x) FROM x AS x) IN (SELECT 0 AS x) AS x) FROM x AS x) IN (SELECT x WHERE x=0) AS x FROM x AS x) AS x) AS x GROUP BY x) AS x FROM x) AS x) AS x) IN (SELECT 0 AS x) AS x FROM x)); -ERROR HY000: Table 'x' is specified twice, both as a target for 'INSERT' and as a separate source for data +select * from x; +x +0 +NULL DROP TABLE x; # MDEV-28622: Item_subselect eliminated flag set but Item still # evaluated/used. diff --git a/mysql-test/main/subselect_elimination.test b/mysql-test/main/subselect_elimination.test index 9d973477b28..e2b5b50f36d 100644 --- a/mysql-test/main/subselect_elimination.test +++ b/mysql-test/main/subselect_elimination.test @@ -133,12 +133,17 @@ DROP TABLE t1; CREATE TABLE x (x INT) ENGINE=InnoDB; INSERT INTO x (x) VALUES (0); ---error ER_UPDATE_TABLE_USED +select NULL IN (SELECT (SELECT x FROM (SELECT x FROM +(SELECT 0 IN (SELECT x=0 FROM (SELECT x FROM (SELECT (SELECT (SELECT (SELECT +(SELECT 0 AS x) FROM x AS x) IN (SELECT 0 AS x) AS x) FROM x AS x) IN +(SELECT x WHERE x=0) AS x FROM x AS x) AS x) AS x GROUP BY x) AS x FROM x) AS x) +AS x) IN (SELECT 0 AS x) AS x FROM x) as exp; INSERT INTO x (x) VALUES (x IN (SELECT (SELECT x FROM (SELECT x FROM (SELECT 0 IN (SELECT x=0 FROM (SELECT x FROM (SELECT (SELECT (SELECT (SELECT (SELECT 0 AS x) FROM x AS x) IN (SELECT 0 AS x) AS x) FROM x AS x) IN (SELECT x WHERE x=0) AS x FROM x AS x) AS x) AS x GROUP BY x) AS x FROM x) AS x) AS x) IN (SELECT 0 AS x) AS x FROM x)); +select * from x; DROP TABLE x; --echo # MDEV-28622: Item_subselect eliminated flag set but Item still diff --git a/mysql-test/main/subselect_no_exists_to_in.result b/mysql-test/main/subselect_no_exists_to_in.result index 860cc252073..3df77e3ba9a 100644 --- a/mysql-test/main/subselect_no_exists_to_in.result +++ b/mysql-test/main/subselect_no_exists_to_in.result @@ -683,22 +683,24 @@ create table t3 (b int); insert into t2 values (1); insert into t3 values (1),(2); INSERT INTO t1 (x) VALUES ((SELECT x FROM t1)); -ERROR HY000: Table 't1' is specified twice, both as a target for 'INSERT' and as a separate source for data INSERT INTO t1 (x) VALUES ((SELECT b FROM t3)); ERROR 21000: Subquery returns more than 1 row INSERT INTO t1 (x) VALUES ((SELECT a FROM t2)); select * from t1; x +NULL 1 insert into t2 values (1); INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(a) FROM t2)); select * from t1; x +NULL 1 2 INSERT INTO t1 (x) select (SELECT SUM(a)+1 FROM t2) FROM t2; select * from t1; x +NULL 1 2 3 @@ -706,6 +708,7 @@ x INSERT INTO t1 (x) select (SELECT SUM(x)+2 FROM t1) FROM t2; select * from t1; x +NULL 1 2 3 @@ -715,6 +718,7 @@ x INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(a) FROM t2)); select * from t1; x +NULL 1 2 3 @@ -731,7 +735,7 @@ insert into t3 values (1),(2); select * from t1; x y replace into t1 (x, y) VALUES ((SELECT x FROM t1), (SELECT a+1 FROM t2)); -ERROR HY000: Table 't1' is specified twice, both as a target for 'INSERT' and as a separate source for data +ERROR 23000: Column 'x' cannot be null replace into t1 (x, y) VALUES ((SELECT a FROM t3), (SELECT a+1 FROM t2)); ERROR 21000: Subquery returns more than 1 row replace into t1 (x, y) VALUES ((SELECT a FROM t2), (SELECT a+1 FROM t2)); @@ -799,13 +803,21 @@ SELECT * FROM t2 WHERE id IN (SELECT 5 UNION SELECT 2); id 2 INSERT INTO t2 VALUES ((SELECT * FROM t2)); -ERROR HY000: Table 't2' is specified twice, both as a target for 'INSERT' and as a separate source for data +ERROR 21000: Subquery returns more than 1 row INSERT INTO t2 VALUES ((SELECT id FROM t2)); -ERROR HY000: Table 't2' is specified twice, both as a target for 'INSERT' and as a separate source for data +ERROR 21000: Subquery returns more than 1 row +select * from t2; +id +1 +2 +INSERT INTO t2 VALUES ((SELECT count(*) FROM t2)); +INSERT INTO t2 VALUES ((SELECT max(id) FROM t2)); SELECT * FROM t2; id 1 2 +2 +2 CREATE TABLE t1 (id int(11) default NULL, KEY id (id)) ENGINE=MyISAM CHARSET=latin1; INSERT INTO t1 values (1),(1); UPDATE t2 SET id=(SELECT * FROM t1); diff --git a/mysql-test/main/subselect_no_mat.result b/mysql-test/main/subselect_no_mat.result index c4b306c6f06..721d4f5fdf9 100644 --- a/mysql-test/main/subselect_no_mat.result +++ b/mysql-test/main/subselect_no_mat.result @@ -686,22 +686,24 @@ create table t3 (b int); insert into t2 values (1); insert into t3 values (1),(2); INSERT INTO t1 (x) VALUES ((SELECT x FROM t1)); -ERROR HY000: Table 't1' is specified twice, both as a target for 'INSERT' and as a separate source for data INSERT INTO t1 (x) VALUES ((SELECT b FROM t3)); ERROR 21000: Subquery returns more than 1 row INSERT INTO t1 (x) VALUES ((SELECT a FROM t2)); select * from t1; x +NULL 1 insert into t2 values (1); INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(a) FROM t2)); select * from t1; x +NULL 1 2 INSERT INTO t1 (x) select (SELECT SUM(a)+1 FROM t2) FROM t2; select * from t1; x +NULL 1 2 3 @@ -709,6 +711,7 @@ x INSERT INTO t1 (x) select (SELECT SUM(x)+2 FROM t1) FROM t2; select * from t1; x +NULL 1 2 3 @@ -718,6 +721,7 @@ x INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(a) FROM t2)); select * from t1; x +NULL 1 2 3 @@ -734,7 +738,7 @@ insert into t3 values (1),(2); select * from t1; x y replace into t1 (x, y) VALUES ((SELECT x FROM t1), (SELECT a+1 FROM t2)); -ERROR HY000: Table 't1' is specified twice, both as a target for 'INSERT' and as a separate source for data +ERROR 23000: Column 'x' cannot be null replace into t1 (x, y) VALUES ((SELECT a FROM t3), (SELECT a+1 FROM t2)); ERROR 21000: Subquery returns more than 1 row replace into t1 (x, y) VALUES ((SELECT a FROM t2), (SELECT a+1 FROM t2)); @@ -802,13 +806,21 @@ SELECT * FROM t2 WHERE id IN (SELECT 5 UNION SELECT 2); id 2 INSERT INTO t2 VALUES ((SELECT * FROM t2)); -ERROR HY000: Table 't2' is specified twice, both as a target for 'INSERT' and as a separate source for data +ERROR 21000: Subquery returns more than 1 row INSERT INTO t2 VALUES ((SELECT id FROM t2)); -ERROR HY000: Table 't2' is specified twice, both as a target for 'INSERT' and as a separate source for data +ERROR 21000: Subquery returns more than 1 row +select * from t2; +id +1 +2 +INSERT INTO t2 VALUES ((SELECT count(*) FROM t2)); +INSERT INTO t2 VALUES ((SELECT max(id) FROM t2)); SELECT * FROM t2; id 1 2 +2 +2 CREATE TABLE t1 (id int(11) default NULL, KEY id (id)) ENGINE=MyISAM CHARSET=latin1; INSERT INTO t1 values (1),(1); UPDATE t2 SET id=(SELECT * FROM t1); diff --git a/mysql-test/main/subselect_no_opts.result b/mysql-test/main/subselect_no_opts.result index d286d7ed015..6afdeb6fc8f 100644 --- a/mysql-test/main/subselect_no_opts.result +++ b/mysql-test/main/subselect_no_opts.result @@ -682,22 +682,24 @@ create table t3 (b int); insert into t2 values (1); insert into t3 values (1),(2); INSERT INTO t1 (x) VALUES ((SELECT x FROM t1)); -ERROR HY000: Table 't1' is specified twice, both as a target for 'INSERT' and as a separate source for data INSERT INTO t1 (x) VALUES ((SELECT b FROM t3)); ERROR 21000: Subquery returns more than 1 row INSERT INTO t1 (x) VALUES ((SELECT a FROM t2)); select * from t1; x +NULL 1 insert into t2 values (1); INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(a) FROM t2)); select * from t1; x +NULL 1 2 INSERT INTO t1 (x) select (SELECT SUM(a)+1 FROM t2) FROM t2; select * from t1; x +NULL 1 2 3 @@ -705,6 +707,7 @@ x INSERT INTO t1 (x) select (SELECT SUM(x)+2 FROM t1) FROM t2; select * from t1; x +NULL 1 2 3 @@ -714,6 +717,7 @@ x INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(a) FROM t2)); select * from t1; x +NULL 1 2 3 @@ -730,7 +734,7 @@ insert into t3 values (1),(2); select * from t1; x y replace into t1 (x, y) VALUES ((SELECT x FROM t1), (SELECT a+1 FROM t2)); -ERROR HY000: Table 't1' is specified twice, both as a target for 'INSERT' and as a separate source for data +ERROR 23000: Column 'x' cannot be null replace into t1 (x, y) VALUES ((SELECT a FROM t3), (SELECT a+1 FROM t2)); ERROR 21000: Subquery returns more than 1 row replace into t1 (x, y) VALUES ((SELECT a FROM t2), (SELECT a+1 FROM t2)); @@ -798,13 +802,21 @@ SELECT * FROM t2 WHERE id IN (SELECT 5 UNION SELECT 2); id 2 INSERT INTO t2 VALUES ((SELECT * FROM t2)); -ERROR HY000: Table 't2' is specified twice, both as a target for 'INSERT' and as a separate source for data +ERROR 21000: Subquery returns more than 1 row INSERT INTO t2 VALUES ((SELECT id FROM t2)); -ERROR HY000: Table 't2' is specified twice, both as a target for 'INSERT' and as a separate source for data +ERROR 21000: Subquery returns more than 1 row +select * from t2; +id +1 +2 +INSERT INTO t2 VALUES ((SELECT count(*) FROM t2)); +INSERT INTO t2 VALUES ((SELECT max(id) FROM t2)); SELECT * FROM t2; id 1 2 +2 +2 CREATE TABLE t1 (id int(11) default NULL, KEY id (id)) ENGINE=MyISAM CHARSET=latin1; INSERT INTO t1 values (1),(1); UPDATE t2 SET id=(SELECT * FROM t1); diff --git a/mysql-test/main/subselect_no_scache.result b/mysql-test/main/subselect_no_scache.result index 3975ccc5193..80e9a003b5e 100644 --- a/mysql-test/main/subselect_no_scache.result +++ b/mysql-test/main/subselect_no_scache.result @@ -685,22 +685,24 @@ create table t3 (b int); insert into t2 values (1); insert into t3 values (1),(2); INSERT INTO t1 (x) VALUES ((SELECT x FROM t1)); -ERROR HY000: Table 't1' is specified twice, both as a target for 'INSERT' and as a separate source for data INSERT INTO t1 (x) VALUES ((SELECT b FROM t3)); ERROR 21000: Subquery returns more than 1 row INSERT INTO t1 (x) VALUES ((SELECT a FROM t2)); select * from t1; x +NULL 1 insert into t2 values (1); INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(a) FROM t2)); select * from t1; x +NULL 1 2 INSERT INTO t1 (x) select (SELECT SUM(a)+1 FROM t2) FROM t2; select * from t1; x +NULL 1 2 3 @@ -708,6 +710,7 @@ x INSERT INTO t1 (x) select (SELECT SUM(x)+2 FROM t1) FROM t2; select * from t1; x +NULL 1 2 3 @@ -717,6 +720,7 @@ x INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(a) FROM t2)); select * from t1; x +NULL 1 2 3 @@ -733,7 +737,7 @@ insert into t3 values (1),(2); select * from t1; x y replace into t1 (x, y) VALUES ((SELECT x FROM t1), (SELECT a+1 FROM t2)); -ERROR HY000: Table 't1' is specified twice, both as a target for 'INSERT' and as a separate source for data +ERROR 23000: Column 'x' cannot be null replace into t1 (x, y) VALUES ((SELECT a FROM t3), (SELECT a+1 FROM t2)); ERROR 21000: Subquery returns more than 1 row replace into t1 (x, y) VALUES ((SELECT a FROM t2), (SELECT a+1 FROM t2)); @@ -801,13 +805,21 @@ SELECT * FROM t2 WHERE id IN (SELECT 5 UNION SELECT 2); id 2 INSERT INTO t2 VALUES ((SELECT * FROM t2)); -ERROR HY000: Table 't2' is specified twice, both as a target for 'INSERT' and as a separate source for data +ERROR 21000: Subquery returns more than 1 row INSERT INTO t2 VALUES ((SELECT id FROM t2)); -ERROR HY000: Table 't2' is specified twice, both as a target for 'INSERT' and as a separate source for data +ERROR 21000: Subquery returns more than 1 row +select * from t2; +id +1 +2 +INSERT INTO t2 VALUES ((SELECT count(*) FROM t2)); +INSERT INTO t2 VALUES ((SELECT max(id) FROM t2)); SELECT * FROM t2; id 1 2 +2 +2 CREATE TABLE t1 (id int(11) default NULL, KEY id (id)) ENGINE=MyISAM CHARSET=latin1; INSERT INTO t1 values (1),(1); UPDATE t2 SET id=(SELECT * FROM t1); diff --git a/mysql-test/main/subselect_no_semijoin.result b/mysql-test/main/subselect_no_semijoin.result index fab8ad0e2cc..8f05b95cb8c 100644 --- a/mysql-test/main/subselect_no_semijoin.result +++ b/mysql-test/main/subselect_no_semijoin.result @@ -682,22 +682,24 @@ create table t3 (b int); insert into t2 values (1); insert into t3 values (1),(2); INSERT INTO t1 (x) VALUES ((SELECT x FROM t1)); -ERROR HY000: Table 't1' is specified twice, both as a target for 'INSERT' and as a separate source for data INSERT INTO t1 (x) VALUES ((SELECT b FROM t3)); ERROR 21000: Subquery returns more than 1 row INSERT INTO t1 (x) VALUES ((SELECT a FROM t2)); select * from t1; x +NULL 1 insert into t2 values (1); INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(a) FROM t2)); select * from t1; x +NULL 1 2 INSERT INTO t1 (x) select (SELECT SUM(a)+1 FROM t2) FROM t2; select * from t1; x +NULL 1 2 3 @@ -705,6 +707,7 @@ x INSERT INTO t1 (x) select (SELECT SUM(x)+2 FROM t1) FROM t2; select * from t1; x +NULL 1 2 3 @@ -714,6 +717,7 @@ x INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(a) FROM t2)); select * from t1; x +NULL 1 2 3 @@ -730,7 +734,7 @@ insert into t3 values (1),(2); select * from t1; x y replace into t1 (x, y) VALUES ((SELECT x FROM t1), (SELECT a+1 FROM t2)); -ERROR HY000: Table 't1' is specified twice, both as a target for 'INSERT' and as a separate source for data +ERROR 23000: Column 'x' cannot be null replace into t1 (x, y) VALUES ((SELECT a FROM t3), (SELECT a+1 FROM t2)); ERROR 21000: Subquery returns more than 1 row replace into t1 (x, y) VALUES ((SELECT a FROM t2), (SELECT a+1 FROM t2)); @@ -798,13 +802,21 @@ SELECT * FROM t2 WHERE id IN (SELECT 5 UNION SELECT 2); id 2 INSERT INTO t2 VALUES ((SELECT * FROM t2)); -ERROR HY000: Table 't2' is specified twice, both as a target for 'INSERT' and as a separate source for data +ERROR 21000: Subquery returns more than 1 row INSERT INTO t2 VALUES ((SELECT id FROM t2)); -ERROR HY000: Table 't2' is specified twice, both as a target for 'INSERT' and as a separate source for data +ERROR 21000: Subquery returns more than 1 row +select * from t2; +id +1 +2 +INSERT INTO t2 VALUES ((SELECT count(*) FROM t2)); +INSERT INTO t2 VALUES ((SELECT max(id) FROM t2)); SELECT * FROM t2; id 1 2 +2 +2 CREATE TABLE t1 (id int(11) default NULL, KEY id (id)) ENGINE=MyISAM CHARSET=latin1; INSERT INTO t1 values (1),(1); UPDATE t2 SET id=(SELECT * FROM t1); diff --git a/mysql-test/main/view.result b/mysql-test/main/view.result index 1686a6c0026..a1b78dcb551 100644 --- a/mysql-test/main/view.result +++ b/mysql-test/main/view.result @@ -944,31 +944,19 @@ create view v1 as select * from t1; create view v2 as select * from v1; create view v3 as select v2.col1 from v2,t2 where v2.col1 = t2.col1; insert into v2 values ((select max(col1) from v1)); -ERROR HY000: The definition of table 'v1' prevents operation INSERT on table 'v2' insert into t1 values ((select max(col1) from v1)); -ERROR HY000: The definition of table 'v1' prevents operation INSERT on table 't1' insert into v2 values ((select max(col1) from v1)); -ERROR HY000: The definition of table 'v1' prevents operation INSERT on table 'v2' insert into v2 values ((select max(col1) from t1)); -ERROR HY000: The definition of table 'v2' prevents operation INSERT on table 'v2' insert into t1 values ((select max(col1) from t1)); -ERROR HY000: Table 't1' is specified twice, both as a target for 'INSERT' and as a separate source for data insert into v2 values ((select max(col1) from t1)); -ERROR HY000: The definition of table 'v2' prevents operation INSERT on table 'v2' insert into v2 values ((select max(col1) from v2)); -ERROR HY000: Table 'v2' is specified twice, both as a target for 'INSERT' and as a separate source for data insert into t1 values ((select max(col1) from v2)); -ERROR HY000: The definition of table 'v2' prevents operation INSERT on table 't1' insert into v2 values ((select max(col1) from v2)); -ERROR HY000: Table 'v2' is specified twice, both as a target for 'INSERT' and as a separate source for data insert into v3 (col1) values ((select max(col1) from v1)); -ERROR HY000: The definition of table 'v1' prevents operation INSERT on table 'v3' insert into v3 (col1) values ((select max(col1) from t1)); -ERROR HY000: The definition of table 'v3' prevents operation INSERT on table 'v3' insert into v3 (col1) values ((select max(col1) from v2)); -ERROR HY000: The definition of table 'v2' prevents operation INSERT on table 'v3' -insert into v3 (col1) values ((select CONVERT_TZ('20050101000000','UTC','MET') from v2)); -ERROR HY000: The definition of table 'v2' prevents operation INSERT on table 'v3' +insert into v3 (col1) values ((select CONVERT_TZ('20050101000000','UTC','MET') from v2 LIMIT 1)); +ERROR 22003: Out of range value for column 'col1' at row 2 insert into v3 (col1) values ((select CONVERT_TZ('20050101000000','UTC','MET') from t2)); insert into t3 values ((select CONVERT_TZ('20050101000000','UTC','MET') from t2)); ERROR 23000: Column 'col1' cannot be null @@ -978,6 +966,18 @@ insert into t1 (col1) values ((select max(col1) from v4)); select * from t1; col1 NULL +NULL +NULL +NULL +NULL +NULL +NULL +NULL +NULL +NULL +NULL +NULL +NULL 1 2 3 @@ -1332,9 +1332,26 @@ create view v3 as select * from t1 where 20 < (select (s1) from v2); insert into v3 values (30); ERROR HY000: The target table v3 of the INSERT is not insertable-into create view v4 as select * from v2 where 20 < (select (s1) from t1); +select * from t1; +s1 insert into v4 values (30); -ERROR HY000: The target table v4 of the INSERT is not insertable-into -drop view v4, v3, v2, v1; +select * from t1; +s1 +30 +create view v5 as select * from v2 where s1 < (select min(s1) from t1) WITH CHECK OPTION; +# can't insert only less then minimum +insert into v5 values (40); +ERROR 44000: CHECK OPTION failed `test`.`v5` +# allow insert the new minimum +insert into v5 values (10); +# always emply view (can't be something less than minimum) +select * from v5; +s1 +select * from t1; +s1 +30 +10 +drop view v5, v4, v3, v2, v1; drop table t1; create table t1 (a int); create view v1 as select * from t1; diff --git a/mysql-test/main/view.test b/mysql-test/main/view.test index 3c6117c0e36..37c5783c347 100644 --- a/mysql-test/main/view.test +++ b/mysql-test/main/view.test @@ -865,33 +865,21 @@ create table t3 (col1 datetime not null); create view v1 as select * from t1; create view v2 as select * from v1; create view v3 as select v2.col1 from v2,t2 where v2.col1 = t2.col1; --- error ER_VIEW_PREVENT_UPDATE insert into v2 values ((select max(col1) from v1)); --- error ER_VIEW_PREVENT_UPDATE insert into t1 values ((select max(col1) from v1)); --- error ER_VIEW_PREVENT_UPDATE insert into v2 values ((select max(col1) from v1)); --- error ER_VIEW_PREVENT_UPDATE insert into v2 values ((select max(col1) from t1)); --- error ER_UPDATE_TABLE_USED insert into t1 values ((select max(col1) from t1)); --- error ER_VIEW_PREVENT_UPDATE insert into v2 values ((select max(col1) from t1)); --- error ER_UPDATE_TABLE_USED insert into v2 values ((select max(col1) from v2)); --- error ER_VIEW_PREVENT_UPDATE insert into t1 values ((select max(col1) from v2)); --- error ER_UPDATE_TABLE_USED insert into v2 values ((select max(col1) from v2)); --- error ER_VIEW_PREVENT_UPDATE insert into v3 (col1) values ((select max(col1) from v1)); --- error ER_VIEW_PREVENT_UPDATE insert into v3 (col1) values ((select max(col1) from t1)); --- error ER_VIEW_PREVENT_UPDATE insert into v3 (col1) values ((select max(col1) from v2)); # check with TZ tables in list --- error ER_VIEW_PREVENT_UPDATE -insert into v3 (col1) values ((select CONVERT_TZ('20050101000000','UTC','MET') from v2)); +--error ER_WARN_DATA_OUT_OF_RANGE +insert into v3 (col1) values ((select CONVERT_TZ('20050101000000','UTC','MET') from v2 LIMIT 1)); insert into v3 (col1) values ((select CONVERT_TZ('20050101000000','UTC','MET') from t2)); -- error ER_BAD_NULL_ERROR insert into t3 values ((select CONVERT_TZ('20050101000000','UTC','MET') from t2)); @@ -1209,9 +1197,19 @@ create view v3 as select * from t1 where 20 < (select (s1) from v2); -- error ER_NON_INSERTABLE_TABLE insert into v3 values (30); create view v4 as select * from v2 where 20 < (select (s1) from t1); --- error ER_NON_INSERTABLE_TABLE +select * from t1; insert into v4 values (30); -drop view v4, v3, v2, v1; +select * from t1; +create view v5 as select * from v2 where s1 < (select min(s1) from t1) WITH CHECK OPTION; +--echo # can't insert only less then minimum +--error ER_VIEW_CHECK_FAILED +insert into v5 values (40); +--echo # allow insert the new minimum +insert into v5 values (10); +--echo # always emply view (can't be something less than minimum) +select * from v5; +select * from t1; +drop view v5, v4, v3, v2, v1; drop table t1; # diff --git a/mysql-test/suite/sql_sequence/other.result b/mysql-test/suite/sql_sequence/other.result index b950a7d8b13..247eb5b30a8 100644 --- a/mysql-test/suite/sql_sequence/other.result +++ b/mysql-test/suite/sql_sequence/other.result @@ -48,7 +48,6 @@ create sequence s2; insert into s1 (next_not_cached_value, minimum_value) values (100,1000); ERROR HY000: Field 'maximum_value' doesn't have a default value insert into s1 values (next value for s1, 1,9223372036854775806,1,1,1000,0,0); -ERROR HY000: Table 's1' is specified twice, both as a target for 'INSERT' and as a separate source for data insert into s1 values(1000,9223372036854775806,1,1,1,1000,0,0); ERROR HY000: Sequence 'test.s1' has out of range value for options insert into s1 values(0,9223372036854775806,1,1,1,1000,0,0); diff --git a/mysql-test/suite/sql_sequence/other.test b/mysql-test/suite/sql_sequence/other.test index 9761e4268a8..f727f4856fb 100644 --- a/mysql-test/suite/sql_sequence/other.test +++ b/mysql-test/suite/sql_sequence/other.test @@ -38,7 +38,6 @@ create sequence s1; create sequence s2; --error ER_NO_DEFAULT_FOR_FIELD insert into s1 (next_not_cached_value, minimum_value) values (100,1000); ---error ER_UPDATE_TABLE_USED insert into s1 values (next value for s1, 1,9223372036854775806,1,1,1000,0,0); --error ER_SEQUENCE_INVALID_DATA insert into s1 values(1000,9223372036854775806,1,1,1,1000,0,0); diff --git a/sql/item.h b/sql/item.h index d60d5c01985..ac351e94dfb 100644 --- a/sql/item.h +++ b/sql/item.h @@ -723,6 +723,17 @@ public: virtual const String *const_ptr_string() const { return NULL; } }; +struct subselect_table_finder_param +{ + THD *thd; + /* + We're searching for different TABLE_LIST objects referring to the same + table as this one + */ + const TABLE_LIST *find; + /* NUL - not found, ERROR_TABLE - search error, or the found table reference */ + TABLE_LIST *dup; +}; /****************************************************************************/ @@ -2068,6 +2079,7 @@ public: set_extraction_flag(*(int*)arg); return 0; } + virtual bool subselect_table_finder_processor(void *arg) { return 0; }; /* TRUE if the expression depends only on the table indicated by tab_map diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 05e98071947..05ea91c3cb1 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -7078,3 +7078,27 @@ void Subq_materialization_tracker::report_partial_merge_keys( for (uint i= 0; i < merge_keys_count; i++) partial_match_array_sizes[i]= merge_keys[i]->get_key_buff_elements(); } + + +/* + Check if somewhere inside this subselect we read the table. This means a + full read "(SELECT ... FROM tbl)", outside reference to tbl.column does not + count +*/ + +bool +Item_subselect::subselect_table_finder_processor(void *arg) +{ + subselect_table_finder_param *param= (subselect_table_finder_param *)arg; + for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select()) + { + TABLE_LIST *dup; + if ((dup= sl->find_table(param->thd, ¶m->find->db, + ¶m->find->table_name))) + { + param->dup= dup; + return TRUE; + } + } + return FALSE; +}; diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 7c4ca91bfac..7eb3b8b17b8 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -277,6 +277,7 @@ public: { return TRUE; } + bool subselect_table_finder_processor(void *arg) override; void register_as_with_rec_ref(With_element *with_elem); void init_expr_cache_tracker(THD *thd); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 8feb5cf57da..53663a6b90e 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -19,6 +19,7 @@ #include "mariadb.h" #include "sql_base.h" // setup_table_map +#include "sql_list.h" #include "sql_priv.h" #include "unireg.h" #include "debug_sync.h" @@ -1122,7 +1123,6 @@ TABLE_LIST* find_dup_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, t_name= &table->table_name; t_alias= &table->alias; -retry: DBUG_PRINT("info", ("real table: %s.%s", d_name->str, t_name->str)); for (TABLE_LIST *tl= table_list; tl ; tl= tl->next_global, res= 0) { @@ -1184,37 +1184,53 @@ retry: DBUG_PRINT("info", ("found same copy of table or table which we should skip")); } - /* - If we've found a duplicate in a derived table, try to work around that. - - For INSERT...SELECT, do not do any workarounds, return the duplicate. The - caller will enable buffering to handle this. - */ - if (res && res->belong_to_derived && - !(check_flag & CHECK_DUP_FOR_INSERT_SELECT)) - { - /* - We come here for queries like this: - - INSERT INTO t1 VALUES ((SELECT tmp.a FROM (select * FROM t1))); - DELETE FROM t1 WHERE ( ... (SELECT ... FROM t1) ) ; - - Try to fix by materializing the derived table - */ - TABLE_LIST *derived= res->belong_to_derived; - if (derived->is_merged_derived() && !derived->derived->is_excluded()) - { - DBUG_PRINT("info", - ("convert merged to materialization to resolve the conflict")); - derived->change_refs_to_fields(); - derived->set_materialized_derived(); - goto retry; - } - } DBUG_RETURN(res); } +TABLE_LIST* unique_table_in_select_list(THD *thd, TABLE_LIST *table, SELECT_LEX *sel) +{ + subselect_table_finder_param param= {thd, table, NULL}; + List_iterator_fast it(sel->item_list); + Item *item; + while ((item= it++)) + { + if (item->walk(&Item::subselect_table_finder_processor, FALSE, ¶m)) + { + if (param.dup == NULL) + return ERROR_TABLE; + return param.dup; + } + DBUG_ASSERT(param.dup == NULL); + } + return NULL; +} + + +typedef TABLE_LIST* (*find_table_callback)(THD *thd, TABLE_LIST *table, + TABLE_LIST *table_list, + uint check_flag, SELECT_LEX *sel); + +static +TABLE_LIST* +find_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, + uint check_flag, SELECT_LEX *sel, find_table_callback callback ); + +TABLE_LIST* unique_table_callback(THD *thd, TABLE_LIST *table, + TABLE_LIST *table_list, + uint check_flag, SELECT_LEX *sel) +{ + return find_dup_table(thd, table, table_list, check_flag); +} + + +TABLE_LIST* unique_in_sel_table_callback(THD *thd, TABLE_LIST *table, + TABLE_LIST *table_list, + uint check_flag, SELECT_LEX *sel) +{ + return unique_table_in_select_list(thd, table, sel); +} + /** Test that the subject table of INSERT/UPDATE/DELETE/CREATE or (in case of MyISAMMRG) one of its children are not used later @@ -1233,6 +1249,25 @@ retry: TABLE_LIST* unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, uint check_flag) +{ + return find_table(thd, table, table_list, check_flag, NULL, + &unique_table_callback); +} + + +TABLE_LIST* +unique_table_in_insert_returning_subselect(THD *thd, TABLE_LIST *table, SELECT_LEX *sel) +{ + return find_table(thd, table, NULL, 0, sel, + &unique_in_sel_table_callback); + +} + + +static +TABLE_LIST* +find_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, + uint check_flag, SELECT_LEX *sel, find_table_callback callback ) { TABLE_LIST *dup; @@ -1264,12 +1299,12 @@ unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, if (!tmp_parent) break; - if ((dup= find_dup_table(thd, child, child->next_global, check_flag))) + if ((dup= (*callback)(thd, child, child->next_global, check_flag, sel))) break; } } else - dup= find_dup_table(thd, table, table_list, check_flag); + dup= (*callback)(thd, table, table_list, check_flag, sel); return dup; } diff --git a/sql/sql_base.h b/sql/sql_base.h index 9ec464df19a..e07600d88ba 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -72,7 +72,6 @@ enum find_item_error_report_type {REPORT_ALL_ERRORS, REPORT_EXCEPT_NOT_FOUND, #define CHECK_DUP_ALLOW_DIFFERENT_ALIAS 1 #define CHECK_DUP_FOR_CREATE 2 #define CHECK_DUP_SKIP_TEMP_TABLE 4 -#define CHECK_DUP_FOR_INSERT_SELECT 8 uint get_table_def_key(const TABLE_LIST *table_list, const char **key); TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update, @@ -290,6 +289,8 @@ bool open_and_lock_internal_tables(TABLE *table, bool lock); bool lock_tables(THD *thd, TABLE_LIST *tables, uint counter, uint flags); int decide_logging_format(THD *thd, TABLE_LIST *tables); void close_thread_table(THD *thd, TABLE **table_ptr); +TABLE_LIST* +unique_table_in_insert_returning_subselect(THD *thd, TABLE_LIST *table, SELECT_LEX *sel); TABLE_LIST *unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, uint check_flag); bool is_equal(const LEX_CSTRING *a, const LEX_CSTRING *b); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 8f52924a4d5..db02fb4875d 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -57,6 +57,7 @@ */ #include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "sql_list.h" #include "sql_priv.h" #include "sql_insert.h" #include "sql_update.h" // compare_record @@ -714,6 +715,8 @@ bool mysql_insert(THD *thd, TABLE_LIST *table_list, Name_resolution_context_state ctx_state; SELECT_LEX *returning= thd->lex->has_returning() ? thd->lex->returning() : 0; unsigned char *readbuff= NULL; + List insert_values_cache; + bool cache_insert_values= FALSE; #ifndef EMBEDDED_LIBRARY char *query= thd->query(); @@ -771,7 +774,7 @@ bool mysql_insert(THD *thd, TABLE_LIST *table_list, if ((res= mysql_prepare_insert(thd, table_list, fields, values, update_fields, update_values, duplic, - &unused_conds, FALSE))) + &unused_conds, FALSE, &cache_insert_values))) { retval= thd->is_error(); if (res < 0) @@ -1000,8 +1003,42 @@ bool mysql_insert(THD *thd, TABLE_LIST *table_list, restore_record(table,s->default_values); // Get empty record thd->reconsider_logging_format_for_iodup(table); } + + if (cache_insert_values) + { + insert_values_cache.empty(); + while ((values= its++)) + { + List *caches= new (thd->mem_root) List_item; + List_iterator_fast iv(*values); + Item *item; + if (caches == 0) + { + error= 1; + goto values_loop_end; + } + caches->empty(); + while((item= iv++)) + { + Item_cache *cache= item->get_cache(thd); + if (!cache) + { + error= 1; + goto values_loop_end; + } + cache->setup(thd, item); + caches->push_back(cache); + } + insert_values_cache.push_back(caches); + } + its.rewind(); + } + do { + List_iterator_fast itc(insert_values_cache); + List_iterator_fast *itr; + DBUG_PRINT("info", ("iteration %llu", iteration)); if (iteration && bulk_parameters_set(thd)) { @@ -1009,7 +1046,24 @@ bool mysql_insert(THD *thd, TABLE_LIST *table_list, goto values_loop_end; } - while ((values= its++)) + if (cache_insert_values) + { + List_item *caches; + while ((caches= itc++)) + { + List_iterator_fast ic(*caches); + Item_cache *cache; + while((cache= (Item_cache*) ic++)) + { + cache->cache_value(); + } + } + itc.rewind(); + itr= &itc; + } + else + itr= &its; + while ((values= (*itr)++)) { if (fields.elements || !value_count) { @@ -1112,7 +1166,7 @@ bool mysql_insert(THD *thd, TABLE_LIST *table_list, break; thd->get_stmt_da()->inc_current_row_for_warning(); } - its.rewind(); + itr->rewind(); iteration++; } while (bulk_parameters_iterations(thd)); @@ -1601,6 +1655,7 @@ static void prepare_for_positional_update(TABLE *table, TABLE_LIST *tables) table_list Global/local table list where Where clause (for insert ... select) select_insert TRUE if INSERT ... SELECT statement + cache_insert_values insert's VALUES(...) has to be pre-computed TODO (in far future) In cases of: @@ -1622,7 +1677,7 @@ int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, List &fields, List_item *values, List &update_fields, List &update_values, enum_duplicates duplic, COND **where, - bool select_insert) + bool select_insert, bool * const cache_insert_values) { SELECT_LEX *select_lex= thd->lex->first_select_lex(); Name_resolution_context *context= &select_lex->context; @@ -1712,11 +1767,12 @@ int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, /* Check if we read from the same table we're inserting into. - Queries like INSERT INTO t1 VALUES ((SELECT ... FROM t1...)) are not - allowed. + Queries like INSERT INTO t1 VALUES ((SELECT ... FROM t1...)) have + to pre-compute the VALUES part. + Reading from the same table in the RETURNING clause is not allowed. - INSERT...SELECT is an exception: it will detect this case and use - buffering to handle it correctly. + INSERT...SELECT detects this case in select_insert::prepare and also + uses buffering to handle it correcly. */ if (!select_insert) { @@ -1725,10 +1781,30 @@ int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, if ((duplicate= unique_table(thd, table_list, table_list->next_global, CHECK_DUP_ALLOW_DIFFERENT_ALIAS))) { - update_non_unique_table_error(table_list, "INSERT", duplicate); - DBUG_RETURN(1); + /* + This is INSERT INTO ... VALUES (...) and it must pre-compute the + values to be inserted. + */ + (*cache_insert_values)= true; } + else + (*cache_insert_values)= false; + select_lex->fix_prepare_information(thd, &fake_conds, &fake_conds); + + if ((*cache_insert_values) && thd->lex->has_returning()) + { + // Check if the table we're inserting into is also in RETURNING clause + TABLE_LIST *dup= + unique_table_in_insert_returning_subselect(thd, table_list, + thd->lex->returning()); + if (dup) + { + if (dup != ERROR_TABLE) + update_non_unique_table_error(table_list, "INSERT", duplicate); + DBUG_RETURN(1); + } + } } /* Only call prepare_for_posistion() if we are not performing a DELAYED @@ -3857,6 +3933,7 @@ int mysql_insert_select_prepare(THD *thd, select_result *sel_res) int res; LEX *lex= thd->lex; SELECT_LEX *select_lex= lex->first_select_lex(); + bool cache_insert_values= false; DBUG_ENTER("mysql_insert_select_prepare"); /* @@ -3867,7 +3944,7 @@ int mysql_insert_select_prepare(THD *thd, select_result *sel_res) if ((res= mysql_prepare_insert(thd, lex->query_tables, lex->field_list, 0, lex->update_list, lex->value_list, lex->duplicates, - &select_lex->where, TRUE))) + &select_lex->where, TRUE, &cache_insert_values))) DBUG_RETURN(res); /* @@ -4050,8 +4127,7 @@ select_insert::prepare(List &values, SELECT_LEX_UNIT *u) Is table which we are changing used somewhere in other parts of query */ - if (unique_table(thd, table_list, table_list->next_global, - CHECK_DUP_FOR_INSERT_SELECT)) + if (unique_table(thd, table_list, table_list->next_global, 0)) { /* Using same table for INSERT and SELECT */ lex->current_select->options|= OPTION_BUFFER_RESULT; diff --git a/sql/sql_insert.h b/sql/sql_insert.h index 9aa234b715e..e6dc13d928c 100644 --- a/sql/sql_insert.h +++ b/sql/sql_insert.h @@ -27,7 +27,7 @@ int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, List &fields, List_item *values, List &update_fields, List &update_values, enum_duplicates duplic, - COND **where, bool select_insert); + COND **where, bool select_insert, bool * const cache_results); bool mysql_insert(THD *thd,TABLE_LIST *table,List &fields, List &values, List &update_fields, List &update_values, enum_duplicates flag, diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index eb57cb8050b..b53988706ae 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -12047,3 +12047,45 @@ bool SELECT_LEX_UNIT::explainable() const derived->is_materialized_derived() : // (3) false; } + +/** + Find the real table in prepared SELECT tree + + NOTE: all SELECT must be prepared (to have leaf table list). + + NOTE: it looks only for real tables (not view or derived) + + @param thd the current thread handle + @param db_name name of db of the table to look for + @param db_name name of db of the table to look for + + @return first found table, NULL or ERROR_TABLE +*/ + +TABLE_LIST *SELECT_LEX::find_table(THD *thd, + const LEX_CSTRING *db_name, + const LEX_CSTRING *table_name) +{ + uchar buff[STACK_BUFF_ALLOC]; // Max argument in function + if (check_stack_overrun(thd, STACK_MIN_SIZE, buff)) + return NULL; + + List_iterator_fast ti(leaf_tables); + TABLE_LIST *table; + while ((table= ti++)) + { + if (cmp(&table->db, db_name) == 0 && + cmp(&table->table_name, table_name) == 0) + return table; + } + + for (SELECT_LEX_UNIT *u= first_inner_unit(); u; u= u->next_unit()) + { + for (st_select_lex *sl= u->first_select(); sl; sl=sl->next_select()) + { + if ((table= sl->find_table(thd, db_name, table_name))) + return table; + } + } + return NULL; +} diff --git a/sql/sql_lex.h b/sql/sql_lex.h index f1c3e041c7a..0f493a345e4 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1666,6 +1666,10 @@ public: void lex_start(LEX *plex); bool is_unit_nest() { return (nest_flags & UNIT_NEST_FL); } void mark_as_unit_nest() { nest_flags= UNIT_NEST_FL; } + + TABLE_LIST *find_table(THD *thd, + const LEX_CSTRING *db_name, + const LEX_CSTRING *table_name); }; typedef class st_select_lex SELECT_LEX; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index c102e2f734e..28e734105cd 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1287,6 +1287,7 @@ static bool mysql_test_insert(Prepared_statement *stmt, THD *thd= stmt->thd; List_iterator_fast its(values_list); List_item *values; + bool cache_results= FALSE; DBUG_ENTER("mysql_test_insert"); /* @@ -1330,7 +1331,8 @@ static bool mysql_test_insert(Prepared_statement *stmt, } if (mysql_prepare_insert(thd, table_list, fields, values, update_fields, - update_values, duplic, &unused_conds, FALSE)) + update_values, duplic, &unused_conds, FALSE, + &cache_results)) goto error; value_count= values->elements; diff --git a/sql/table.h b/sql/table.h index eeec8b05022..07715363e0a 100644 --- a/sql/table.h +++ b/sql/table.h @@ -3023,6 +3023,8 @@ private: ulong m_table_ref_version; }; +#define ERROR_TABLE ((TABLE_LIST*) 0x1) + class Item; /* diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index f06566cf208..5fabbd96a48 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -22626,6 +22626,164 @@ static void test_mdev_34958() rc= mysql_query(mysql, "DROP TABLE t1, t2"); myquery(rc); } + +/* Server crash when inserting from derived table containing insert target table */ +static void test_mdev_32086() +{ + int rc; + MYSQL_STMT *stmt_insert; + MYSQL_BIND bind[2]; + MYSQL_RES *result; + MYSQL_ROW row; + unsigned int vals[] = { 123, 124}; + unsigned int vals_array_len = 2; + const char *insert_stmt= "\ +insert into t1 values(\ + (select 101+count(*)\ + from\ + (\ + select dt2.id\ + from (select id from t1) dt2, t1 t where t.id=dt2.id\ + ) dt\ + where dt.id<1000\ + ), ?\ +)"; + + /* Set up test's environment */ + + + rc= mysql_query(mysql, "create table t1 (pk int, id int);"); + myquery(rc); + + rc= mysql_query(mysql, "insert into t1 values (2,2), (3,3), (4,4);"); + myquery(rc); + + stmt_insert = mysql_stmt_init(mysql); + if (!stmt_insert) + { + fprintf(stderr, "mysql_stmt_init failed: Error: %s\n", + mysql_error(mysql)); + exit(1); + } + + rc= mysql_stmt_prepare(stmt_insert, insert_stmt, strlen(insert_stmt)); + if (rc) + { + fprintf(stderr, "mysql_stmt_prepare failed: %s\n", + mysql_stmt_error(stmt_insert)); + exit(1); + } + + memset(&bind[0], 0, sizeof(MYSQL_BIND)); + + bind[0].buffer_type= MYSQL_TYPE_LONG; + bind[0].buffer= vals; + + rc= mysql_stmt_attr_set(stmt_insert, STMT_ATTR_ARRAY_SIZE, &vals_array_len); + if (rc) + { + fprintf(stderr, "mysql_stmt_prepare failed: %s\n", + mysql_stmt_error(stmt_insert)); + exit(1); + } + + rc= mysql_stmt_bind_param(stmt_insert, bind); + if (rc) + { + fprintf(stderr, "mysql_stmt_bind_param failed: %s\n", + mysql_stmt_error(stmt_insert)); + exit(1); + } + + rc= mysql_stmt_execute(stmt_insert); + if (rc) + { + fprintf(stderr, "mysql_stmt_execute failed: %s\n", + mysql_stmt_error(stmt_insert)); + exit(1); + } + + /* + pk id + 2 2 + 3 3 + 4 4 + 104 123 + 104 124 + */ + rc= mysql_query(mysql, "select * from t1"); + if (rc) + { + fprintf(stderr, "Query failed: %s\n", mysql_error(mysql)); + } + result= mysql_store_result(mysql); + row= mysql_fetch_row(result); + DIE_UNLESS(atoi(row[0]) == 2 && atoi(row[1]) == 2); + row= mysql_fetch_row(result); + DIE_UNLESS(atoi(row[0]) == 3 && atoi(row[1]) == 3); + row= mysql_fetch_row(result); + DIE_UNLESS(atoi(row[0]) == 4 && atoi(row[1]) == 4); + row= mysql_fetch_row(result); + printf("\n %d, %d \n", atoi(row[0]), atoi(row[1])); + DIE_UNLESS(atoi(row[0]) == 104 && atoi(row[1]) == 123); + row= mysql_fetch_row(result); + printf("\n %d, %d \n", atoi(row[0]), atoi(row[1])); + DIE_UNLESS(atoi(row[0]) == 104 && atoi(row[1]) == 124); + row= mysql_fetch_row(result); + DIE_UNLESS(row == NULL); + + mysql_free_result(result); + + rc= mysql_stmt_execute(stmt_insert); + if (rc) + { + fprintf(stderr, "mysql_stmt_execute failed: %s\n", + mysql_stmt_error(stmt_insert)); + exit(1); + } + /* + pk id + 2 2 + 3 3 + 4 4 + 104 123 + 104 124 + 106 123 + 106 124 + */ + rc= mysql_query(mysql, "select * from t1"); + if (rc) + { + fprintf(stderr, "Query failed: %s\n", mysql_error(mysql)); + } + result= mysql_store_result(mysql); + row= mysql_fetch_row(result); + DIE_UNLESS(atoi(row[0]) == 2 && atoi(row[1]) == 2); + row= mysql_fetch_row(result); + DIE_UNLESS(atoi(row[0]) == 3 && atoi(row[1]) == 3); + row= mysql_fetch_row(result); + DIE_UNLESS(atoi(row[0]) == 4 && atoi(row[1]) == 4); + row= mysql_fetch_row(result); + DIE_UNLESS(atoi(row[0]) == 104 && atoi(row[1]) == 123); + row= mysql_fetch_row(result); + DIE_UNLESS(atoi(row[0]) == 104 && atoi(row[1]) == 124); + row= mysql_fetch_row(result); + printf("\n %d, %d \n", atoi(row[0]), atoi(row[1])); + DIE_UNLESS(atoi(row[0]) == 106 && atoi(row[1]) == 123); + row= mysql_fetch_row(result); + printf("\n %d, %d \n", atoi(row[0]), atoi(row[1])); + DIE_UNLESS(atoi(row[0]) == 106 && atoi(row[1]) == 124); + row= mysql_fetch_row(result); + DIE_UNLESS(row == NULL); + + mysql_free_result(result); + + mysql_stmt_close(stmt_insert); + + /* Clean up */ + rc= mysql_query(mysql, "DROP TABLE t1"); + myquery(rc); +} #endif // EMBEDDED_LIBRARY /* @@ -22980,6 +23138,7 @@ static struct my_tests_st my_tests[]= { { "test_mdev_34718_bd", test_mdev_34718_bd }, { "test_mdev_34718_ad", test_mdev_34718_ad }, { "test_mdev_34958", test_mdev_34958 }, + { "test_mdev_32086", test_mdev_32086 }, #endif { 0, 0 } };