mirror of
https://github.com/postgres/postgres.git
synced 2025-04-22 23:02:54 +03:00
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
This commit is contained in:
parent
16c33c50e1
commit
c1ca3a19df
@ -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)
|
|||||||
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
|
|||||||
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}';
|
|||||||
{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;
|
|||||||
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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user