Skip to content
/ server Public

Commit ff235ab

Browse files
committed
MDEV-29919: Support INSERT ... AS alias ON DUPLICATE KEY UPDATE
1 parent 366de0a commit ff235ab

File tree

6 files changed

+216
-1
lines changed

6 files changed

+216
-1
lines changed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#
2+
# Test setup
3+
#
4+
CREATE TABLE t1 (
5+
a INT PRIMARY KEY,
6+
b INT,
7+
c INT
8+
);
9+
#
10+
# Test 1: Basic INSERT AS alias ON DUPLICATE KEY UPDATE
11+
#
12+
INSERT INTO t1 VALUES (1, 10, 100);
13+
INSERT INTO t1 VALUES (1, 20, 200) AS new
14+
ON DUPLICATE KEY UPDATE b = new.b, c = new.c;
15+
SELECT * FROM t1;
16+
a b c
17+
1 20 200
18+
#
19+
# Test 2: Multiple rows with AS alias
20+
#
21+
TRUNCATE TABLE t1;
22+
INSERT INTO t1 VALUES (1, 10, 100);
23+
INSERT INTO t1 VALUES (1, 20, 200), (2, 30, 300) AS new
24+
ON DUPLICATE KEY UPDATE b = new.b;
25+
SELECT * FROM t1 ORDER BY a;
26+
a b c
27+
1 20 100
28+
2 30 300
29+
#
30+
# Test 3: Expression using alias columns
31+
#
32+
TRUNCATE TABLE t1;
33+
INSERT INTO t1 VALUES (1, 10, 100);
34+
INSERT INTO t1 VALUES (1, 5, 50) AS new
35+
ON DUPLICATE KEY UPDATE b = new.b + new.c, c = new.a * 10;
36+
SELECT * FROM t1;
37+
a b c
38+
1 55 10
39+
#
40+
# Test 4: Backward compatibility - VALUES() function still works
41+
#
42+
TRUNCATE TABLE t1;
43+
INSERT INTO t1 VALUES (1, 10, 100);
44+
INSERT INTO t1 VALUES (1, 20, 200)
45+
ON DUPLICATE KEY UPDATE b = VALUES(b), c = VALUES(c);
46+
SELECT * FROM t1;
47+
a b c
48+
1 20 200
49+
#
50+
# Test 5: Mix of alias and table column references
51+
#
52+
TRUNCATE TABLE t1;
53+
INSERT INTO t1 VALUES (1, 10, 100);
54+
INSERT INTO t1 VALUES (1, 20, 200) AS new
55+
ON DUPLICATE KEY UPDATE b = new.b, c = t1.c + new.c;
56+
SELECT * FROM t1;
57+
a b c
58+
1 20 300
59+
#
60+
# Test 6: INSERT without ON DUPLICATE KEY (alias should be ignored)
61+
#
62+
TRUNCATE TABLE t1;
63+
INSERT INTO t1 VALUES (1, 10, 100) AS new;
64+
SELECT * FROM t1;
65+
a b c
66+
1 10 100
67+
#
68+
# Test 7: Different alias names
69+
#
70+
TRUNCATE TABLE t1;
71+
INSERT INTO t1 VALUES (1, 10, 100);
72+
INSERT INTO t1 VALUES (1, 99, 999) AS inserted_row
73+
ON DUPLICATE KEY UPDATE b = inserted_row.b, c = inserted_row.c;
74+
SELECT * FROM t1;
75+
a b c
76+
1 99 999
77+
#
78+
# Cleanup
79+
#
80+
DROP TABLE t1;
81+
#
82+
# End of MDEV-29919 tests
83+
#
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#
2+
# MDEV-29919: Support MySQL 8.0.20+ INSERT ... AS alias ON DUPLICATE KEY UPDATE syntax
3+
#
4+
# This test validates the new row alias syntax for INSERT ON DUPLICATE KEY UPDATE
5+
# which allows referencing inserted values using alias.column instead of VALUES(column)
6+
#
7+
# Skip PS protocol for now - alias resolution during fix_fields needs work for PS mode
8+
--source include/no_protocol.inc
9+
10+
--echo #
11+
--echo # Test setup
12+
--echo #
13+
CREATE TABLE t1 (
14+
a INT PRIMARY KEY,
15+
b INT,
16+
c INT
17+
);
18+
19+
--echo #
20+
--echo # Test 1: Basic INSERT AS alias ON DUPLICATE KEY UPDATE
21+
--echo #
22+
INSERT INTO t1 VALUES (1, 10, 100);
23+
INSERT INTO t1 VALUES (1, 20, 200) AS new
24+
ON DUPLICATE KEY UPDATE b = new.b, c = new.c;
25+
SELECT * FROM t1;
26+
27+
--echo #
28+
--echo # Test 2: Multiple rows with AS alias
29+
--echo #
30+
TRUNCATE TABLE t1;
31+
INSERT INTO t1 VALUES (1, 10, 100);
32+
INSERT INTO t1 VALUES (1, 20, 200), (2, 30, 300) AS new
33+
ON DUPLICATE KEY UPDATE b = new.b;
34+
SELECT * FROM t1 ORDER BY a;
35+
36+
--echo #
37+
--echo # Test 3: Expression using alias columns
38+
--echo #
39+
TRUNCATE TABLE t1;
40+
INSERT INTO t1 VALUES (1, 10, 100);
41+
INSERT INTO t1 VALUES (1, 5, 50) AS new
42+
ON DUPLICATE KEY UPDATE b = new.b + new.c, c = new.a * 10;
43+
SELECT * FROM t1;
44+
45+
--echo #
46+
--echo # Test 4: Backward compatibility - VALUES() function still works
47+
--echo #
48+
TRUNCATE TABLE t1;
49+
INSERT INTO t1 VALUES (1, 10, 100);
50+
INSERT INTO t1 VALUES (1, 20, 200)
51+
ON DUPLICATE KEY UPDATE b = VALUES(b), c = VALUES(c);
52+
SELECT * FROM t1;
53+
54+
--echo #
55+
--echo # Test 5: Mix of alias and table column references
56+
--echo #
57+
TRUNCATE TABLE t1;
58+
INSERT INTO t1 VALUES (1, 10, 100);
59+
INSERT INTO t1 VALUES (1, 20, 200) AS new
60+
ON DUPLICATE KEY UPDATE b = new.b, c = t1.c + new.c;
61+
SELECT * FROM t1;
62+
63+
--echo #
64+
--echo # Test 6: INSERT without ON DUPLICATE KEY (alias should be ignored)
65+
--echo #
66+
TRUNCATE TABLE t1;
67+
INSERT INTO t1 VALUES (1, 10, 100) AS new;
68+
SELECT * FROM t1;
69+
70+
--echo #
71+
--echo # Test 7: Different alias names
72+
--echo #
73+
TRUNCATE TABLE t1;
74+
INSERT INTO t1 VALUES (1, 10, 100);
75+
INSERT INTO t1 VALUES (1, 99, 999) AS inserted_row
76+
ON DUPLICATE KEY UPDATE b = inserted_row.b, c = inserted_row.c;
77+
SELECT * FROM t1;
78+
79+
--echo #
80+
--echo # Cleanup
81+
--echo #
82+
DROP TABLE t1;
83+
84+
--echo #
85+
--echo # End of MDEV-29919 tests
86+
--echo #

