diff options
author | Rucha Deodhar <ruchad1998@gmail.com> | 2019-06-02 00:51:46 +0530 |
---|---|---|
committer | Sergei Golubchik <serg@mariadb.org> | 2019-10-13 20:03:08 +0200 |
commit | 6bf48d3d88e20b274d6801e444aef8150fbf2c7d (patch) | |
tree | a285dd054b4429f8583d63e9f72ad4d66304a805 | |
parent | abd8e14e45d0474edd757e41a9a041ddb08b7a75 (diff) | |
download | mariadb-git-6bf48d3d88e20b274d6801e444aef8150fbf2c7d.tar.gz |
MDEV-10014 Add RETURNING to INSERT
Closes #1384
26 files changed, 1846 insertions, 108 deletions
diff --git a/mysql-test/main/features.result b/mysql-test/main/features.result index beab7fb946f..0da2e5fe986 100644 --- a/mysql-test/main/features.result +++ b/mysql-test/main/features.result @@ -10,6 +10,7 @@ Feature_delay_key_write 0 Feature_dynamic_columns 0 Feature_fulltext 0 Feature_gis 0 +Feature_insert_returning 0 Feature_invisible_columns 0 Feature_json 0 Feature_locale 0 @@ -165,9 +166,24 @@ drop table t1; show status like "feature_delay_key_write"; Variable_name Value Feature_delay_key_write 2 +# +# Feature CHECK CONSTRAINT +# create table t1 (a int check (a > 5)); create table t2 (b int, constraint foo check (b < 10)); drop table t1, t2; show status like "feature_check_constraint"; Variable_name Value Feature_check_constraint 2 +# +# Feature insert...returning +# +create table t1(id1 int); +insert into t1 values (1),(2) returning *; +id1 +1 +2 +drop table t1; +show status like "feature_insert_returning"; +Variable_name Value +Feature_insert_returning 1 diff --git a/mysql-test/main/features.test b/mysql-test/main/features.test index 1241bd50bdd..d0f5263c5c3 100644 --- a/mysql-test/main/features.test +++ b/mysql-test/main/features.test @@ -132,10 +132,18 @@ drop table t1; show status like "feature_delay_key_write"; -# -# Feature CHECK CONSTRAINT -# +--echo # +--echo # Feature CHECK CONSTRAINT +--echo # create table t1 (a int check (a > 5)); create table t2 (b int, constraint foo check (b < 10)); drop table t1, t2; show status like "feature_check_constraint"; + +--echo # +--echo # Feature insert...returning +--echo # +create table t1(id1 int); +insert into t1 values (1),(2) returning *; +drop table t1; +show status like "feature_insert_returning"; diff --git a/mysql-test/main/insert_returning.result b/mysql-test/main/insert_returning.result new file mode 100644 index 00000000000..8928cdfcf41 --- /dev/null +++ b/mysql-test/main/insert_returning.result @@ -0,0 +1,560 @@ +# Test for INSERT...RETURNING +CREATE TABLE t1(id1 INT PRIMARY KEY AUTO_INCREMENT, val1 VARCHAR(1)); +CREATE TABLE t2(id2 INT PRIMARY KEY AUTO_INCREMENT, val2 VARCHAR(1)); +CREATE VIEW v1 AS SELECT id1, val1 FROM t1; +CREATE VIEW v2 AS SELECT id2,val2 FROM t2; +INSERT INTO t2 VALUES (1,'a'),(2,'b'),(3,'c'); +CREATE FUNCTION f(arg INT) RETURNS TEXT +BEGIN +RETURN (SELECT arg+arg); +END| +# +# Simple insert statement...RETURNING +# +INSERT INTO t1 (id1, val1) VALUES (1, 'a'); +INSERT INTO t1 (id1, val1) VALUES (2, 'b') RETURNING *; +id1 val1 +2 b +INSERT INTO t1 (id1, val1) VALUES (3, 'c') RETURNING id1+id1 as total,val1, +id1 && id1, id1 | id1, UPPER(val1),f(id1); +total val1 id1 && id1 id1 | id1 UPPER(val1) f(id1) +6 c 1 3 C 6 +INSERT INTO t1(id1,val1) VALUES (4,'d') RETURNING(SELECT GROUP_CONCAT(val2) +FROM t2 WHERE id2=1); +(SELECT GROUP_CONCAT(val2) +FROM t2 WHERE id2=1) +a +INSERT INTO t1(id1,val1) VALUES(5,'e') RETURNING(SELECT GROUP_CONCAT(val2) +FROM t2 GROUP BY id2 HAVING id2=id2+1); +(SELECT GROUP_CONCAT(val2) +FROM t2 GROUP BY id2 HAVING id2=id2+1) +NULL +PREPARE stmt FROM "INSERT INTO t1 (id1,val1) VALUES (6,'f') RETURNING (SELECT id2 FROM t2 WHERE val2='b')"; +EXECUTE stmt; +(SELECT id2 FROM t2 WHERE val2='b') +2 +DELETE FROM t1 WHERE id1=6; +SELECT * FROM t1; +id1 val1 +1 a +2 b +3 c +4 d +5 e +EXECUTE stmt; +(SELECT id2 FROM t2 WHERE val2='b') +2 +DEALLOCATE PREPARE stmt; +INSERT IGNORE INTO t1(id1,val1) VALUES (7,'h') RETURNING *; +id1 val1 +7 h +SELECT * FROM t1; +id1 val1 +1 a +2 b +3 c +4 d +5 e +6 f +7 h +EXPLAIN INSERT INTO t1(id1,val1) VALUES (8,'i') RETURNING *; +id select_type table type possible_keys key key_len ref rows Extra +1 INSERT t1 ALL NULL NULL NULL NULL NULL NULL +EXPLAIN EXTENDED INSERT INTO t1(id1,val1) VALUES(9,'j') RETURNING id1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 INSERT t1 ALL NULL NULL NULL NULL NULL 100.00 NULL +EXPLAIN FORMAT="json" INSERT INTO t1(id1,val1) VALUES (10,'k') RETURNING id1; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "t1" + } + } +} +SELECT * FROM t1; +id1 val1 +1 a +2 b +3 c +4 d +5 e +6 f +7 h +INSERT INTO v1(id1, val1) VALUES (12, 'a') RETURNING id1+id1 as total,val1, +id1 && id1, id1 id1, UPPER(val1),f(id1); +total val1 id1 && id1 id1 UPPER(val1) f(id1) +24 a 1 12 A 24 +ANALYZE INSERT INTO t1(id1,val1) VALUES(13,'l') RETURNING *; +id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra +1 INSERT t1 ALL NULL NULL NULL NULL NULL NULL 100.00 100.00 NULL +TRUNCATE TABLE t1; +# +# Multiple values in one insert statement...RETURNING +# +INSERT INTO t1 VALUES (1,'a'),(2,'b'); +INSERT INTO t1 VALUES (3,'c'),(4,'d') RETURNING *; +id1 val1 +3 c +4 d +INSERT INTO t1 VALUES (5,'e'),(6,'f') RETURNING id1 as id,val1, +id1 && id1, id1|id1, UPPER(val1),f(id1); +id val1 id1 && id1 id1|id1 UPPER(val1) f(id1) +5 e 1 5 E 10 +6 f 1 6 F 12 +INSERT INTO t1 VALUES (7,'g'),(8,'h') RETURNING(SELECT GROUP_CONCAT(val2) +FROM t2 WHERE id2=1); +(SELECT GROUP_CONCAT(val2) +FROM t2 WHERE id2=1) +a +a +INSERT INTO t1 VALUES (9,'i'),(10,'j') RETURNING(SELECT GROUP_CONCAT(val2) +FROM t2 GROUP BY id2 HAVING id2=id2+1); +(SELECT GROUP_CONCAT(val2) +FROM t2 GROUP BY id2 HAVING id2=id2+1) +NULL +NULL +PREPARE stmt FROM "INSERT INTO t1 VALUES (11,'k'),(12,'l') RETURNING (SELECT id2 FROM t2 WHERE val2='b')"; +EXECUTE stmt; +(SELECT id2 FROM t2 WHERE val2='b') +2 +2 +DELETE FROM t1 WHERE val1 IN ('k','l'); +SELECT * FROM t1; +id1 val1 +1 a +2 b +3 c +4 d +5 e +6 f +7 g +8 h +9 i +10 j +EXECUTE stmt; +(SELECT id2 FROM t2 WHERE val2='b') +2 +2 +DEALLOCATE PREPARE stmt; +INSERT IGNORE INTO t1 VALUES(13,'o'),(14,'p') RETURNING *; +id1 val1 +13 o +14 p +EXPLAIN INSERT INTO t1 VALUES(15,'q'),(16,'r') RETURNING *; +id select_type table type possible_keys key key_len ref rows Extra +1 INSERT t1 ALL NULL NULL NULL NULL NULL NULL +EXPLAIN EXTENDED INSERT INTO t1 VALUES (17,'s'),(18,'t') RETURNING *; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 INSERT t1 ALL NULL NULL NULL NULL NULL 100.00 NULL +EXPLAIN FORMAT="json" INSERT INTO t1 VALUES(19,'u'),(20,'v') RETURNING id1; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "t1" + } + } +} +SELECT * FROM t1; +id1 val1 +1 a +2 b +3 c +4 d +5 e +6 f +7 g +8 h +9 i +10 j +12 l +11 k +13 o +14 p +INSERT INTO v1 VALUES(23,'y'),(24,'z') RETURNING id1 as id,val1, +id1 && id1, id1|id1, UPPER(val1),f(id1); +id val1 id1 && id1 id1|id1 UPPER(val1) f(id1) +23 y 1 23 Y 46 +24 z 1 24 Z 48 +ANALYZE INSERT INTO t1 VALUES(25,'a'),(26,'b') RETURNING *; +id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra +1 INSERT t1 ALL NULL NULL NULL NULL NULL NULL 100.00 100.00 NULL +# +# INSERT...ON DUPLICATE KEY UPDATE...RETURNING +# +CREATE TABLE ins_duplicate (id INT PRIMARY KEY AUTO_INCREMENT, val VARCHAR(1)); +INSERT INTO ins_duplicate VALUES (1,'a'); +INSERT INTO ins_duplicate VALUES (2,'b') ON DUPLICATE KEY UPDATE val='b' +RETURNING *; +id val +2 b +INSERT INTO ins_duplicate VALUES (2,'b') ON DUPLICATE KEY UPDATE val='c' +RETURNING id+id AS total, val, id && id, id|id, UPPER(val),f(id); +total val id && id id|id UPPER(val) f(id) +4 c 1 2 C 4 +INSERT INTO ins_duplicate VALUES (2,'b') ON DUPLICATE KEY UPDATE val='d' +RETURNING (SELECT GROUP_CONCAT(val2) FROM t2 WHERE id2=1); +(SELECT GROUP_CONCAT(val2) FROM t2 WHERE id2=1) +a +INSERT INTO ins_duplicate VALUES (2,'b') ON DUPLICATE KEY UPDATE val='e' +RETURNING (SELECT GROUP_CONCAT(val2) FROM t2 GROUP BY id2 HAVING id2=id2+1); +(SELECT GROUP_CONCAT(val2) FROM t2 GROUP BY id2 HAVING id2=id2+1) +NULL +PREPARE stmt FROM "INSERT INTO ins_duplicate VALUES (2,'b') ON DUPLICATE +KEY UPDATE val='f' RETURNING (SELECT id2 FROM t2 WHERE val2='b')"; +EXECUTE stmt; +(SELECT id2 FROM t2 WHERE val2='b') +2 +SELECT * FROM t2; +id2 val2 +1 a +2 b +3 c +EXECUTE stmt; +(SELECT id2 FROM t2 WHERE val2='b') +2 +DEALLOCATE PREPARE stmt; +INSERT IGNORE INTO ins_duplicate(id,val) VALUES (3,'c'),(4,'d') ON DUPLICATE +KEY UPDATE val='g' RETURNING id; +id +3 +4 +EXPLAIN INSERT INTO ins_duplicate(id,val) VALUES (2,'b') ON DUPLICATE KEY +UPDATE val='h' RETURNING val; +id select_type table type possible_keys key key_len ref rows Extra +1 INSERT ins_duplicate ALL NULL NULL NULL NULL NULL NULL +EXPLAIN EXTENDED INSERT INTO ins_duplicate(id,val) VALUES (2,'b') +ON DUPLICATE KEY UPDATE val='i' RETURNING val; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 INSERT ins_duplicate ALL NULL NULL NULL NULL NULL 100.00 NULL +EXPLAIN FORMAT="json" INSERT INTO ins_duplicate(id,val) VALUES (2,'b') +ON DUPLICATE KEY UPDATE val='j' RETURNING val; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "ins_duplicate" + } + } +} +INSERT INTO v1(id1, val1) VALUES (2,'d') ON DUPLICATE KEY UPDATE +val1='d' RETURNING id1+id1 AS total, val1, id1 && id1, id1|id1, UPPER(val1), +f(id1); +total val1 id1 && id1 id1|id1 UPPER(val1) f(id1) +4 d 1 2 D 4 +ANALYZE INSERT INTO ins_duplicate(id,val) VALUES(2,'b') ON DUPLICATE KEY UPDATE +val='k' RETURNING *; +id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra +1 INSERT ins_duplicate ALL NULL NULL NULL NULL NULL NULL 100.00 100.00 NULL +SELECT * FROM ins_duplicate; +id val +1 a +2 k +3 c +4 d +# +# INSERT...SET...RETURNING +# +TRUNCATE TABLE t1; +INSERT INTO t1 SET id1= 1, val1= 'a'; +INSERT INTO t1 SET id1= 2, val1= 'b' RETURNING *; +id1 val1 +2 b +INSERT INTO t1 SET id1= 3, val1= 'c' RETURNING id1+id1 AS total,val1, +id1 && id1, id1|id1, UPPER(val1),f(id1); +total val1 id1 && id1 id1|id1 UPPER(val1) f(id1) +6 c 1 3 C 6 +INSERT INTO t1 SET id1= 4, val1= 'd' RETURNING (SELECT GROUP_CONCAT(val2) +FROM t2 WHERE id2=1); +(SELECT GROUP_CONCAT(val2) +FROM t2 WHERE id2=1) +a +INSERT INTO t1 SET id1= 5, val1='e' RETURNING (SELECT GROUP_CONCAT(val2) +FROM t2 GROUP BY id2 HAVING id2=id2+1); +(SELECT GROUP_CONCAT(val2) +FROM t2 GROUP BY id2 HAVING id2=id2+1) +NULL +PREPARE stmt FROM "INSERT INTO t1 SET id1= 6, val1='f' RETURNING (SELECT id2 FROM t2 WHERE val2='b')"; +EXECUTE stmt; +(SELECT id2 FROM t2 WHERE val2='b') +2 +DELETE FROM t1 WHERE val1='f'; +SELECT * FROM t1; +id1 val1 +1 a +2 b +3 c +4 d +5 e +EXECUTE stmt; +(SELECT id2 FROM t2 WHERE val2='b') +2 +DEALLOCATE PREPARE stmt; +INSERT INTO t1 SET id1= 7, val1= 'g' RETURNING f(id1); +f(id1) +14 +INSERT INTO t1 SET val1= 'n' RETURNING *; +id1 val1 +8 n +INSERT IGNORE INTO t1 SET id1= 8, val1= 'h' RETURNING *; +id1 val1 +8 h +Warnings: +Warning 1062 Duplicate entry '8' for key 'PRIMARY' +EXPLAIN INSERT INTO t1 SET id1=9, val1='i' RETURNING id1; +id select_type table type possible_keys key key_len ref rows Extra +1 INSERT t1 ALL NULL NULL NULL NULL NULL NULL +EXPLAIN EXTENDED INSERT INTO t1 SET id1=10, val1='j' RETURNING val1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 INSERT t1 ALL NULL NULL NULL NULL NULL 100.00 NULL +EXPLAIN FORMAT="json" INSERT INTO t1 SET id1=11, val1='k' RETURNING val1; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "t1" + } + } +} +INSERT INTO v1 SET id1=26, val1='Z' RETURNING id1+id1 AS total,val1, +id1 && id1, id1|id1, UPPER(val1),f(id1); +total val1 id1 && id1 id1|id1 UPPER(val1) f(id1) +52 Z 1 26 Z 52 +ANALYZE INSERT INTO t1 SET id1=12, val1='l' RETURNING *; +id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra +1 INSERT t1 ALL NULL NULL NULL NULL NULL NULL 100.00 100.00 NULL +SELECT * FROM t1; +id1 val1 +1 a +2 b +3 c +4 d +5 e +6 f +7 g +8 n +26 Z +12 l +# +# INSERT...SELECT...RETURNING +# +TRUNCATE TABLE t2; +INSERT INTO t2(id2,val2) SELECT * FROM t1; +TRUNCATE TABLE t2; +INSERT INTO t2 SELECT * FROM t1 WHERE id1=1 RETURNING *; +id2 val2 +1 a +INSERT INTO t2 SELECT * FROM t1 WHERE id1=2 RETURNING id2+id2 AS total, +val2,id2 && id2, id2|id2, UPPER(val2),f(id2); +total val2 id2 && id2 id2|id2 UPPER(val2) f(id2) +4 b 1 2 B 4 +INSERT INTO t2 SELECT * FROM t1 WHERE id1=3 RETURNING (SELECT GROUP_CONCAT(val1) FROM t1 GROUP BY id1 HAVING id1=id1+1); +(SELECT GROUP_CONCAT(val1) FROM t1 GROUP BY id1 HAVING id1=id1+1) +NULL +PREPARE stmt FROM "INSERT INTO t2 SELECT * FROM t1 WHERE id1=4 RETURNING (SELECT id1 FROM t1 WHERE val1='b')"; +EXECUTE stmt; +(SELECT id1 FROM t1 WHERE val1='b') +2 +DELETE FROM t2 WHERE id2=4; +SELECT * FROM t1; +id1 val1 +1 a +2 b +3 c +4 d +5 e +6 f +7 g +8 n +26 Z +12 l +EXECUTE stmt; +(SELECT id1 FROM t1 WHERE val1='b') +2 +DEALLOCATE PREPARE stmt; +INSERT INTO t2 SELECT * FROM t1 WHERE id1=6 RETURNING +(SELECT id1+id2 FROM t1 WHERE id1=1); +(SELECT id1+id2 FROM t1 WHERE id1=1) +7 +INSERT INTO t2 SELECT * FROM t1 WHERE id1=7 RETURNING f(id2); +f(id2) +14 +EXPLAIN INSERT INTO t2 SELECT * FROM t1 WHERE id1=8 RETURNING id2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 const PRIMARY PRIMARY 4 const 1 +EXPLAIN EXTENDED INSERT INTO t1 SELECT * FROM t1 WHERE id1=9 RETURNING val1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +EXPLAIN FORMAT="json" INSERT INTO t1 SELECT * FROM t1 WHERE id1=10 RETURNING val1; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "message": "Impossible WHERE noticed after reading const tables" + } + } +} +INSERT IGNORE INTO t2 SELECT * FROM t1 WHERE id1=8 RETURNING *; +id2 val2 +8 n +INSERT IGNORE INTO v2 SELECT * FROM v1 RETURNING *; +id2 val2 +1 a +2 b +3 c +4 d +5 e +6 f +7 g +8 n +26 Z +12 l +Warnings: +Warning 1062 Duplicate entry '1' for key 'PRIMARY' +Warning 1062 Duplicate entry '2' for key 'PRIMARY' +Warning 1062 Duplicate entry '3' for key 'PRIMARY' +Warning 1062 Duplicate entry '4' for key 'PRIMARY' +Warning 1062 Duplicate entry '6' for key 'PRIMARY' +Warning 1062 Duplicate entry '7' for key 'PRIMARY' +Warning 1062 Duplicate entry '8' for key 'PRIMARY' +ANALYZE INSERT INTO t2 SELECT * FROM t1 WHERE id1=11 RETURNING *; +id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra +1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL no matching row in const table +SELECT * FROM t2; +id2 val2 +1 a +2 b +3 c +4 d +6 f +7 g +8 n +5 e +26 Z +12 l +DROP TABLE t1; +DROP TABLE t2; +DROP TABLE ins_duplicate; +# +# Error message test +# +CREATE TABLE t1(id1 INT,val1 VARCHAR(1)); +CREATE TABLE t2(id2 INT,val2 VARCHAR(1)); +CREATE TABLE ins_duplicate (id INT PRIMARY KEY, val VARCHAR(1)); +INSERT INTO t1 VALUES(1,'a'),(2,'b'),(3,'c'); +# +# SIMLPE INSERT STATEMENT +# +INSERT INTO t2(id2,val2) VALUES(1,'a') RETURNING id1; +ERROR 42S22: Unknown column 'id1' in 'field list' +INSERT INTO t2(id2,val2) values(2,'b') RETURNING SUM(id2); +ERROR HY000: Invalid use of group function +INSERT INTO t2(id2,val2) VALUES(3,'c') RETURNING (SELECT id1 FROM t1); +ERROR 21000: Subquery returns more than 1 row +INSERT INTO t2(id2,val2) VALUES(4,'d') RETURNING (SELECT * FROM t1); +ERROR 21000: Operand should contain 1 column(s) +INSERT INTO t2(id2,val2) VALUES(4,'d') RETURNING (SELECT * FROM t2); +ERROR 21000: Operand should contain 1 column(s) +INSERT INTO t2(id2,val2) VALUES(5,'e') RETURNING id2, (SELECT id1+id2 FROM +t1 WHERE id1=1); +id2 (SELECT id1+id2 FROM +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 +# +# Multiple rows in single insert statement +# +INSERT INTO t2 VALUES(1,'a'),(2,'b') RETURNING id1; +ERROR 42S22: Unknown column 'id1' in 'field list' +INSERT INTO t2 VALUES(3,'c'),(4,'d') RETURNING MAX(id2); +ERROR HY000: Invalid use of group function +INSERT INTO t2 VALUES(5,'c'),(6,'f') RETURNING (SELECT id1 FROM t1); +ERROR 21000: Subquery returns more than 1 row +INSERT INTO t2 VALUES(7,'g'),(8,'h') RETURNING (SELECT * FROM t1); +ERROR 21000: Operand should contain 1 column(s) +INSERT INTO t2 VALUES(9,'g'),(10,'h') RETURNING (SELECT * FROM t2); +ERROR 21000: Operand should contain 1 column(s) +INSERT INTO t2 VALUES(11,'e'),(12,'f') RETURNING id2, (SELECT id1+id2 FROM +t1 WHERE id1=1); +id2 (SELECT id1+id2 FROM +t1 WHERE id1=1) +11 12 +12 13 +INSERT INTO t2 VALUES(13,'f'),(14,'g') 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 ... SET +# +INSERT INTO t2 SET id2=1, val2='a' RETURNING id1; +ERROR 42S22: Unknown column 'id1' in 'field list' +INSERT INTO t2 SET id2=2, val2='b' RETURNING COUNT(id2); +ERROR HY000: Invalid use of group function +INSERT INTO t2 SET id2=3, val2='c' RETURNING (SELECT id1 FROM t1); +ERROR 21000: Subquery returns more than 1 row +INSERT INTO t2 SET id2=4, val2='d' RETURNING (SELECT * FROM t1); +ERROR 21000: Operand should contain 1 column(s) +INSERT INTO t2 SET id2=4, val2='d' RETURNING (SELECT * FROM t2); +ERROR 21000: Operand should contain 1 column(s) +INSERT INTO t2 SET id2=5, val2='e' RETURNING id2, (SELECT id1+id2 FROM t1 +WHERE id1=1); +id2 (SELECT id1+id2 FROM t1 +WHERE id1=1) +5 6 +INSERT INTO t2 SET id2=5, val2='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...ON DUPLICATE KEY UPDATE +# +INSERT INTO ins_duplicate VALUES (2,'b') ON DUPLICATE KEY UPDATE val='b' +RETURNING id1; +ERROR 42S22: Unknown column 'id1' in 'field list' +INSERT INTO ins_duplicate VALUES (2,'b') ON DUPLICATE KEY UPDATE val='b' +RETURNING MAX(id); +ERROR HY000: Invalid use of group function +INSERT INTO ins_duplicate VALUES (2,'b') ON DUPLICATE KEY UPDATE val='b' +RETURNING (SELECT id1 FROM t1); +ERROR 21000: Subquery returns more than 1 row +INSERT INTO ins_duplicate VALUES (2,'b') ON DUPLICATE KEY UPDATE val='b' +RETURNING (SELECT * FROM t1); +ERROR 21000: Operand should contain 1 column(s) +INSERT INTO ins_duplicate VALUES (2,'b') ON DUPLICATE KEY UPDATE val='b' +RETURNING (SELECT * FROM ins_duplicate); +ERROR 21000: Operand should contain 1 column(s) +INSERT INTO ins_duplicate VALUES(2,'b') ON DUPLICATE KEY UPDATE val='b' +RETURNING id2, (SELECT id1+id FROM t1 WHERE id1=1); +ERROR 42S22: Unknown column 'id2' in 'field list' +INSERT INTO ins_duplicate VALUES(2,'b') ON DUPLICATE KEY UPDATE val='b' +RETURNING (SELECT id FROM ins_duplicate); +ERROR HY000: Table 'ins_duplicate' is specified twice, both as a target for 'INSERT' and as a separate source for data +# +# INSERT...SELECT +# +INSERT INTO t2(id2, val2) SELECT * FROM t1 WHERE id1=1 RETURNING id1; +ERROR 42S22: Unknown column 'id1' in 'field list' +INSERT INTO t2(id2, val2) SELECT * FROM t1 WHERE id1=2 RETURNING MAX(id2); +ERROR HY000: Invalid use of group function +INSERT INTO t2(id2, val2) SELECT * FROM t1 WHERE id1=2 RETURNING (SELECT +id1 FROM t1); +ERROR 21000: Subquery returns more than 1 row +INSERT INTO t2(id2, val2) SELECT * FROM t1 WHERE id1=2 RETURNING (SELECT +* FROM t1); +ERROR 21000: Operand should contain 1 column(s) +INSERT INTO t2(id2, val2) SELECT * FROM t1 WHERE id1=2 RETURNING(SELECT +* FROM t2); +ERROR 21000: Operand should contain 1 column(s) +INSERT INTO t2(id2,val2) SELECT * FROM t1 WHERE id1=2 RETURNING (SELECT +id2 FROM t2); +ERROR 21000: Subquery returns more than 1 row +DROP TABLE t1; +DROP TABLE t2; +DROP TABLE ins_duplicate; +DROP VIEW v1; +DROP VIEW v2; +DROP FUNCTION f; diff --git a/mysql-test/main/insert_returning.test b/mysql-test/main/insert_returning.test new file mode 100644 index 00000000000..a8c5dc3a3f2 --- /dev/null +++ b/mysql-test/main/insert_returning.test @@ -0,0 +1,280 @@ +--echo # Test for INSERT...RETURNING + +CREATE TABLE t1(id1 INT PRIMARY KEY AUTO_INCREMENT, val1 VARCHAR(1)); +CREATE TABLE t2(id2 INT PRIMARY KEY AUTO_INCREMENT, val2 VARCHAR(1)); +CREATE VIEW v1 AS SELECT id1, val1 FROM t1; +CREATE VIEW v2 AS SELECT id2,val2 FROM t2; +INSERT INTO t2 VALUES (1,'a'),(2,'b'),(3,'c'); + +DELIMITER |; + +CREATE FUNCTION f(arg INT) RETURNS TEXT +BEGIN + RETURN (SELECT arg+arg); +END| + +DELIMITER ;| + +--echo # +--echo # Simple insert statement...RETURNING +--echo # +INSERT INTO t1 (id1, val1) VALUES (1, 'a'); +INSERT INTO t1 (id1, val1) VALUES (2, 'b') RETURNING *; +INSERT INTO t1 (id1, val1) VALUES (3, 'c') RETURNING id1+id1 as total,val1, +id1 && id1, id1 | id1, UPPER(val1),f(id1); +INSERT INTO t1(id1,val1) VALUES (4,'d') RETURNING(SELECT GROUP_CONCAT(val2) +FROM t2 WHERE id2=1); +INSERT INTO t1(id1,val1) VALUES(5,'e') RETURNING(SELECT GROUP_CONCAT(val2) +FROM t2 GROUP BY id2 HAVING id2=id2+1); +PREPARE stmt FROM "INSERT INTO t1 (id1,val1) VALUES (6,'f') RETURNING (SELECT id2 FROM t2 WHERE val2='b')"; +EXECUTE stmt; +DELETE FROM t1 WHERE id1=6; +SELECT * FROM t1; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; +INSERT IGNORE INTO t1(id1,val1) VALUES (7,'h') RETURNING *; +SELECT * FROM t1; +EXPLAIN INSERT INTO t1(id1,val1) VALUES (8,'i') RETURNING *; +EXPLAIN EXTENDED INSERT INTO t1(id1,val1) VALUES(9,'j') RETURNING id1; +EXPLAIN FORMAT="json" INSERT INTO t1(id1,val1) VALUES (10,'k') RETURNING id1; +SELECT * FROM t1; +INSERT INTO v1(id1, val1) VALUES (12, 'a') RETURNING id1+id1 as total,val1, +id1 && id1, id1 id1, UPPER(val1),f(id1); +ANALYZE INSERT INTO t1(id1,val1) VALUES(13,'l') RETURNING *; +TRUNCATE TABLE t1; + +--echo # +--echo # Multiple values in one insert statement...RETURNING +--echo # +INSERT INTO t1 VALUES (1,'a'),(2,'b'); +INSERT INTO t1 VALUES (3,'c'),(4,'d') RETURNING *; +INSERT INTO t1 VALUES (5,'e'),(6,'f') RETURNING id1 as id,val1, +id1 && id1, id1|id1, UPPER(val1),f(id1); +INSERT INTO t1 VALUES (7,'g'),(8,'h') RETURNING(SELECT GROUP_CONCAT(val2) +FROM t2 WHERE id2=1); +INSERT INTO t1 VALUES (9,'i'),(10,'j') RETURNING(SELECT GROUP_CONCAT(val2) +FROM t2 GROUP BY id2 HAVING id2=id2+1); +PREPARE stmt FROM "INSERT INTO t1 VALUES (11,'k'),(12,'l') RETURNING (SELECT id2 FROM t2 WHERE val2='b')"; +EXECUTE stmt; +DELETE FROM t1 WHERE val1 IN ('k','l'); +SELECT * FROM t1; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; +INSERT IGNORE INTO t1 VALUES(13,'o'),(14,'p') RETURNING *; +EXPLAIN INSERT INTO t1 VALUES(15,'q'),(16,'r') RETURNING *; +EXPLAIN EXTENDED INSERT INTO t1 VALUES (17,'s'),(18,'t') RETURNING *; +EXPLAIN FORMAT="json" INSERT INTO t1 VALUES(19,'u'),(20,'v') RETURNING id1; +SELECT * FROM t1; +INSERT INTO v1 VALUES(23,'y'),(24,'z') RETURNING id1 as id,val1, +id1 && id1, id1|id1, UPPER(val1),f(id1); +ANALYZE INSERT INTO t1 VALUES(25,'a'),(26,'b') RETURNING *; + +--echo # +--echo # INSERT...ON DUPLICATE KEY UPDATE...RETURNING +--echo # +CREATE TABLE ins_duplicate (id INT PRIMARY KEY AUTO_INCREMENT, val VARCHAR(1)); +INSERT INTO ins_duplicate VALUES (1,'a'); +INSERT INTO ins_duplicate VALUES (2,'b') ON DUPLICATE KEY UPDATE val='b' +RETURNING *; +INSERT INTO ins_duplicate VALUES (2,'b') ON DUPLICATE KEY UPDATE val='c' +RETURNING id+id AS total, val, id && id, id|id, UPPER(val),f(id); +INSERT INTO ins_duplicate VALUES (2,'b') ON DUPLICATE KEY UPDATE val='d' +RETURNING (SELECT GROUP_CONCAT(val2) FROM t2 WHERE id2=1); +INSERT INTO ins_duplicate VALUES (2,'b') ON DUPLICATE KEY UPDATE val='e' +RETURNING (SELECT GROUP_CONCAT(val2) FROM t2 GROUP BY id2 HAVING id2=id2+1); +PREPARE stmt FROM "INSERT INTO ins_duplicate VALUES (2,'b') ON DUPLICATE +KEY UPDATE val='f' RETURNING (SELECT id2 FROM t2 WHERE val2='b')"; +EXECUTE stmt; +SELECT * FROM t2; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; +INSERT IGNORE INTO ins_duplicate(id,val) VALUES (3,'c'),(4,'d') ON DUPLICATE +KEY UPDATE val='g' RETURNING id; +EXPLAIN INSERT INTO ins_duplicate(id,val) VALUES (2,'b') ON DUPLICATE KEY +UPDATE val='h' RETURNING val; +EXPLAIN EXTENDED INSERT INTO ins_duplicate(id,val) VALUES (2,'b') +ON DUPLICATE KEY UPDATE val='i' RETURNING val; +EXPLAIN FORMAT="json" INSERT INTO ins_duplicate(id,val) VALUES (2,'b') +ON DUPLICATE KEY UPDATE val='j' RETURNING val; +INSERT INTO v1(id1, val1) VALUES (2,'d') ON DUPLICATE KEY UPDATE +val1='d' RETURNING id1+id1 AS total, val1, id1 && id1, id1|id1, UPPER(val1), +f(id1); +ANALYZE INSERT INTO ins_duplicate(id,val) VALUES(2,'b') ON DUPLICATE KEY UPDATE +val='k' RETURNING *; +SELECT * FROM ins_duplicate; + +--echo # +--echo # INSERT...SET...RETURNING +--echo # +TRUNCATE TABLE t1; +INSERT INTO t1 SET id1= 1, val1= 'a'; +INSERT INTO t1 SET id1= 2, val1= 'b' RETURNING *; +INSERT INTO t1 SET id1= 3, val1= 'c' RETURNING id1+id1 AS total,val1, +id1 && id1, id1|id1, UPPER(val1),f(id1); +INSERT INTO t1 SET id1= 4, val1= 'd' RETURNING (SELECT GROUP_CONCAT(val2) +FROM t2 WHERE id2=1); +INSERT INTO t1 SET id1= 5, val1='e' RETURNING (SELECT GROUP_CONCAT(val2) +FROM t2 GROUP BY id2 HAVING id2=id2+1); +PREPARE stmt FROM "INSERT INTO t1 SET id1= 6, val1='f' RETURNING (SELECT id2 FROM t2 WHERE val2='b')"; +EXECUTE stmt; +DELETE FROM t1 WHERE val1='f'; +SELECT * FROM t1; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; +INSERT INTO t1 SET id1= 7, val1= 'g' RETURNING f(id1); +INSERT INTO t1 SET val1= 'n' RETURNING *; +INSERT IGNORE INTO t1 SET id1= 8, val1= 'h' RETURNING *; +EXPLAIN INSERT INTO t1 SET id1=9, val1='i' RETURNING id1; +EXPLAIN EXTENDED INSERT INTO t1 SET id1=10, val1='j' RETURNING val1; +EXPLAIN FORMAT="json" INSERT INTO t1 SET id1=11, val1='k' RETURNING val1; +INSERT INTO v1 SET id1=26, val1='Z' RETURNING id1+id1 AS total,val1, +id1 && id1, id1|id1, UPPER(val1),f(id1); +ANALYZE INSERT INTO t1 SET id1=12, val1='l' RETURNING *; +SELECT * FROM t1; + +--echo # +--echo # INSERT...SELECT...RETURNING +--echo # +TRUNCATE TABLE t2; +INSERT INTO t2(id2,val2) SELECT * FROM t1; +TRUNCATE TABLE t2; +INSERT INTO t2 SELECT * FROM t1 WHERE id1=1 RETURNING *; +INSERT INTO t2 SELECT * FROM t1 WHERE id1=2 RETURNING id2+id2 AS total, +val2,id2 && id2, id2|id2, UPPER(val2),f(id2); +INSERT INTO t2 SELECT * FROM t1 WHERE id1=3 RETURNING (SELECT GROUP_CONCAT(val1) FROM t1 GROUP BY id1 HAVING id1=id1+1); +PREPARE stmt FROM "INSERT INTO t2 SELECT * FROM t1 WHERE id1=4 RETURNING (SELECT id1 FROM t1 WHERE val1='b')"; +EXECUTE stmt; +DELETE FROM t2 WHERE id2=4; +SELECT * FROM t1; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; +INSERT INTO t2 SELECT * FROM t1 WHERE id1=6 RETURNING +(SELECT id1+id2 FROM t1 WHERE id1=1); +INSERT INTO t2 SELECT * FROM t1 WHERE id1=7 RETURNING f(id2); +EXPLAIN INSERT INTO t2 SELECT * FROM t1 WHERE id1=8 RETURNING id2; +EXPLAIN EXTENDED INSERT INTO t1 SELECT * FROM t1 WHERE id1=9 RETURNING val1; +EXPLAIN FORMAT="json" INSERT INTO t1 SELECT * FROM t1 WHERE id1=10 RETURNING val1; +INSERT IGNORE INTO t2 SELECT * FROM t1 WHERE id1=8 RETURNING *; +INSERT IGNORE INTO v2 SELECT * FROM v1 RETURNING *; +ANALYZE INSERT INTO t2 SELECT * FROM t1 WHERE id1=11 RETURNING *; +SELECT * FROM t2; + +DROP TABLE t1; +DROP TABLE t2; +DROP TABLE ins_duplicate; + +--echo # +--echo # Error message test +--echo # + +CREATE TABLE t1(id1 INT,val1 VARCHAR(1)); +CREATE TABLE t2(id2 INT,val2 VARCHAR(1)); +CREATE TABLE ins_duplicate (id INT PRIMARY KEY, val VARCHAR(1)); + +INSERT INTO t1 VALUES(1,'a'),(2,'b'),(3,'c'); + +--echo # +--echo # SIMLPE INSERT STATEMENT +--echo # +--error ER_BAD_FIELD_ERROR +INSERT INTO t2(id2,val2) VALUES(1,'a') RETURNING id1; +--error ER_INVALID_GROUP_FUNC_USE +INSERT INTO t2(id2,val2) values(2,'b') RETURNING SUM(id2); +--error ER_SUBQUERY_NO_1_ROW +INSERT INTO t2(id2,val2) VALUES(3,'c') RETURNING (SELECT id1 FROM t1); +--error ER_OPERAND_COLUMNS +INSERT INTO t2(id2,val2) VALUES(4,'d') RETURNING (SELECT * FROM t1); +--error ER_OPERAND_COLUMNS +INSERT INTO t2(id2,val2) VALUES(4,'d') RETURNING (SELECT * FROM t2); +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); + +--echo # +--echo # Multiple rows in single insert statement +--echo # +--error ER_BAD_FIELD_ERROR +INSERT INTO t2 VALUES(1,'a'),(2,'b') RETURNING id1; +--error ER_INVALID_GROUP_FUNC_USE +INSERT INTO t2 VALUES(3,'c'),(4,'d') RETURNING MAX(id2); +--error ER_SUBQUERY_NO_1_ROW +INSERT INTO t2 VALUES(5,'c'),(6,'f') RETURNING (SELECT id1 FROM t1); +--error ER_OPERAND_COLUMNS +INSERT INTO t2 VALUES(7,'g'),(8,'h') RETURNING (SELECT * FROM t1); +--error ER_OPERAND_COLUMNS +INSERT INTO t2 VALUES(9,'g'),(10,'h') RETURNING (SELECT * FROM t2); +INSERT INTO t2 VALUES(11,'e'),(12,'f') RETURNING id2, (SELECT id1+id2 FROM +t1 WHERE id1=1); +--error ER_UPDATE_TABLE_USED +INSERT INTO t2 VALUES(13,'f'),(14,'g') RETURNING (SELECT id2 FROM t2); + +--echo # +--echo # INSERT ... SET +--echo # +--error ER_BAD_FIELD_ERROR +INSERT INTO t2 SET id2=1, val2='a' RETURNING id1; +--error ER_INVALID_GROUP_FUNC_USE +INSERT INTO t2 SET id2=2, val2='b' RETURNING COUNT(id2); +--error ER_SUBQUERY_NO_1_ROW +INSERT INTO t2 SET id2=3, val2='c' RETURNING (SELECT id1 FROM t1); +--error ER_OPERAND_COLUMNS +INSERT INTO t2 SET id2=4, val2='d' RETURNING (SELECT * FROM t1); +--error ER_OPERAND_COLUMNS +INSERT INTO t2 SET id2=4, val2='d' RETURNING (SELECT * FROM t2); +INSERT INTO t2 SET id2=5, val2='e' RETURNING id2, (SELECT id1+id2 FROM t1 +WHERE id1=1); +--error ER_UPDATE_TABLE_USED +INSERT INTO t2 SET id2=5, val2='f' RETURNING (SELECT id2 FROM t2); + +--echo # +--echo # INSERT...ON DUPLICATE KEY UPDATE +--echo # +--error ER_BAD_FIELD_ERROR +INSERT INTO ins_duplicate VALUES (2,'b') ON DUPLICATE KEY UPDATE val='b' +RETURNING id1; +--error ER_INVALID_GROUP_FUNC_USE +INSERT INTO ins_duplicate VALUES (2,'b') ON DUPLICATE KEY UPDATE val='b' +RETURNING MAX(id); +--error ER_SUBQUERY_NO_1_ROW +INSERT INTO ins_duplicate VALUES (2,'b') ON DUPLICATE KEY UPDATE val='b' +RETURNING (SELECT id1 FROM t1); +--error ER_OPERAND_COLUMNS +INSERT INTO ins_duplicate VALUES (2,'b') ON DUPLICATE KEY UPDATE val='b' +RETURNING (SELECT * FROM t1); +--error ER_OPERAND_COLUMNS +INSERT INTO ins_duplicate VALUES (2,'b') ON DUPLICATE KEY UPDATE val='b' +RETURNING (SELECT * FROM ins_duplicate); +--error ER_BAD_FIELD_ERROR +INSERT INTO ins_duplicate VALUES(2,'b') ON DUPLICATE KEY UPDATE val='b' +RETURNING id2, (SELECT id1+id FROM t1 WHERE id1=1); +--error ER_UPDATE_TABLE_USED +INSERT INTO ins_duplicate VALUES(2,'b') ON DUPLICATE KEY UPDATE val='b' +RETURNING (SELECT id FROM ins_duplicate); + +--echo # +--echo # INSERT...SELECT +--echo # +--error ER_BAD_FIELD_ERROR +INSERT INTO t2(id2, val2) SELECT * FROM t1 WHERE id1=1 RETURNING id1; +--error ER_INVALID_GROUP_FUNC_USE +INSERT INTO t2(id2, val2) SELECT * FROM t1 WHERE id1=2 RETURNING MAX(id2); +--error ER_SUBQUERY_NO_1_ROW +INSERT INTO t2(id2, val2) SELECT * FROM t1 WHERE id1=2 RETURNING (SELECT +id1 FROM t1); +--error ER_OPERAND_COLUMNS +INSERT INTO t2(id2, val2) SELECT * FROM t1 WHERE id1=2 RETURNING (SELECT +* FROM t1); +--error ER_OPERAND_COLUMNS +INSERT INTO t2(id2, val2) SELECT * FROM t1 WHERE id1=2 RETURNING(SELECT +* FROM t2); +--error ER_SUBQUERY_NO_1_ROW +INSERT INTO t2(id2,val2) SELECT * FROM t1 WHERE id1=2 RETURNING (SELECT +id2 FROM t2); + +DROP TABLE t1; +DROP TABLE t2; +DROP TABLE ins_duplicate; +DROP VIEW v1; +DROP VIEW v2; +DROP FUNCTION f; diff --git a/mysql-test/main/insert_returning_datatypes.result b/mysql-test/main/insert_returning_datatypes.result new file mode 100644 index 00000000000..d0516d172f8 --- /dev/null +++ b/mysql-test/main/insert_returning_datatypes.result @@ -0,0 +1,72 @@ +CREATE TABLE t1(num_int1 INT(2) PRIMARY KEY, +num_bit1 BIT(8), +num_float1 FLOAT(5,2), +num_double1 DOUBLE(5,2), +char_enum1 ENUM('A','B','C','D'), +char_set1 SET('a','b','c','d','e'), +str_varchar1 VARCHAR(2), +d1 DATE, +dt1 DATETIME, +ts1 TIMESTAMP, +y1 YEAR, +b1 BOOL); +CREATE TABLE t2(num_int2 INT(2) PRIMARY KEY, +num_bit2 BIT(8), +num_float2 FLOAT(5,2), +num_double2 DOUBLE(5,2), +char_enum2 ENUM('A','B','C','D'), +char_set2 SET('a','b','c','d','e'), +str_varchar2 VARCHAR(2), +d2 DATE, +dt2 DATETIME, +ts2 TIMESTAMP, +y2 YEAR, +b2 BOOL); +# +# SIMLPE INSERT STATEMENT +# +INSERT INTO t1(num_int1,num_bit1,num_float1,num_double1,char_enum1,char_set1, +str_varchar1, d1,dt1,ts1,y1,b1) VALUES(1,b'1000011', 123.45, 123.55, +'A','b,e', 'V','120314',"2012-04-19 13:08:22", '2001-07-22 12:12:12', +2012,0) RETURNING *; +num_int1 num_bit1 num_float1 num_double1 char_enum1 char_set1 str_varchar1 d1 dt1 ts1 y1 b1 +1 C 123.45 123.55 A b,e V 2012-03-14 2012-04-19 13:08:22 2001-07-22 12:12:12 2012 0 +# +# MULTIPLE ROWS IN SINGLE STATEMENT +# +INSERT INTO t1 VALUES(2,b'1000011', 123.45, 123.55, 'A','b,e', 'V', +'120314',"2012-04-19 13:08:22", '2001-07-22 12:12:12',2012,0),(3,b'1000011', +123.45, 123.55, 'A','b,e', 'V','120314',"2012-04-19 13:08:22", +'2001-07-22 12:12:12',2012,1) RETURNING *; +num_int1 num_bit1 num_float1 num_double1 char_enum1 char_set1 str_varchar1 d1 dt1 ts1 y1 b1 +2 C 123.45 123.55 A b,e V 2012-03-14 2012-04-19 13:08:22 2001-07-22 12:12:12 2012 0 +3 C 123.45 123.55 A b,e V 2012-03-14 2012-04-19 13:08:22 2001-07-22 12:12:12 2012 1 +# +# INSERT...SET...RETURNING +# +INSERT INTO t1 SET num_int1=4,num_bit1=b'1000011',num_float1=124.67, +num_double1=231.12,char_enum1='B',char_set1='a,d,e', +str_varchar1='AB',d1='120314',dt1="2012-04-19 13:08:22", +ts1='2001-07-22 12:12:1',y1=2014,b1=1 RETURNING *; +num_int1 num_bit1 num_float1 num_double1 char_enum1 char_set1 str_varchar1 d1 dt1 ts1 y1 b1 +4 C 124.67 231.12 B a,d,e AB 2012-03-14 2012-04-19 13:08:22 2001-07-22 12:12:01 2014 1 +# +# INSERT...ON DUPLICATE KEY UPDATE +# +INSERT INTO t1 VALUES (5,b'1000011', 123.45, 123.55,'C','b,e', 'V', +'120314',"2012-04-19 13:08:22", '2001-07-22 12:12:12',2012,0) +ON DUPLICATE KEY UPDATE num_float1=111.111 RETURNING *; +num_int1 num_bit1 num_float1 num_double1 char_enum1 char_set1 str_varchar1 d1 dt1 ts1 y1 b1 +5 C 123.45 123.55 C b,e V 2012-03-14 2012-04-19 13:08:22 2001-07-22 12:12:12 2012 0 +# +# INSERT...SELECT...RETURNING +# +INSERT INTO t2 SELECT * FROM t1 RETURNING *; +num_int2 num_bit2 num_float2 num_double2 char_enum2 char_set2 str_varchar2 d2 dt2 ts2 y2 b2 +1 C 123.45 123.55 A b,e V 2012-03-14 2012-04-19 13:08:22 2001-07-22 12:12:12 2012 0 +2 C 123.45 123.55 A b,e V 2012-03-14 2012-04-19 13:08:22 2001-07-22 12:12:12 2012 0 +3 C 123.45 123.55 A b,e V 2012-03-14 2012-04-19 13:08:22 2001-07-22 12:12:12 2012 1 +4 C 124.67 231.12 B a,d,e AB 2012-03-14 2012-04-19 13:08:22 2001-07-22 12:12:01 2014 1 +5 C 123.45 123.55 C b,e V 2012-03-14 2012-04-19 13:08:22 2001-07-22 12:12:12 2012 0 +DROP TABLE t1; +DROP TABLE t2; diff --git a/mysql-test/main/insert_returning_datatypes.test b/mysql-test/main/insert_returning_datatypes.test new file mode 100644 index 00000000000..6591e91cb4d --- /dev/null +++ b/mysql-test/main/insert_returning_datatypes.test @@ -0,0 +1,74 @@ +#Test for checking all dataypes are returned + +CREATE TABLE t1(num_int1 INT(2) PRIMARY KEY, +num_bit1 BIT(8), +num_float1 FLOAT(5,2), +num_double1 DOUBLE(5,2), +char_enum1 ENUM('A','B','C','D'), +char_set1 SET('a','b','c','d','e'), +str_varchar1 VARCHAR(2), +d1 DATE, +dt1 DATETIME, +ts1 TIMESTAMP, +y1 YEAR, +b1 BOOL); + +CREATE TABLE t2(num_int2 INT(2) PRIMARY KEY, +num_bit2 BIT(8), +num_float2 FLOAT(5,2), +num_double2 DOUBLE(5,2), +char_enum2 ENUM('A','B','C','D'), +char_set2 SET('a','b','c','d','e'), +str_varchar2 VARCHAR(2), +d2 DATE, +dt2 DATETIME, +ts2 TIMESTAMP, +y2 YEAR, +b2 BOOL); + + +--echo # +--echo # SIMLPE INSERT STATEMENT +--echo # +INSERT INTO t1(num_int1,num_bit1,num_float1,num_double1,char_enum1,char_set1, +str_varchar1, d1,dt1,ts1,y1,b1) VALUES(1,b'1000011', 123.45, 123.55, + 'A','b,e', 'V','120314',"2012-04-19 13:08:22", '2001-07-22 12:12:12', +2012,0) RETURNING *; + + +--echo # +--echo # MULTIPLE ROWS IN SINGLE STATEMENT +--echo # +INSERT INTO t1 VALUES(2,b'1000011', 123.45, 123.55, 'A','b,e', 'V', +'120314',"2012-04-19 13:08:22", '2001-07-22 12:12:12',2012,0),(3,b'1000011', +123.45, 123.55, 'A','b,e', 'V','120314',"2012-04-19 13:08:22", +'2001-07-22 12:12:12',2012,1) RETURNING *; + + + +--echo # +--echo # INSERT...SET...RETURNING +--echo # +INSERT INTO t1 SET num_int1=4,num_bit1=b'1000011',num_float1=124.67, +num_double1=231.12,char_enum1='B',char_set1='a,d,e', +str_varchar1='AB',d1='120314',dt1="2012-04-19 13:08:22", +ts1='2001-07-22 12:12:1',y1=2014,b1=1 RETURNING *; + + + +--echo # +--echo # INSERT...ON DUPLICATE KEY UPDATE +--echo # +INSERT INTO t1 VALUES (5,b'1000011', 123.45, 123.55,'C','b,e', 'V', +'120314',"2012-04-19 13:08:22", '2001-07-22 12:12:12',2012,0) +ON DUPLICATE KEY UPDATE num_float1=111.111 RETURNING *; + + +--echo # +--echo # INSERT...SELECT...RETURNING +--echo # +INSERT INTO t2 SELECT * FROM t1 RETURNING *; + + +DROP TABLE t1; +DROP TABLE t2; diff --git a/mysql-test/main/replace_returning.result b/mysql-test/main/replace_returning.result new file mode 100644 index 00000000000..628b70abad1 --- /dev/null +++ b/mysql-test/main/replace_returning.result @@ -0,0 +1,154 @@ +# Test for REPLACE...RETURNING +CREATE TABLE t1(id1 INT PRIMARY KEY, val1 VARCHAR(1)); +CREATE TABLE t2(id2 INT PRIMARY KEY, val2 VARCHAR(1)); +INSERT INTO t2 VALUES (1,'a'),(2,'b'),(3,'c'); +CREATE FUNCTION f(arg INT) RETURNS INT +BEGIN +RETURN (SELECT arg+arg); +END| +# +# Simple replace statement...RETURNING +# +REPLACE INTO t1 (id1, val1) VALUES (1, 'a'); +REPLACE INTO t1 (id1, val1) VALUES (1, 'b') RETURNING *; +id1 val1 +1 b +REPLACE INTO t1 (id1, val1) VALUES (1, 'c') RETURNING id1+id1 AS total, +id1&&id1, id1|id1,UPPER(val1),f(id1); +total id1&&id1 id1|id1 UPPER(val1) f(id1) +2 1 1 C 2 +REPLACE INTO t1(id1,val1) VALUES (1,'d') RETURNING (SELECT GROUP_CONCAT(val2) +FROM t2 WHERE id2=1); +(SELECT GROUP_CONCAT(val2) +FROM t2 WHERE id2=1) +a +REPLACE INTO t1(id1,val1) VALUES(1,'e') RETURNING (SELECT GROUP_CONCAT(val2) +FROM t2 GROUP BY id2 HAVING id2=id2+1); +(SELECT GROUP_CONCAT(val2) +FROM t2 GROUP BY id2 HAVING id2=id2+1) +NULL +PREPARE stmt FROM "REPLACE INTO t1 (id1,val1) VALUES (1,'f') RETURNING +id1,(SELECT id2 FROM t2 WHERE val2='b')"; +EXECUTE stmt; +id1 (SELECT id2 FROM t2 WHERE val2='b') +1 2 +DEALLOCATE PREPARE stmt; +SELECT * FROM t1; +id1 val1 +1 f +TRUNCATE TABLE t1; +# +# Multiple values in one replace statement...RETURNING +# +REPLACE INTO t1 VALUES (1,'a'),(2,'b'); +REPLACE INTO t1 VALUES (1,'c'),(2,'d') RETURNING *; +id1 val1 +1 c +2 d +REPLACE INTO t1 VALUES (1,'e'),(2,'f') RETURNING id1+id1 AS total, +id1&&id1, id1|id1,UPPER(val1),f(id1); +total id1&&id1 id1|id1 UPPER(val1) f(id1) +2 1 1 E 2 +4 1 2 F 4 +REPLACE INTO t1 VALUES (1,'o'),(2,'p') RETURNING (SELECT GROUP_CONCAT(val2) +FROM t2 WHERE id2=1); +(SELECT GROUP_CONCAT(val2) +FROM t2 WHERE id2=1) +a +a +REPLACE INTO t1 VALUES (1,'q'),(2,'r') RETURNING (SELECT GROUP_CONCAT(val2) +FROM t2 GROUP BY id2 HAVING id2=id2+1); +(SELECT GROUP_CONCAT(val2) +FROM t2 GROUP BY id2 HAVING id2=id2+1) +NULL +NULL +PREPARE stmt FROM "REPLACE INTO t1 VALUES (1,'s'),(2,'t') RETURNING id1, +(SELECT id2 FROM t2 WHERE val2='b')"; +EXECUTE stmt; +id1 (SELECT id2 FROM t2 WHERE val2='b') +1 2 +2 2 +DEALLOCATE PREPARE stmt; +SELECT * FROM t1; +id1 val1 +1 s +2 t +TRUNCATE TABLE t1; +# +# REPLACE...SET...RETURNING +# +REPLACE INTO t1 SET id1=1, val1 = 'a'; +REPLACE INTO t1 SET id1=2, val1 = 'b' RETURNING *; +id1 val1 +2 b +REPLACE INTO t1 SET id1=3, val1 = 'c' RETURNING id1+id1 AS total, +id1&&id1, id1|id1,UPPER(val1),f(id1); +total id1&&id1 id1|id1 UPPER(val1) f(id1) +6 1 3 C 6 +REPLACE INTO t1 SET id1=1, val1 = 'i' RETURNING (SELECT GROUP_CONCAT(val2) +FROM t2 WHERE id2=1); +(SELECT GROUP_CONCAT(val2) +FROM t2 WHERE id2=1) +a +REPLACE INTO t1 SET id1=2, val1='j' RETURNING (SELECT GROUP_CONCAT(val2) +FROM t2 GROUP BY id2 HAVING id2=id2+1); +(SELECT GROUP_CONCAT(val2) +FROM t2 GROUP BY id2 HAVING id2=id2+1) +NULL +PREPARE stmt FROM "REPLACE INTO t1 SET id1=3, val1='k' RETURNING id1, +(SELECT id2 FROM t2 WHERE val2='b')"; +EXECUTE stmt; +id1 (SELECT id2 FROM t2 WHERE val2='b') +3 2 +DEALLOCATE PREPARE stmt; +SELECT * FROM t1; +id1 val1 +1 i +2 j +3 k +# +# REPLACE...SELECT...RETURNING +# +TRUNCATE TABLE t2; +REPLACE INTO t2(id2,val2) SELECT * FROM t1; +REPLACE INTO t2 SELECT * FROM t1 WHERE id1=1 RETURNING *; +id2 val2 +1 i +REPLACE INTO t2 SELECT * FROM t1 WHERE id1=2 RETURNING id2+id2 AS total, +id2&&id2, id2|id2,UPPER(val2),f(id2); +total id2&&id2 id2|id2 UPPER(val2) f(id2) +4 1 2 J 4 +REPLACE INTO t2 SELECT * FROM t1 WHERE id1=3 RETURNING (SELECT +GROUP_CONCAT(val1) FROM t1 WHERE id1=1); +(SELECT +GROUP_CONCAT(val1) FROM t1 WHERE id1=1) +i +REPLACE INTO t2 SELECT * FROM t1 WHERE id1=1 RETURNING (SELECT +GROUP_CONCAT(val1) FROM t1 GROUP BY id1 HAVING id1=id1+1); +(SELECT +GROUP_CONCAT(val1) FROM t1 GROUP BY id1 HAVING id1=id1+1) +NULL +PREPARE stmt FROM "REPLACE INTO t2 SELECT * FROM t1 WHERE id1=2 RETURNING +id2,(SELECT id1 FROM t1 WHERE val1='b')"; +EXECUTE stmt; +id2 (SELECT id1 FROM t1 WHERE val1='b') +2 NULL +DEALLOCATE PREPARE stmt; +REPLACE INTO t2 SELECT * FROM t1 WHERE id1=3 RETURNING (SELECT id1+id2 +FROM t1 WHERE id1=1); +(SELECT id1+id2 +FROM t1 WHERE id1=1) +4 +REPLACE INTO t2 SELECT * FROM t1 WHERE id1=1 RETURNING (SELECT id1+id2 +FROM t2 WHERE id2=0); +(SELECT id1+id2 +FROM t2 WHERE id2=0) +NULL +SELECT * FROM t2; +id2 val2 +1 i +2 j +3 k +DROP TABLE t1; +DROP TABLE t2; +DROP FUNCTION f; diff --git a/mysql-test/main/replace_returning.test b/mysql-test/main/replace_returning.test new file mode 100644 index 00000000000..b2681585d31 --- /dev/null +++ b/mysql-test/main/replace_returning.test @@ -0,0 +1,93 @@ +--echo # Test for REPLACE...RETURNING + +CREATE TABLE t1(id1 INT PRIMARY KEY, val1 VARCHAR(1)); +CREATE TABLE t2(id2 INT PRIMARY KEY, val2 VARCHAR(1)); +INSERT INTO t2 VALUES (1,'a'),(2,'b'),(3,'c'); + +DELIMITER |; + +CREATE FUNCTION f(arg INT) RETURNS INT +BEGIN + RETURN (SELECT arg+arg); +END| + +DELIMITER ;| + +--echo # +--echo # Simple replace statement...RETURNING +--echo # +REPLACE INTO t1 (id1, val1) VALUES (1, 'a'); +REPLACE INTO t1 (id1, val1) VALUES (1, 'b') RETURNING *; +REPLACE INTO t1 (id1, val1) VALUES (1, 'c') RETURNING id1+id1 AS total, +id1&&id1, id1|id1,UPPER(val1),f(id1); +REPLACE INTO t1(id1,val1) VALUES (1,'d') RETURNING (SELECT GROUP_CONCAT(val2) +FROM t2 WHERE id2=1); +REPLACE INTO t1(id1,val1) VALUES(1,'e') RETURNING (SELECT GROUP_CONCAT(val2) +FROM t2 GROUP BY id2 HAVING id2=id2+1); +PREPARE stmt FROM "REPLACE INTO t1 (id1,val1) VALUES (1,'f') RETURNING +id1,(SELECT id2 FROM t2 WHERE val2='b')"; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; +SELECT * FROM t1; +TRUNCATE TABLE t1; + +--echo # +--echo # Multiple values in one replace statement...RETURNING +--echo # +REPLACE INTO t1 VALUES (1,'a'),(2,'b'); +REPLACE INTO t1 VALUES (1,'c'),(2,'d') RETURNING *; +REPLACE INTO t1 VALUES (1,'e'),(2,'f') RETURNING id1+id1 AS total, +id1&&id1, id1|id1,UPPER(val1),f(id1); +REPLACE INTO t1 VALUES (1,'o'),(2,'p') RETURNING (SELECT GROUP_CONCAT(val2) +FROM t2 WHERE id2=1); +REPLACE INTO t1 VALUES (1,'q'),(2,'r') RETURNING (SELECT GROUP_CONCAT(val2) +FROM t2 GROUP BY id2 HAVING id2=id2+1); +PREPARE stmt FROM "REPLACE INTO t1 VALUES (1,'s'),(2,'t') RETURNING id1, +(SELECT id2 FROM t2 WHERE val2='b')"; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; +SELECT * FROM t1; +TRUNCATE TABLE t1; + +--echo # +--echo # REPLACE...SET...RETURNING +--echo # +REPLACE INTO t1 SET id1=1, val1 = 'a'; +REPLACE INTO t1 SET id1=2, val1 = 'b' RETURNING *; +REPLACE INTO t1 SET id1=3, val1 = 'c' RETURNING id1+id1 AS total, +id1&&id1, id1|id1,UPPER(val1),f(id1); +REPLACE INTO t1 SET id1=1, val1 = 'i' RETURNING (SELECT GROUP_CONCAT(val2) +FROM t2 WHERE id2=1); +REPLACE INTO t1 SET id1=2, val1='j' RETURNING (SELECT GROUP_CONCAT(val2) +FROM t2 GROUP BY id2 HAVING id2=id2+1); +PREPARE stmt FROM "REPLACE INTO t1 SET id1=3, val1='k' RETURNING id1, +(SELECT id2 FROM t2 WHERE val2='b')"; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; +SELECT * FROM t1; + +--echo # +--echo # REPLACE...SELECT...RETURNING +--echo # +TRUNCATE TABLE t2; +REPLACE INTO t2(id2,val2) SELECT * FROM t1; +REPLACE INTO t2 SELECT * FROM t1 WHERE id1=1 RETURNING *; +REPLACE INTO t2 SELECT * FROM t1 WHERE id1=2 RETURNING id2+id2 AS total, +id2&&id2, id2|id2,UPPER(val2),f(id2); +REPLACE INTO t2 SELECT * FROM t1 WHERE id1=3 RETURNING (SELECT +GROUP_CONCAT(val1) FROM t1 WHERE id1=1); +REPLACE INTO t2 SELECT * FROM t1 WHERE id1=1 RETURNING (SELECT +GROUP_CONCAT(val1) FROM t1 GROUP BY id1 HAVING id1=id1+1); +PREPARE stmt FROM "REPLACE INTO t2 SELECT * FROM t1 WHERE id1=2 RETURNING +id2,(SELECT id1 FROM t1 WHERE val1='b')"; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; +REPLACE INTO t2 SELECT * FROM t1 WHERE id1=3 RETURNING (SELECT id1+id2 +FROM t1 WHERE id1=1); +REPLACE INTO t2 SELECT * FROM t1 WHERE id1=1 RETURNING (SELECT id1+id2 +FROM t2 WHERE id2=0); +SELECT * FROM t2; + +DROP TABLE t1; +DROP TABLE t2; +DROP FUNCTION f; diff --git a/mysql-test/main/replace_returning_datatypes.result b/mysql-test/main/replace_returning_datatypes.result new file mode 100644 index 00000000000..2dc7b5404c2 --- /dev/null +++ b/mysql-test/main/replace_returning_datatypes.result @@ -0,0 +1,61 @@ +CREATE TABLE t1(num_int1 INT(2) PRIMARY KEY, +num_bit1 BIT(8), +num_float1 FLOAT(5,2), +num_double1 DOUBLE(5,2), +char_enum1 ENUM('A','B','C','D'), +char_set1 SET('a','b','c','d','e'), +str_varchar1 VARCHAR(2), +d1 DATE, +dt1 DATETIME, +ts1 TIMESTAMP, +y1 YEAR, +b1 BOOL); +CREATE TABLE t2(num_int2 INT(2) PRIMARY KEY, +num_bit2 BIT(8), +num_float2 FLOAT(5,2), +num_double2 DOUBLE(5,2), +char_enum2 ENUM('A','B','C','D'), +char_set2 SET('a','b','c','d','e'), +str_varchar2 VARCHAR(2), +d2 DATE, +dt2 DATETIME, +ts2 TIMESTAMP, +y2 YEAR, +b2 BOOL); +# +# SIMLPE REPLACE STATEMENT +# +REPLACE INTO t1(num_int1,num_bit1,num_float1,num_double1,char_enum1,char_set1, +str_varchar1, d1,dt1,ts1,y1,b1) VALUES(1,b'1000011', 123.45, 123.55, +'A','b,e', 'V','120314',"2012-04-19 13:08:22", '2001-07-22 12:12:12', +2012,0) RETURNING *; +num_int1 num_bit1 num_float1 num_double1 char_enum1 char_set1 str_varchar1 d1 dt1 ts1 y1 b1 +1 C 123.45 123.55 A b,e V 2012-03-14 2012-04-19 13:08:22 2001-07-22 12:12:12 2012 0 +# +# MULTIPLE ROWS IN SINGLE STATEMENT +# +REPLACE INTO t1 VALUES(1,b'1000011', 123.45, 123.55, 'A','b,e', 'V', +'120314',"2012-04-19 13:08:22", '2001-07-22 12:12:12',2012,0),(3,b'1000011', +123.45, 123.55, 'A','b,e', 'V','120314',"2012-04-19 13:08:22", +'2001-07-22 12:12:12',2012,1) RETURNING *; +num_int1 num_bit1 num_float1 num_double1 char_enum1 char_set1 str_varchar1 d1 dt1 ts1 y1 b1 +1 C 123.45 123.55 A b,e V 2012-03-14 2012-04-19 13:08:22 2001-07-22 12:12:12 2012 0 +3 C 123.45 123.55 A b,e V 2012-03-14 2012-04-19 13:08:22 2001-07-22 12:12:12 2012 1 +# +# REPLACE...SET...RETURNING +# +REPLACE INTO t1 SET num_int1=1,num_bit1=b'1000011',num_float1=124.67, +num_double1=231.12,char_enum1='B',char_set1='a,d,e', +str_varchar1='AB',d1='120314',dt1="2012-04-19 13:08:22", +ts1='2001-07-22 12:12:1',y1=2014,b1=1 RETURNING *; +num_int1 num_bit1 num_float1 num_double1 char_enum1 char_set1 str_varchar1 d1 dt1 ts1 y1 b1 +1 C 124.67 231.12 B a,d,e AB 2012-03-14 2012-04-19 13:08:22 2001-07-22 12:12:01 2014 1 +# +# REPLACE...SELECT...RETURNING +# +REPLACE INTO t2 SELECT * FROM t1 RETURNING *; +num_int2 num_bit2 num_float2 num_double2 char_enum2 char_set2 str_varchar2 d2 dt2 ts2 y2 b2 +1 C 124.67 231.12 B a,d,e AB 2012-03-14 2012-04-19 13:08:22 2001-07-22 12:12:01 2014 1 +3 C 123.45 123.55 A b,e V 2012-03-14 2012-04-19 13:08:22 2001-07-22 12:12:12 2012 1 +DROP TABLE t1; +DROP TABLE t2; diff --git a/mysql-test/main/replace_returning_datatypes.test b/mysql-test/main/replace_returning_datatypes.test new file mode 100644 index 00000000000..f063865ecb7 --- /dev/null +++ b/mysql-test/main/replace_returning_datatypes.test @@ -0,0 +1,66 @@ +#Test for checking all dataypes are returned + +CREATE TABLE t1(num_int1 INT(2) PRIMARY KEY, +num_bit1 BIT(8), +num_float1 FLOAT(5,2), +num_double1 DOUBLE(5,2), +char_enum1 ENUM('A','B','C','D'), +char_set1 SET('a','b','c','d','e'), +str_varchar1 VARCHAR(2), +d1 DATE, +dt1 DATETIME, +ts1 TIMESTAMP, +y1 YEAR, +b1 BOOL); + +CREATE TABLE t2(num_int2 INT(2) PRIMARY KEY, +num_bit2 BIT(8), +num_float2 FLOAT(5,2), +num_double2 DOUBLE(5,2), +char_enum2 ENUM('A','B','C','D'), +char_set2 SET('a','b','c','d','e'), +str_varchar2 VARCHAR(2), +d2 DATE, +dt2 DATETIME, +ts2 TIMESTAMP, +y2 YEAR, +b2 BOOL); + + +--echo # +--echo # SIMLPE REPLACE STATEMENT +--echo # +REPLACE INTO t1(num_int1,num_bit1,num_float1,num_double1,char_enum1,char_set1, +str_varchar1, d1,dt1,ts1,y1,b1) VALUES(1,b'1000011', 123.45, 123.55, +'A','b,e', 'V','120314',"2012-04-19 13:08:22", '2001-07-22 12:12:12', +2012,0) RETURNING *; + + +--echo # +--echo # MULTIPLE ROWS IN SINGLE STATEMENT +--echo # +REPLACE INTO t1 VALUES(1,b'1000011', 123.45, 123.55, 'A','b,e', 'V', +'120314',"2012-04-19 13:08:22", '2001-07-22 12:12:12',2012,0),(3,b'1000011', +123.45, 123.55, 'A','b,e', 'V','120314',"2012-04-19 13:08:22", +'2001-07-22 12:12:12',2012,1) RETURNING *; + + + +--echo # +--echo # REPLACE...SET...RETURNING +--echo # +REPLACE INTO t1 SET num_int1=1,num_bit1=b'1000011',num_float1=124.67, +num_double1=231.12,char_enum1='B',char_set1='a,d,e', +str_varchar1='AB',d1='120314',dt1="2012-04-19 13:08:22", +ts1='2001-07-22 12:12:1',y1=2014,b1=1 RETURNING *; + + + +--echo # +--echo # REPLACE...SELECT...RETURNING +--echo # +REPLACE INTO t2 SELECT * FROM t1 RETURNING *; + + +DROP TABLE t1; +DROP TABLE t2; diff --git a/mysql-test/main/replace_returning_err.result b/mysql-test/main/replace_returning_err.result new file mode 100644 index 00000000000..04ccdb6c5a5 --- /dev/null +++ b/mysql-test/main/replace_returning_err.result @@ -0,0 +1,69 @@ +CREATE TABLE t1(id1 INT PRIMARY KEY, val1 VARCHAR(1)); +CREATE TABLE t2(id2 INT PRIMARY KEY, val2 VARCHAR(1)); +INSERT INTO t1 VALUES(1, 'a'), (2, 'b'), (3, 'c'); +# +# SIMLPE REPLACE STATEMENT +# +REPLACE INTO t2(id2, val2) VALUES(1, 'a') RETURNING id1; +ERROR 42S22: Unknown column 'id1' in 'field list' +REPLACE INTO t2(id2, val2) values(2, 'b') RETURNING SUM(id2); +ERROR HY000: Invalid use of group function +REPLACE INTO t2(id2, val2) VALUES(3, 'c') RETURNING(SELECT id1 FROM t1); +ERROR 21000: Subquery returns more than 1 row +REPLACE INTO t2(id2, val2) VALUES(4, 'd') RETURNING(SELECT* FROM t1); +ERROR 21000: Operand should contain 1 column(s) +REPLACE INTO t2(id2, val2) VALUES(4, 'd') RETURNING(SELECT* FROM t2); +ERROR 21000: Operand should contain 1 column(s) +REPLACE 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 +# +# Multiple rows in single replace statement +# +REPLACE INTO t2 VALUES(1, 'a'), (2, 'b') RETURNING id1; +ERROR 42S22: Unknown column 'id1' in 'field list' +REPLACE INTO t2 VALUES(3, 'c'), (4, 'd') RETURNING MAX(id2); +ERROR HY000: Invalid use of group function +REPLACE INTO t2 VALUES(5, 'c'), (6, 'f') RETURNING(SELECT id1 FROM t1); +ERROR 21000: Subquery returns more than 1 row +REPLACE INTO t2 VALUES(7, 'g'), (8, 'h') RETURNING(SELECT* FROM t1); +ERROR 21000: Operand should contain 1 column(s) +REPLACE INTO t2 VALUES(9, 'g'), (10, 'h') RETURNING(SELECT* FROM t2); +ERROR 21000: Operand should contain 1 column(s) +REPLACE INTO t2 VALUES(13, 'f'), (14, 'g') 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 +# +# REPLACE ... SET +# +REPLACE INTO t2 SET id2 = 1, val2 = 'a' RETURNING id1; +ERROR 42S22: Unknown column 'id1' in 'field list' +REPLACE INTO t2 SET id2 = 2, val2 = 'b' RETURNING COUNT(id2); +ERROR HY000: Invalid use of group function +REPLACE INTO t2 SET id2 = 3, val2 = 'c' RETURNING(SELECT id1 FROM t1); +ERROR 21000: Subquery returns more than 1 row +REPLACE INTO t2 SET id2 = 4, val2 = 'd' RETURNING(SELECT * FROM t1); +ERROR 21000: Operand should contain 1 column(s) +REPLACE INTO t2 SET id2 = 4, val2 = 'd' RETURNING(SELECT * FROM t2); +ERROR 21000: Operand should contain 1 column(s) +REPLACE INTO t2 SET id2 = 5, val2 = '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 +# +# REPLACE...SELECT +# +REPLACE INTO t2(id2, val2) SELECT* FROM t1 WHERE id1 = 1 RETURNING id1; +ERROR 42S22: Unknown column 'id1' in 'field list' +REPLACE INTO t2(id2, val2) SELECT* FROM t1 WHERE id1 = 2 RETURNING MAX(id2); +ERROR HY000: Invalid use of group function +REPLACE INTO t2(id2, val2) SELECT* FROM t1 WHERE id1 = 2 RETURNING(SELECT id1 +FROM t1); +ERROR 21000: Subquery returns more than 1 row +REPLACE INTO t2(id2, val2) SELECT* FROM t1 WHERE id1 = 2 RETURNING(SELECT * +FROM t1); +ERROR 21000: Operand should contain 1 column(s) +REPLACE INTO t2(id2, val2) SELECT* FROM t1 WHERE id1 = 2 RETURNING(SELECT * +FROM t2); +ERROR 21000: Operand should contain 1 column(s) +REPLACE INTO t2(id2, val2) SELECT* FROM t1 WHERE id1 = 2 RETURNING(SELECT id2 +FROM t2); +ERROR 21000: Subquery returns more than 1 row +DROP TABLE t1; +DROP TABLE t2; diff --git a/mysql-test/main/replace_returning_err.test b/mysql-test/main/replace_returning_err.test new file mode 100644 index 00000000000..383bd1e1c8b --- /dev/null +++ b/mysql-test/main/replace_returning_err.test @@ -0,0 +1,86 @@ +# +# Test for checking error message for REPLACE...RETURNING +# + +#REPLACE INTO <table> ... RETURNING <not existing col> +#REPLACE INTO <table> ... RETURNING <expr with aggr function> +#REPLACE INTO ... RETURNING subquery with more than 1 row +#REPLACE INTO ... RETURNING operand should contain 1 colunm(s) +#REPLACE INTO ... RETURNING operand should contain 1 colunm(s) +#REPLACE INTO ... SELECT...RETURNING < SELECT <colname> FROM <REPLACE TABLE> + +CREATE TABLE t1(id1 INT PRIMARY KEY, val1 VARCHAR(1)); +CREATE TABLE t2(id2 INT PRIMARY KEY, val2 VARCHAR(1)); + +INSERT INTO t1 VALUES(1, 'a'), (2, 'b'), (3, 'c'); + +--echo # +--echo # SIMLPE REPLACE STATEMENT +--echo # +--error ER_BAD_FIELD_ERROR +REPLACE INTO t2(id2, val2) VALUES(1, 'a') RETURNING id1; +--error ER_INVALID_GROUP_FUNC_USE +REPLACE INTO t2(id2, val2) values(2, 'b') RETURNING SUM(id2); +--error ER_SUBQUERY_NO_1_ROW +REPLACE INTO t2(id2, val2) VALUES(3, 'c') RETURNING(SELECT id1 FROM t1); +--error ER_OPERAND_COLUMNS +REPLACE INTO t2(id2, val2) VALUES(4, 'd') RETURNING(SELECT* FROM t1); +--error ER_OPERAND_COLUMNS +REPLACE INTO t2(id2, val2) VALUES(4, 'd') RETURNING(SELECT* FROM t2); +--error ER_UPDATE_TABLE_USED +REPLACE INTO t2(id2, val2) VALUES(5, 'f') RETURNING(SELECT id2 FROM t2); + +--echo # +--echo # Multiple rows in single replace statement +--echo # +--error ER_BAD_FIELD_ERROR +REPLACE INTO t2 VALUES(1, 'a'), (2, 'b') RETURNING id1; +--error ER_INVALID_GROUP_FUNC_USE +REPLACE INTO t2 VALUES(3, 'c'), (4, 'd') RETURNING MAX(id2); +--error ER_SUBQUERY_NO_1_ROW +REPLACE INTO t2 VALUES(5, 'c'), (6, 'f') RETURNING(SELECT id1 FROM t1); +--error ER_OPERAND_COLUMNS +REPLACE INTO t2 VALUES(7, 'g'), (8, 'h') RETURNING(SELECT* FROM t1); +--error ER_OPERAND_COLUMNS +REPLACE INTO t2 VALUES(9, 'g'), (10, 'h') RETURNING(SELECT* FROM t2); +--error ER_UPDATE_TABLE_USED +REPLACE INTO t2 VALUES(13, 'f'), (14, 'g') RETURNING(SELECT id2 FROM t2); + +--echo # +--echo # REPLACE ... SET +--echo # +--error ER_BAD_FIELD_ERROR +REPLACE INTO t2 SET id2 = 1, val2 = 'a' RETURNING id1; +--error ER_INVALID_GROUP_FUNC_USE +REPLACE INTO t2 SET id2 = 2, val2 = 'b' RETURNING COUNT(id2); +--error ER_SUBQUERY_NO_1_ROW +REPLACE INTO t2 SET id2 = 3, val2 = 'c' RETURNING(SELECT id1 FROM t1); +--error ER_OPERAND_COLUMNS +REPLACE INTO t2 SET id2 = 4, val2 = 'd' RETURNING(SELECT * FROM t1); +--error ER_OPERAND_COLUMNS +REPLACE INTO t2 SET id2 = 4, val2 = 'd' RETURNING(SELECT * FROM t2); +--error ER_UPDATE_TABLE_USED +REPLACE INTO t2 SET id2 = 5, val2 = 'f' RETURNING(SELECT id2 FROM t2); + +--echo # +--echo # REPLACE...SELECT +--echo # +--error ER_BAD_FIELD_ERROR +REPLACE INTO t2(id2, val2) SELECT* FROM t1 WHERE id1 = 1 RETURNING id1; +--error ER_INVALID_GROUP_FUNC_USE +REPLACE INTO t2(id2, val2) SELECT* FROM t1 WHERE id1 = 2 RETURNING MAX(id2); +--error ER_SUBQUERY_NO_1_ROW +REPLACE INTO t2(id2, val2) SELECT* FROM t1 WHERE id1 = 2 RETURNING(SELECT id1 +FROM t1); +--error ER_OPERAND_COLUMNS +REPLACE INTO t2(id2, val2) SELECT* FROM t1 WHERE id1 = 2 RETURNING(SELECT * +FROM t1); +--error ER_OPERAND_COLUMNS +REPLACE INTO t2(id2, val2) SELECT* FROM t1 WHERE id1 = 2 RETURNING(SELECT * +FROM t2); +--error ER_SUBQUERY_NO_1_ROW +REPLACE INTO t2(id2, val2) SELECT* FROM t1 WHERE id1 = 2 RETURNING(SELECT id2 +FROM t2); + +DROP TABLE t1; +DROP TABLE t2; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 2a7470dc689..1df08eb739e 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -7431,6 +7431,7 @@ SHOW_VAR status_vars[]= { {"Feature_dynamic_columns", (char*) offsetof(STATUS_VAR, feature_dynamic_columns), SHOW_LONG_STATUS}, {"Feature_fulltext", (char*) offsetof(STATUS_VAR, feature_fulltext), SHOW_LONG_STATUS}, {"Feature_gis", (char*) offsetof(STATUS_VAR, feature_gis), SHOW_LONG_STATUS}, + {"Feature_insert_returning", (char*)offsetof(STATUS_VAR, feature_insert_returning), SHOW_LONG_STATUS}, {"Feature_invisible_columns", (char*) offsetof(STATUS_VAR, feature_invisible_columns), SHOW_LONG_STATUS}, {"Feature_json", (char*) offsetof(STATUS_VAR, feature_json), SHOW_LONG_STATUS}, {"Feature_locale", (char*) offsetof(STATUS_VAR, feature_locale), SHOW_LONG_STATUS}, diff --git a/sql/sql_base.cc b/sql/sql_base.cc index feb79990796..15c0e976c26 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -7632,6 +7632,26 @@ bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array, /* + Perform checks like all given fields exists, if exists fill struct with + current data and expand all '*' in given fields for LEX::returning. + + SYNOPSIS + thd Thread handler + table_list Global/local table list +*/ + +int setup_returning_fields(THD* thd, TABLE_LIST* table_list) +{ + if (!thd->lex->has_returning()) + return 0; + return setup_wild(thd, table_list, thd->lex->returning()->item_list, NULL, + thd->lex->returning()) + || setup_fields(thd, Ref_ptr_array(), thd->lex->returning()->item_list, + MARK_COLUMNS_READ, NULL, NULL, false); +} + + +/* make list of leaves of join table tree SYNOPSIS diff --git a/sql/sql_base.h b/sql/sql_base.h index 40dd3ed0907..211993c41ac 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -176,6 +176,7 @@ void make_leaves_list(THD *thd, List<TABLE_LIST> &list, TABLE_LIST *tables, bool full_table_list, TABLE_LIST *boundary); int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, List<Item> *sum_func_list, SELECT_LEX *sl); +int setup_returning_fields(THD* thd, TABLE_LIST* table_list); bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array, List<Item> &item, enum_column_usage column_usage, List<Item> *sum_func_list, List<Item> *pre_fix, diff --git a/sql/sql_class.h b/sql/sql_class.h index 34bafea8784..a208b627b7e 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -878,6 +878,7 @@ typedef struct system_status_var ulong feature_system_versioning; /* +1 opening a table WITH SYSTEM VERSIONING */ ulong feature_application_time_periods; /* +1 opening a table with application-time period */ + ulong feature_insert_returning; /* +1 when INSERT...RETURNING is used */ ulong feature_timezone; /* +1 when XPATH is used */ ulong feature_trigger; /* +1 opening a table with triggers */ ulong feature_xml; /* +1 when XPATH is used */ @@ -5517,16 +5518,17 @@ public: class select_insert :public select_result_interceptor { public: + select_result *sel_result; TABLE_LIST *table_list; TABLE *table; List<Item> *fields; ulonglong autoinc_value_of_last_inserted_row; // autogenerated or not COPY_INFO info; bool insert_into_view; - select_insert(THD *thd_arg, TABLE_LIST *table_list_par, - TABLE *table_par, List<Item> *fields_par, - List<Item> *update_fields, List<Item> *update_values, - enum_duplicates duplic, bool ignore); + select_insert(THD *thd_arg, TABLE_LIST *table_list_par, TABLE *table_par, + List<Item> *fields_par, List<Item> *update_fields, + List<Item> *update_values, enum_duplicates duplic, + bool ignore, select_result *sel_ret_list); ~select_insert(); int prepare(List<Item> &list, SELECT_LEX_UNIT *u); virtual int prepare2(JOIN *join); @@ -5562,7 +5564,7 @@ public: List<Item> &select_fields,enum_duplicates duplic, bool ignore, TABLE_LIST *select_tables_arg): select_insert(thd_arg, table_arg, NULL, &select_fields, 0, 0, duplic, - ignore), + ignore, NULL), create_table(table_arg), create_info(create_info_par), select_tables(select_tables_arg), diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 6a58dab9394..8161a761486 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -319,10 +319,10 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ORDER *order= (ORDER *) ((order_list && order_list->elements) ? order_list->first : NULL); SELECT_LEX *select_lex= thd->lex->first_select_lex(); + SELECT_LEX *returning= thd->lex->has_returning() ? thd->lex->returning() : 0; killed_state killed_status= NOT_KILLED; THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE; bool binlog_is_row; - bool with_select= !select_lex->item_list.is_empty(); Explain_delete *explain; Delete_plan query_plan(thd->mem_root); Unique * deltempfile= NULL; @@ -385,16 +385,14 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, query_plan.select_lex= thd->lex->first_select_lex(); query_plan.table= table; - if (mysql_prepare_delete(thd, table_list, select_lex->with_wild, - select_lex->item_list, &conds, - &delete_while_scanning)) + if (mysql_prepare_delete(thd, table_list, &conds, &delete_while_scanning)) DBUG_RETURN(TRUE); if (delete_history) table->vers_write= false; - if (with_select) - (void) result->prepare(select_lex->item_list, NULL); + if (returning) + (void) result->prepare(returning->item_list, NULL); if (thd->lex->current_select->first_cond_optimization) { @@ -459,9 +457,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, has_triggers= table->triggers && table->triggers->has_delete_triggers(); - if (!with_select && !using_limit && const_cond_result && - (!thd->is_current_stmt_binlog_format_row() && - !has_triggers) + if (!returning && !using_limit && const_cond_result && + (!thd->is_current_stmt_binlog_format_row() && !has_triggers) && !table->versioned(VERS_TIMESTAMP) && !table_list->has_period()) { /* Update the table->file->stats.records number */ @@ -632,7 +629,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, */ if ((table->file->ha_table_flags() & HA_CAN_DIRECT_UPDATE_AND_DELETE) && - !has_triggers && !binlog_is_row && !with_select && + !has_triggers && !binlog_is_row && !returning && !table_list->has_period()) { table->mark_columns_needed_for_delete(); @@ -682,7 +679,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, DELETE ... RETURNING we can't, because the RETURNING part may have a subquery in it) */ - if (!with_select) + if (!returning) free_underlaid_joins(thd, select_lex); select= 0; } @@ -717,11 +714,10 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, !table->prepare_triggers_for_delete_stmt_or_event()) will_batch= !table->file->start_bulk_delete(); - if (with_select) + if (returning) { - if (unlikely(result->send_result_set_metadata(select_lex->item_list, - Protocol::SEND_NUM_ROWS | - Protocol::SEND_EOF))) + if (result->send_result_set_metadata(returning->item_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) goto cleanup; } @@ -804,7 +800,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } // no LIMIT / OFFSET - if (with_select && result->send_data(select_lex->item_list) < 0) + if (returning && result->send_data(returning->item_list) < 0) { error=1; break; @@ -948,7 +944,7 @@ cleanup: if (thd->lex->analyze_stmt) goto send_nothing_and_leave; - if (with_select) + if (returning) result->send_eof(); else my_ok(thd, deleted); @@ -998,16 +994,13 @@ got_error: mysql_prepare_delete() thd - thread handler table_list - global/local table list - wild_num - number of wildcards used in optional SELECT clause - field_list - list of items in optional SELECT clause conds - conditions RETURN VALUE FALSE OK TRUE error */ -int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, - uint wild_num, List<Item> &field_list, Item **conds, +int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds, bool *delete_while_scanning) { Item *fake_conds= 0; @@ -1017,12 +1010,9 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, *delete_while_scanning= true; thd->lex->allow_sum_func.clear_all(); - if (setup_tables_and_check_access(thd, - &thd->lex->first_select_lex()->context, - &thd->lex->first_select_lex()-> - top_join_list, - table_list, - select_lex->leaf_tables, FALSE, + if (setup_tables_and_check_access(thd, &select_lex->context, + &select_lex->top_join_list, table_list, + select_lex->leaf_tables, FALSE, DELETE_ACL, SELECT_ACL, TRUE)) DBUG_RETURN(TRUE); if (table_list->vers_conditions.is_set()) @@ -1049,9 +1039,7 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, DBUG_RETURN(true); } - if ((wild_num && setup_wild(thd, table_list, field_list, NULL, select_lex)) || - setup_fields(thd, Ref_ptr_array(), - field_list, MARK_COLUMNS_READ, NULL, NULL, 0) || + if (setup_returning_fields(thd, table_list) || setup_conds(thd, table_list, select_lex->leaf_tables, conds) || setup_ftfuncs(select_lex)) DBUG_RETURN(TRUE); diff --git a/sql/sql_delete.h b/sql/sql_delete.h index 7af8564abf9..520524c72cc 100644 --- a/sql/sql_delete.h +++ b/sql/sql_delete.h @@ -26,8 +26,7 @@ class select_result; typedef class Item COND; template <typename T> class SQL_I_List; -int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, - uint wild_num, List<Item> &field_list, Item **conds, +int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds, bool *delete_while_scanning); bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SQL_I_List<ORDER> *order, ha_rows rows, diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index e7f746263db..ba7bf4c44c7 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -677,18 +677,19 @@ Field **TABLE::field_to_fill() /** INSERT statement implementation + SYNOPSIS + mysql_insert() + result NULL if the insert is not outputing results + via 'RETURNING' clause. + @note Like implementations of other DDL/DML in MySQL, this function relies on the caller to close the thread tables. This is done in the end of dispatch_command(). */ - -bool mysql_insert(THD *thd,TABLE_LIST *table_list, - List<Item> &fields, - List<List_item> &values_list, - List<Item> &update_fields, - List<Item> &update_values, - enum_duplicates duplic, - bool ignore) +bool mysql_insert(THD *thd, TABLE_LIST *table_list, + List<Item> &fields, List<List_item> &values_list, + List<Item> &update_fields, List<Item> &update_values, + enum_duplicates duplic, bool ignore, select_result *result) { bool retval= true; int error, res; @@ -707,6 +708,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, List_item *values; Name_resolution_context *context; Name_resolution_context_state ctx_state; + SELECT_LEX *returning= thd->lex->has_returning() ? thd->lex->returning() : 0; + #ifndef EMBEDDED_LIBRARY char *query= thd->query(); /* @@ -765,6 +768,9 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, &unused_conds, FALSE)) goto abort; + /* Prepares LEX::returing_list if it is not empty */ + if (returning) + result->prepare(returning->item_list, NULL); /* mysql_prepare_insert sets table_list->table if it was not set */ table= table_list->table; @@ -934,6 +940,16 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, goto values_loop_end; } } + /* + If statement returns result set, we need to send the result set metadata + to the client so that it knows that it has to expect an EOF or ERROR. + At this point we have all the required information to send the result set + metadata. + */ + if (returning && + result->send_result_set_metadata(returning->item_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) + goto values_loop_end; THD_STAGE_INFO(thd, stage_update); do @@ -1063,6 +1079,18 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, error=write_record(thd, table ,&info); if (unlikely(error)) break; + /* + We send the row after writing it to the table so that the + correct values are sent to the client. Otherwise it won't show + autoinc values (generated inside the handler::ha_write()) and + values updated in ON DUPLICATE KEY UPDATE (handled inside + write_record()). + */ + if (returning && result->send_data(returning->item_list) < 0) + { + error= 1; + break; + } thd->get_stmt_da()->inc_current_row_for_warning(); } its.rewind(); @@ -1218,14 +1246,24 @@ values_loop_end: goto abort; if (thd->lex->analyze_stmt) { - retval= thd->lex->explain->send_explain(thd); + retval= 0; goto abort; } if ((iteration * values_list.elements) == 1 && (!(thd->variables.option_bits & OPTION_WARNINGS) || !thd->cuted_fields)) { - my_ok(thd, info.copied + info.deleted + + /* + Client expects an EOF/OK packet if result set metadata was sent. If + LEX::has_returning and the statement returns result set + we send EOF which is the indicator of the end of the row stream. + Oherwise we send an OK packet i.e when the statement returns only the + status information + */ + if (returning) + result->send_eof(); + else + my_ok(thd, info.copied + info.deleted + ((thd->client_capabilities & CLIENT_FOUND_ROWS) ? info.touched : info.updated), id); } @@ -1237,14 +1275,17 @@ values_loop_end: if (ignore) sprintf(buff, ER_THD(thd, ER_INSERT_INFO), (ulong) info.records, - (lock_type == TL_WRITE_DELAYED) ? (ulong) 0 : - (ulong) (info.records - info.copied), + (lock_type == TL_WRITE_DELAYED) ? (ulong) 0 : + (ulong) (info.records - info.copied), (long) thd->get_stmt_da()->current_statement_warn_count()); else sprintf(buff, ER_THD(thd, ER_INSERT_INFO), (ulong) info.records, - (ulong) (info.deleted + updated), + (ulong) (info.deleted + updated), (long) thd->get_stmt_da()->current_statement_warn_count()); - ::my_ok(thd, info.copied + info.deleted + updated, id, buff); + if (returning) + result->send_eof(); + else + ::my_ok(thd, info.copied + info.deleted + updated, id, buff); } thd->abort_on_warning= 0; if (thd->lex->current_select->first_cond_optimization) @@ -1528,10 +1569,11 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, table_list->next_local= 0; context->resolve_in_table_list_only(table_list); - res= (setup_fields(thd, Ref_ptr_array(), - *values, MARK_COLUMNS_READ, 0, NULL, 0) || + res= setup_returning_fields(thd, table_list) || + setup_fields(thd, Ref_ptr_array(), + *values, MARK_COLUMNS_READ, 0, NULL, 0) || check_insert_fields(thd, context->table_list, fields, *values, - !insert_into_view, 0, &map)); + !insert_into_view, 0, &map); if (!res) res= setup_fields(thd, Ref_ptr_array(), @@ -3527,7 +3569,7 @@ bool Delayed_insert::handle_inserts(void) TRUE Error */ -bool mysql_insert_select_prepare(THD *thd) +bool mysql_insert_select_prepare(THD *thd, select_result *sel_res) { LEX *lex= thd->lex; SELECT_LEX *select_lex= lex->first_select_lex(); @@ -3544,6 +3586,13 @@ bool mysql_insert_select_prepare(THD *thd) &select_lex->where, TRUE)) DBUG_RETURN(TRUE); + /* + If sel_res is not empty, it means we have items in returing_list. + So we prepare the list now + */ + if (sel_res) + sel_res->prepare(lex->returning()->item_list, NULL); + DBUG_ASSERT(select_lex->leaf_tables.elements != 0); List_iterator<TABLE_LIST> ti(select_lex->leaf_tables); TABLE_LIST *table; @@ -3586,8 +3635,10 @@ select_insert::select_insert(THD *thd_arg, TABLE_LIST *table_list_par, List<Item> *update_fields, List<Item> *update_values, enum_duplicates duplic, - bool ignore_check_option_errors): + bool ignore_check_option_errors, + select_result *result): select_result_interceptor(thd_arg), + sel_result(result), table_list(table_list_par), table(table_par), fields(fields_par), autoinc_value_of_last_inserted_row(0), insert_into_view(table_list_par && table_list_par->view != 0) @@ -3606,7 +3657,7 @@ int select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) { LEX *lex= thd->lex; - int res; + int res= 0; table_map map= 0; SELECT_LEX *lex_current_select_save= lex->current_select; DBUG_ENTER("select_insert::prepare"); @@ -3620,10 +3671,10 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) */ lex->current_select= lex->first_select_lex(); - res= (setup_fields(thd, Ref_ptr_array(), - values, MARK_COLUMNS_READ, 0, NULL, 0) || - check_insert_fields(thd, table_list, *fields, values, - !insert_into_view, 1, &map)); + res= setup_returning_fields(thd, table_list) || + setup_fields(thd, Ref_ptr_array(), values, MARK_COLUMNS_READ, 0, 0, 0) || + check_insert_fields(thd, table_list, *fields, values, + !insert_into_view, 1, &map); if (!res && fields->elements) { @@ -3790,11 +3841,18 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) int select_insert::prepare2(JOIN *) { DBUG_ENTER("select_insert::prepare2"); + if (table->validate_default_values_of_unset_fields(thd)) + DBUG_RETURN(1); + if (thd->lex->describe) + DBUG_RETURN(0); if (thd->lex->current_select->options & OPTION_BUFFER_RESULT && - thd->locked_tables_mode <= LTM_LOCK_TABLES && - !thd->lex->describe) + thd->locked_tables_mode <= LTM_LOCK_TABLES) table->file->ha_start_bulk_insert((ha_rows) 0); - if (table->validate_default_values_of_unset_fields(thd)) + + /* Same as the other variants of INSERT */ + if (sel_result && + sel_result->send_result_set_metadata(thd->lex->returning()->item_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(1); DBUG_RETURN(0); } @@ -3809,6 +3867,7 @@ void select_insert::cleanup() select_insert::~select_insert() { DBUG_ENTER("~select_insert"); + sel_result= NULL; if (table && table->is_created()) { table->next_number_field=0; @@ -3850,6 +3909,15 @@ int select_insert::send_data(List<Item> &values) } error= write_record(thd, table, &info); + /* + Sending the result set to the cliet after writing record. The reason is + same as other variants of insert. + */ + if (sel_result && sel_result->send_data(thd->lex->returning()->item_list) < 0) + { + error= 1; + DBUG_RETURN(1); + } table->vers_write= table->versioned(); table->auto_increment_field_not_null= FALSE; @@ -4004,7 +4072,14 @@ bool select_insert::send_ok_packet() { thd->first_successful_insert_id_in_prev_stmt : (info.copied ? autoinc_value_of_last_inserted_row : 0)); - ::my_ok(thd, row_count, id, message); + /* + Client expects an EOF/OK packet If LEX::has_returning and if result set + meta was sent. See explanation for other variants of INSERT. + */ + if (sel_result) + sel_result->send_eof(); + else + ::my_ok(thd, row_count, id, message); DBUG_RETURN(false); } diff --git a/sql/sql_insert.h b/sql/sql_insert.h index a37ed1f31e5..5b86f09d13b 100644 --- a/sql/sql_insert.h +++ b/sql/sql_insert.h @@ -31,7 +31,7 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table, bool mysql_insert(THD *thd,TABLE_LIST *table,List<Item> &fields, List<List_item> &values, List<Item> &update_fields, List<Item> &update_values, enum_duplicates flag, - bool ignore); + bool ignore, select_result* result); void upgrade_lock_type_for_insert(THD *thd, thr_lock_type *lock_type, enum_duplicates duplic, bool is_multi_insert); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index ebc66418937..a05264195fb 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -998,14 +998,16 @@ public: bool explainable() { /* - Save plans for child subqueries, when - (1) they are not parts of eliminated WHERE/ON clauses. - (2) they are not merged derived tables - (3) they are not hanging CTEs (they are needed for execution) + EXPLAIN/ANALYZE unit, when: + (1) if it's a subquery - it's not part of eliminated WHERE/ON clause. + (2) if it's a CTE - it's not hanging (needed for execution) + (3) if it's a derived - it's not merged + if it's not 1/2/3 - it's some weird internal thing, ignore it */ - return !(item && item->eliminated) && - !(derived && !derived->is_materialized_derived()) && - !(with_element && (!derived || !derived->derived_result)); + return item ? !item->eliminated : // (1) + with_element ? derived && derived->derived_result : // (2) + derived ? derived->is_materialized_derived() : // (3) + false; } void reset_distinct(); @@ -4531,6 +4533,11 @@ public: void stmt_purge_to(const LEX_CSTRING &to); bool stmt_purge_before(Item *item); + SELECT_LEX *returning() + { return &builtin_select; } + bool has_returning() + { return !builtin_select.item_list.is_empty(); } + private: bool stmt_create_routine_start(const DDL_options_st &options) { diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index bbf00e591e3..0493176b264 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -4451,6 +4451,7 @@ mysql_execute_command(THD *thd) case SQLCOM_INSERT: { WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_INSERT_REPLACE); + select_result *sel_result= NULL; DBUG_ASSERT(first_table == all_tables && first_table != 0); WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_INSERT_REPLACE); @@ -4471,9 +4472,41 @@ mysql_execute_command(THD *thd) break; MYSQL_INSERT_START(thd->query()); + Protocol* save_protocol=NULL; + + if (lex->has_returning()) + { + status_var_increment(thd->status_var.feature_insert_returning); + + /* This is INSERT ... RETURNING. It will return output to the client */ + if (thd->lex->analyze_stmt) + { + /* + Actually, it is ANALYZE .. INSERT .. RETURNING. We need to produce + output and then discard it. + */ + sel_result= new (thd->mem_root) select_send_analyze(thd); + save_protocol= thd->protocol; + thd->protocol= new Protocol_discard(thd); + } + else + { + if (!(sel_result= new (thd->mem_root) select_send(thd))) + goto error; + } + } + res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values, - lex->update_list, lex->value_list, - lex->duplicates, lex->ignore); + lex->update_list, lex->value_list, + lex->duplicates, lex->ignore, sel_result); + if (save_protocol) + { + delete thd->protocol; + thd->protocol= save_protocol; + } + if (!res && thd->lex->analyze_stmt) + res= thd->lex->explain->send_explain(thd); + delete sel_result; MYSQL_INSERT_DONE(res, (ulong) thd->get_row_count_func()); /* If we have inserted into a VIEW, and the base table has @@ -4505,6 +4538,7 @@ mysql_execute_command(THD *thd) { WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_INSERT_REPLACE); select_insert *sel_result; + select_result *result= NULL; bool explain= MY_TEST(lex->describe); DBUG_ASSERT(first_table == all_tables && first_table != 0); WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); @@ -4553,6 +4587,31 @@ mysql_execute_command(THD *thd) Only the INSERT table should be merged. Other will be handled by select. */ + + Protocol* save_protocol=NULL; + + if (lex->has_returning()) + { + status_var_increment(thd->status_var.feature_insert_returning); + + /* This is INSERT ... RETURNING. It will return output to the client */ + if (thd->lex->analyze_stmt) + { + /* + Actually, it is ANALYZE .. INSERT .. RETURNING. We need to produce + output and then discard it. + */ + result= new (thd->mem_root) select_send_analyze(thd); + save_protocol= thd->protocol; + thd->protocol= new Protocol_discard(thd); + } + else + { + if (!(result= new (thd->mem_root) select_send(thd))) + goto error; + } + } + /* Skip first table, which is the table we are inserting in */ TABLE_LIST *second_table= first_table->next_local; /* @@ -4563,17 +4622,19 @@ mysql_execute_command(THD *thd) be done properly as well) */ select_lex->table_list.first= second_table; - select_lex->context.table_list= + select_lex->context.table_list= select_lex->context.first_name_resolution_table= second_table; - res= mysql_insert_select_prepare(thd); - if (!res && (sel_result= new (thd->mem_root) select_insert(thd, - first_table, - first_table->table, - &lex->field_list, - &lex->update_list, - &lex->value_list, - lex->duplicates, - lex->ignore))) + res= mysql_insert_select_prepare(thd, result); + if (!res && + (sel_result= new (thd->mem_root) + select_insert(thd, first_table, + first_table->table, + &lex->field_list, + &lex->update_list, + &lex->value_list, + lex->duplicates, + lex->ignore, + result))) { if (lex->analyze_stmt) ((select_result_interceptor*)sel_result)->disable_my_ok_calls(); @@ -4609,7 +4670,12 @@ mysql_execute_command(THD *thd) } delete sel_result; } - + delete result; + if (save_protocol) + { + delete thd->protocol; + thd->protocol= save_protocol; + } if (!res && (explain || lex->analyze_stmt)) res= thd->lex->explain->send_explain(thd); @@ -4644,7 +4710,7 @@ mysql_execute_command(THD *thd) MYSQL_DELETE_START(thd->query()); Protocol *save_protocol= NULL; - if (!select_lex->item_list.is_empty()) + if (lex->has_returning()) { /* This is DELETE ... RETURNING. It will return output to the client */ if (thd->lex->analyze_stmt) diff --git a/sql/sql_parse.h b/sql/sql_parse.h index 1d25b898ca4..a90628781cc 100644 --- a/sql/sql_parse.h +++ b/sql/sql_parse.h @@ -41,7 +41,7 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables); bool multi_delete_precheck(THD *thd, TABLE_LIST *tables); int mysql_multi_update_prepare(THD *thd); int mysql_multi_delete_prepare(THD *thd); -bool mysql_insert_select_prepare(THD *thd); +bool mysql_insert_select_prepare(THD *thd,select_result *sel_res); bool update_precheck(THD *thd, TABLE_LIST *tables); bool delete_precheck(THD *thd, TABLE_LIST *tables); bool insert_precheck(THD *thd, TABLE_LIST *tables); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 9a3013e5d47..e5723cb3b78 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1482,8 +1482,6 @@ static bool mysql_test_delete(Prepared_statement *stmt, } DBUG_RETURN(mysql_prepare_delete(thd, table_list, - lex->first_select_lex()->with_wild, - lex->first_select_lex()->item_list, &lex->first_select_lex()->where, &delete_while_scanning)); error: @@ -2157,7 +2155,7 @@ static int mysql_insert_select_prepare_tester(THD *thd) thd->lex->first_select_lex()->context.first_name_resolution_table= second_table; - return mysql_insert_select_prepare(thd); + return mysql_insert_select_prepare(thd, NULL); } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 5477d1f50bf..036c9aaf3e7 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -2080,7 +2080,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); opt_persistent_stat_clause persistent_stat_spec persistent_column_stat_spec persistent_index_stat_spec table_column_list table_index_list table_index_name - check start checksum + check start checksum opt_returning field_list field_list_item kill key_def constraint_def keycache_list keycache_list_or_parts assign_to_keycache assign_to_keycache_parts @@ -13477,7 +13477,8 @@ insert: { Select->set_lock_for_tables($4, true); } - insert_field_spec opt_insert_update stmt_end {} + insert_field_spec opt_insert_update opt_returning + stmt_end {} ; replace: @@ -13490,7 +13491,8 @@ replace: { Select->set_lock_for_tables($4, true); } - insert_field_spec stmt_end {} + insert_field_spec opt_returning + stmt_end {} ; insert_start: { @@ -13890,7 +13892,7 @@ single_multi: opt_where_clause opt_order_clause delete_limit_clause - opt_select_expressions + opt_returning { if ($3) Select->order_list= *($3); @@ -13920,9 +13922,28 @@ single_multi: } stmt_end {} ; -opt_select_expressions: - /* empty */ - | RETURNING_SYM select_item_list +opt_returning: + /* empty */ + { + DBUG_ASSERT(!Lex->has_returning()); + } + | RETURNING_SYM + { + DBUG_ASSERT(!Lex->has_returning()); + if (($<num>$= (Select != Lex->returning()))) + { + SELECT_LEX *sl= Lex->returning(); + sl->set_master_unit(0); + Select->add_slave(Lex->create_unit(sl)); + sl->include_global((st_select_lex_node**)&Lex->all_selects_list); + Lex->push_select(sl); + } + } + select_item_list + { + if ($<num>2) + Lex->pop_select(); + } ; table_wild_list: diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index 976e239beeb..471364e29b7 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -1555,7 +1555,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); opt_persistent_stat_clause persistent_stat_spec persistent_column_stat_spec persistent_index_stat_spec table_column_list table_index_list table_index_name - check start checksum + check start checksum opt_returning field_list field_list_item kill key_def constraint_def keycache_list keycache_list_or_parts assign_to_keycache assign_to_keycache_parts @@ -13594,7 +13594,8 @@ insert: { Select->set_lock_for_tables($4, true); } - insert_field_spec opt_insert_update stmt_end {} + insert_field_spec opt_insert_update opt_returning + stmt_end {} ; replace: @@ -13607,7 +13608,8 @@ replace: { Select->set_lock_for_tables($4, true); } - insert_field_spec stmt_end {} + insert_field_spec opt_returning + stmt_end {} ; insert_start: { @@ -14007,7 +14009,7 @@ single_multi: opt_where_clause opt_order_clause delete_limit_clause - opt_select_expressions + opt_returning { if ($3) Select->order_list= *($3); @@ -14037,9 +14039,28 @@ single_multi: } stmt_end {} ; -opt_select_expressions: - /* empty */ - | RETURNING_SYM select_item_list +opt_returning: + /* empty */ + { + DBUG_ASSERT(!Lex->has_returning()); + } + | RETURNING_SYM + { + DBUG_ASSERT(!Lex->has_returning()); + if (($<num>$= (Select != Lex->returning()))) + { + SELECT_LEX *sl= Lex->returning(); + sl->set_master_unit(0); + Select->add_slave(Lex->create_unit(sl)); + sl->include_global((st_select_lex_node**)&Lex->all_selects_list); + Lex->push_select(sl); + } + } + select_item_list + { + if ($<num>2) + Lex->pop_select(); + } ; table_wild_list: |