diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 5b87c554f5..f951cb33a9 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -3032,6 +3032,52 @@ rewriteTargetView(Query *parsetree, Relation view) elog(ERROR, "attribute number %d not found in view targetlist", tle->resno); } + + /* + * Do the same for targetlists related to handling ON CONFLICT + * DO UPDATE. + */ + if (parsetree->onConflict && + parsetree->onConflict->action == ONCONFLICT_UPDATE) + { + foreach(lc, parsetree->onConflict->onConflictSet) + { + TargetEntry *tle = (TargetEntry *) lfirst(lc); + TargetEntry *view_tle; + + if (tle->resjunk) + continue; + + view_tle = get_tle_by_resno(view_targetlist, tle->resno); + if (view_tle != NULL && !view_tle->resjunk && IsA(view_tle->expr, Var)) + tle->resno = ((Var *) view_tle->expr)->varattno; + else + elog(ERROR, "attribute number %d not found in view targetlist", + tle->resno); + } + + /* + * exclRelTlist contains Var nodes corresponding to all attributes + * of the target (view) relation, although the corresponding + * entries in view_targetlist may not actually be Vars. Modify + * resnos of only those for which we found a Var node in + * view_targetlist. + */ + foreach(lc, parsetree->onConflict->exclRelTlist) + { + TargetEntry *tle = (TargetEntry *) lfirst(lc); + TargetEntry *view_tle; + + if (tle->resjunk) + continue; + + view_tle = get_tle_by_resno(view_targetlist, tle->resno); + + if (view_tle != NULL && !view_tle->resjunk && + IsA(view_tle->expr, Var)) + tle->resno = ((Var *) view_tle->expr)->varattno; + } + } } /* diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index b34bab4b29..cf5bd9584e 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -2578,3 +2578,20 @@ ERROR: new row violates check option for view "wcowrtest_v2" DETAIL: Failing row contains (2, no such row in sometable). drop view wcowrtest_v, wcowrtest_v2; drop table wcowrtest, sometable; +-- Check INSERT .. ON CONFLICT DO UPDATE works correctly when the view's +-- columns may be ordered differently than the underlying table's. +create table uv_iocu_tab (a text unique, b float); +insert into uv_iocu_tab values ('xyxyxy', 1); +-- note the different order of columns in the view +create view uv_iocu_view as select b, a from uv_iocu_tab; +insert into uv_iocu_view (a, b) values ('xyxyxy', 2) on conflict (a) do update set b = uv_iocu_view.b; +insert into uv_iocu_view (a, b) values ('xyxyxy', 3) on conflict (a) do update set b = excluded.b; +-- should display 'xyxyxy, 3' +select * from uv_iocu_view; + b | a +---+-------- + 3 | xyxyxy +(1 row) + +drop view uv_iocu_view; +drop table uv_iocu_tab; diff --git a/src/test/regress/sql/updatable_views.sql b/src/test/regress/sql/updatable_views.sql index a7786b26e9..cd3f7a9f33 100644 --- a/src/test/regress/sql/updatable_views.sql +++ b/src/test/regress/sql/updatable_views.sql @@ -1244,3 +1244,16 @@ insert into wcowrtest_v2 values (2, 'no such row in sometable'); drop view wcowrtest_v, wcowrtest_v2; drop table wcowrtest, sometable; + +-- Check INSERT .. ON CONFLICT DO UPDATE works correctly when the view's +-- columns may be ordered differently than the underlying table's. +create table uv_iocu_tab (a text unique, b float); +insert into uv_iocu_tab values ('xyxyxy', 1); +-- note the different order of columns in the view +create view uv_iocu_view as select b, a from uv_iocu_tab; +insert into uv_iocu_view (a, b) values ('xyxyxy', 2) on conflict (a) do update set b = uv_iocu_view.b; +insert into uv_iocu_view (a, b) values ('xyxyxy', 3) on conflict (a) do update set b = excluded.b; +-- should display 'xyxyxy, 3' +select * from uv_iocu_view; +drop view uv_iocu_view; +drop table uv_iocu_tab;