sql/item.cc

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6433,6 +6433,33 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
64336433
if (!field) // If field is not checked
64346434
{
64356435
TABLE_LIST *table_list;
6436+
6437+
/*
6438+
MDEV-29919: Handle INSERT ... VALUES (...) AS alias ON DUPLICATE KEY UPDATE
6439+
When we are in ON DUPLICATE KEY UPDATE and the table qualifier matches
6440+
the insert_values_alias, we should resolve this as VALUES(column).
6441+
*/
6442+
if (select &&
6443+
thd->lex->duplicates == DUP_UPDATE && thd->lex->sql_command == SQLCOM_INSERT &&
6444+
thd->lex->insert_values_alias.str &&
6445+
table_name.str &&
6446+
table_name.streq(thd->lex->insert_values_alias))
6447+
{
6448+
/*
6449+
Create a field reference without the alias qualifier,
6450+
then wrap it in Item_insert_value to get the inserted value.
6451+
*/
6452+
Item_field *field_ref= new (thd->mem_root)
6453+
Item_field(thd, context, db_name, Lex_cstring_strlen(NULL), field_name);
6454+
if (!field_ref)
6455+
return TRUE;
6456+
Item_insert_value *ins_val= new (thd->mem_root)
6457+
Item_insert_value(thd, context, field_ref);
6458+
if (!ins_val)
6459+
return TRUE;
6460+
*reference= ins_val;
6461+
return ins_val->fix_fields(thd, reference);
6462+
}
64366463
/*
64376464
In case of view, find_field_in_tables() write pointer to view field
64386465
expression to 'reference', i.e. it substitute that expression instead

sql/sql_lex.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1277,6 +1277,7 @@ void LEX::start(THD *thd_arg)
12771277
part_info= 0;
12781278
m_sql_cmd= NULL;
12791279
duplicates= DUP_ERROR;
1280+
insert_values_alias= null_clex_str;
12801281
spname= NULL;
12811282
spcont= NULL;
12821283
proc_list.first= 0;

sql/sql_lex.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3454,6 +3454,8 @@ struct LEX: public Query_tables_list
34543454
const char *clause_that_disallows_subselect;
34553455

34563456
enum enum_duplicates duplicates;
3457+
/* Alias for INSERT ... AS alias syntax (MDEV-29919) */
3458+
LEX_CSTRING insert_values_alias;
34573459
enum enum_tx_isolation tx_isolation;
34583460
enum enum_ha_read_modes ha_read_mode;
34593461
union {

sql/sql_yacc.yy

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9035,13 +9035,29 @@ table_value_constructor:
90359035
if (Lex->parsed_TVC_start())
90369036
MYSQL_YYABORT;
90379037
}
9038-
values_list
9038+
values_list opt_values_row_alias
90399039
{
90409040
if (!($$= Lex->parsed_TVC_end()))
90419041
MYSQL_YYABORT;
90429042
}
90439043
;
90449044

9045+
/*
9046+
Optional alias for the row(s) being inserted (MDEV-29919).
9047+
MySQL 8.0.20+ syntax: INSERT ... VALUES (...) AS alias
9048+
Used to reference new values in ON DUPLICATE KEY UPDATE clause.
9049+
*/
9050+
opt_values_row_alias:
9051+
/* empty */
9052+
{
9053+
Lex->insert_values_alias= null_clex_str;
9054+
}
9055+
| AS ident
9056+
{
9057+
Lex->insert_values_alias= $2;
9058+
}
9059+
;
9060+
90459061
opt_hint_comment:
90469062
/*empty */ { $$.init(); }
90479063
| HINT_COMMENT { $$= $1; }

0 commit comments

Comments
 (0)