Commit c1ca3a19 authored by Andres Freund's avatar Andres Freund

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
parent 16c33c50
...@@ -891,6 +891,12 @@ transformOnConflictClause(ParseState *pstate, ...@@ -891,6 +891,12 @@ transformOnConflictClause(ParseState *pstate,
/* Process DO UPDATE */ /* Process DO UPDATE */
if (onConflictClause->action == ONCONFLICT_UPDATE) if (onConflictClause->action == ONCONFLICT_UPDATE)
{ {
/*
* All INSERT expressions have been parsed, get ready for potentially
* existing SET statements that need to be processed like an UPDATE.
*/
pstate->p_is_insert = false;
exclRte = addRangeTableEntryForRelation(pstate, exclRte = addRangeTableEntryForRelation(pstate,
pstate->p_target_relation, pstate->p_target_relation,
makeAlias("excluded", NIL), makeAlias("excluded", NIL),
...@@ -1999,7 +2005,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) ...@@ -1999,7 +2005,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
Node *qual; Node *qual;
qry->commandType = CMD_UPDATE; qry->commandType = CMD_UPDATE;
pstate->p_is_update = true; pstate->p_is_insert = false;
/* process the WITH clause independently of all else */ /* process the WITH clause independently of all else */
if (stmt->withClause) if (stmt->withClause)
......
...@@ -152,7 +152,6 @@ struct ParseState ...@@ -152,7 +152,6 @@ struct ParseState
bool p_hasSubLinks; bool p_hasSubLinks;
bool p_hasModifyingCTE; bool p_hasModifyingCTE;
bool p_is_insert; bool p_is_insert;
bool p_is_update;
bool p_locked_from_parent; bool p_locked_from_parent;
Relation p_target_relation; Relation p_target_relation;
RangeTblEntry *p_target_rangetblentry; RangeTblEntry *p_target_rangetblentry;
......
...@@ -1116,6 +1116,27 @@ select * from arr_tbl where f1 >= '{1,2,3}' and f1 < '{1,5,3}'; ...@@ -1116,6 +1116,27 @@ select * from arr_tbl where f1 >= '{1,2,3}' and f1 < '{1,5,3}';
{1,2,10} {1,2,10}
(2 rows) (2 rows)
-- test ON CONFLICT DO UPDATE with arrays
create temp table arr_pk_tbl (pk int4 primary key, f1 int[]);
insert into arr_pk_tbl values (1, '{1,2,3}');
insert into arr_pk_tbl values (1, '{3,4,5}') on conflict (pk)
do update set f1[1] = excluded.f1[1], f1[3] = excluded.f1[3]
returning pk, f1;
pk | f1
----+---------
1 | {3,2,5}
(1 row)
insert into arr_pk_tbl(pk, f1[1:2]) values (1, '{6,7,8}') on conflict (pk)
do update set f1[1] = excluded.f1[1],
f1[2] = excluded.f1[2],
f1[3] = excluded.f1[3]
returning pk, f1;
pk | f1
----+------------
1 | {6,7,NULL}
(1 row)
-- note: if above selects don't produce the expected tuple order, -- note: if above selects don't produce the expected tuple order,
-- then you didn't get an indexscan plan, and something is busted. -- then you didn't get an indexscan plan, and something is busted.
reset enable_seqscan; reset enable_seqscan;
......
...@@ -306,6 +306,19 @@ set enable_seqscan to off; ...@@ -306,6 +306,19 @@ set enable_seqscan to off;
set enable_bitmapscan to off; set enable_bitmapscan to off;
select * from arr_tbl where f1 > '{1,2,3}' and f1 <= '{1,5,3}'; select * from arr_tbl where f1 > '{1,2,3}' and f1 <= '{1,5,3}';
select * from arr_tbl where f1 >= '{1,2,3}' and f1 < '{1,5,3}'; select * from arr_tbl where f1 >= '{1,2,3}' and f1 < '{1,5,3}';
-- test ON CONFLICT DO UPDATE with arrays
create temp table arr_pk_tbl (pk int4 primary key, f1 int[]);
insert into arr_pk_tbl values (1, '{1,2,3}');
insert into arr_pk_tbl values (1, '{3,4,5}') on conflict (pk)
do update set f1[1] = excluded.f1[1], f1[3] = excluded.f1[3]
returning pk, f1;
insert into arr_pk_tbl(pk, f1[1:2]) values (1, '{6,7,8}') on conflict (pk)
do update set f1[1] = excluded.f1[1],
f1[2] = excluded.f1[2],
f1[3] = excluded.f1[3]
returning pk, f1;
-- note: if above selects don't produce the expected tuple order, -- note: if above selects don't produce the expected tuple order,
-- then you didn't get an indexscan plan, and something is busted. -- then you didn't get an indexscan plan, and something is busted.
reset enable_seqscan; reset enable_seqscan;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment