Skip to content

Commit c1ca3a1

Browse files
committed
Fix bug around assignment expressions containing indirections.
Handling of assigned-to expressions with indirection (e.g. set f1[1] = 3) was broken for ON CONFLICT DO UPDATE. The problem was that ParseState was consulted to determine if an INSERT-appropriate or UPDATE-appropriate behavior should be used when transforming expressions with indirections. When the wrong path was taken the old row was substituted with NULL, leading to wrong results.. To fix remove p_is_update and only use p_is_insert to decide how to transform the assignment expression, and uset p_is_insert while parsing the on conflict statement. This isn't particularly pretty, but it's not any worse than before. Author: Peter Geoghegan, slightly edited by me Discussion: CAM3SWZS8RPvA=KFxADZWw3wAHnnbxMxDzkEC6fNaFc7zSm411w@mail.gmail.com Backpatch: 9.5, where the feature was introduced
1 parent 16c33c5 commit c1ca3a1

File tree

4 files changed

+41
-2
lines changed

4 files changed

+41
-2
lines changed

src/backend/parser/analyze.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,12 @@ transformOnConflictClause(ParseState *pstate,
891891
/* Process DO UPDATE */
892892
if (onConflictClause->action == ONCONFLICT_UPDATE)
893893
{
894+
/*
895+
* All INSERT expressions have been parsed, get ready for potentially
896+
* existing SET statements that need to be processed like an UPDATE.
897+
*/
898+
pstate->p_is_insert = false;
899+
894900
exclRte = addRangeTableEntryForRelation(pstate,
895901
pstate->p_target_relation,
896902
makeAlias("excluded", NIL),
@@ -1999,7 +2005,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
19992005
Node *qual;
20002006

20012007
qry->commandType = CMD_UPDATE;
2002-
pstate->p_is_update = true;
2008+
pstate->p_is_insert = false;
20032009

20042010
/* process the WITH clause independently of all else */
20052011
if (stmt->withClause)

src/include/parser/parse_node.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,6 @@ struct ParseState
152152
bool p_hasSubLinks;
153153
bool p_hasModifyingCTE;
154154
bool p_is_insert;
155-
bool p_is_update;
156155
bool p_locked_from_parent;
157156
Relation p_target_relation;
158157
RangeTblEntry *p_target_rangetblentry;

src/test/regress/expected/arrays.out

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1116,6 +1116,27 @@ select * from arr_tbl where f1 >= '{1,2,3}' and f1 < '{1,5,3}';
11161116
{1,2,10}
11171117
(2 rows)
11181118

1119+
-- test ON CONFLICT DO UPDATE with arrays
1120+
create temp table arr_pk_tbl (pk int4 primary key, f1 int[]);
1121+
insert into arr_pk_tbl values (1, '{1,2,3}');
1122+
insert into arr_pk_tbl values (1, '{3,4,5}') on conflict (pk)
1123+
do update set f1[1] = excluded.f1[1], f1[3] = excluded.f1[3]
1124+
returning pk, f1;
1125+
pk | f1
1126+
----+---------
1127+
1 | {3,2,5}
1128+
(1 row)
1129+
1130+
insert into arr_pk_tbl(pk, f1[1:2]) values (1, '{6,7,8}') on conflict (pk)
1131+
do update set f1[1] = excluded.f1[1],
1132+
f1[2] = excluded.f1[2],
1133+
f1[3] = excluded.f1[3]
1134+
returning pk, f1;
1135+
pk | f1
1136+
----+------------
1137+
1 | {6,7,NULL}
1138+
(1 row)
1139+
11191140
-- note: if above selects don't produce the expected tuple order,
11201141
-- then you didn't get an indexscan plan, and something is busted.
11211142
reset enable_seqscan;

src/test/regress/sql/arrays.sql

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,19 @@ set enable_seqscan to off;
306306
set enable_bitmapscan to off;
307307
select * from arr_tbl where f1 > '{1,2,3}' and f1 <= '{1,5,3}';
308308
select * from arr_tbl where f1 >= '{1,2,3}' and f1 < '{1,5,3}';
309+
310+
-- test ON CONFLICT DO UPDATE with arrays
311+
create temp table arr_pk_tbl (pk int4 primary key, f1 int[]);
312+
insert into arr_pk_tbl values (1, '{1,2,3}');
313+
insert into arr_pk_tbl values (1, '{3,4,5}') on conflict (pk)
314+
do update set f1[1] = excluded.f1[1], f1[3] = excluded.f1[3]
315+
returning pk, f1;
316+
insert into arr_pk_tbl(pk, f1[1:2]) values (1, '{6,7,8}') on conflict (pk)
317+
do update set f1[1] = excluded.f1[1],
318+
f1[2] = excluded.f1[2],
319+
f1[3] = excluded.f1[3]
320+
returning pk, f1;
321+
309322
-- note: if above selects don't produce the expected tuple order,
310323
-- then you didn't get an indexscan plan, and something is busted.
311324
reset enable_seqscan;

0 commit comments

Comments
 (0)