On 2017/06/30 18:44, Etsuro Fujita wrote:
On 2017/06/16 21:29, Etsuro Fujita wrote:
I'll have second thought about this, so I'll mark this as waiting on author.

I spent quite a bit of time on this and came up with a solution for addressing the concern mentioned by Ashutosh [1]. The basic idea is, as I said before, to move rewriteTargetListUD from the rewriter to the planner (whether the update or delete is inherited or not), except for the view case. In case of inherited UPDATE/DELETE, the planner adds a necessary junk column needed for the update or delete for each child relation, without the assumption I made before about junk tlist entries, so I think this would be more robust for future changes as mentioned in [1]. It wouldn't work well that the planner does the same thing for the view case (ie, add a whole-row reference to the given target relation), unlike other cases, because what we need to do for that case is to add a whole-row reference to the view as the source for an INSTEAD OF trigger, not the target. So, ISTM that it's reasonable to handle that case in the rewriter as-is, not in the planner, but one thing I'd like to propose to simplify the code in rewriteHandler.c is to move the code for the view case in rewriteTargetListUD to ApplyRetrieveRule. By that change, we won't add a whole-row reference to the view in RewriteQuery, so we don't need this annoying thing in rewriteTargetView any more:

    /*
* For UPDATE/DELETE, rewriteTargetListUD will have added a wholerow junk * TLE for the view to the end of the targetlist, which we no longer need.
     * Remove it to avoid unnecessary work when we process the targetlist.
* Note that when we recurse through rewriteQuery a new junk TLE will be
     * added to allow the executor to find the proper row in the new target
     * relation.  (So, if we failed to do this, we might have multiple junk
     * TLEs with the same name, which would be disastrous.)
     */
    if (parsetree->commandType != CMD_INSERT)
    {
        TargetEntry *tle = (TargetEntry *) llast(parsetree->targetList);

        Assert(tle->resjunk);
        Assert(IsA(tle->expr, Var) &&
               ((Var *) tle->expr)->varno == parsetree->resultRelation &&
               ((Var *) tle->expr)->varattno == 0);
parsetree->targetList = list_delete_ptr(parsetree->targetList, tle);
    }

Attached is an updated version of the patch.  Comments are welcome!

Best regards,
Etsuro Fujita

[1] https://www.postgresql.org/message-id/CAFjFpRfTpamoUz6fNyk6gPh%3DecfBJjbUHJNKbDxscpyPJ3FfjQ%40mail.gmail.com
*** a/contrib/postgres_fdw/expected/postgres_fdw.out
--- b/contrib/postgres_fdw/expected/postgres_fdw.out
***************
*** 6924,6929 **** update bar set f2 = f2 + 100 returning *;
--- 6924,6988 ----
    7 | 277
  (6 rows)
  
+ -- Test that UPDATE/DELETE with inherited target works with row-level triggers
+ CREATE TRIGGER trig_row_before
+ BEFORE UPDATE OR DELETE ON bar2
+ FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo');
+ CREATE TRIGGER trig_row_after
+ AFTER UPDATE OR DELETE ON bar2
+ FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo');
+ explain (verbose, costs off)
+ update bar set f2 = f2 + 100;
+                                       QUERY PLAN                              
        
+ 
--------------------------------------------------------------------------------------
+  Update on public.bar
+    Update on public.bar
+    Foreign Update on public.bar2
+      Remote SQL: UPDATE public.loct2 SET f2 = $2 WHERE ctid = $1 RETURNING 
f1, f2, f3
+    ->  Seq Scan on public.bar
+          Output: bar.f1, (bar.f2 + 100), bar.ctid
+    ->  Foreign Scan on public.bar2
+          Output: bar2.f1, (bar2.f2 + 100), bar2.f3, bar2.ctid, bar2.*
+          Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct2 FOR UPDATE
+ (9 rows)
+ 
+ update bar set f2 = f2 + 100;
+ NOTICE:  trig_row_before(23, skidoo) BEFORE ROW UPDATE ON bar2
+ NOTICE:  OLD: (3,333,33),NEW: (3,433,33)
+ NOTICE:  trig_row_before(23, skidoo) BEFORE ROW UPDATE ON bar2
+ NOTICE:  OLD: (4,344,44),NEW: (4,444,44)
+ NOTICE:  trig_row_before(23, skidoo) BEFORE ROW UPDATE ON bar2
+ NOTICE:  OLD: (7,277,77),NEW: (7,377,77)
+ NOTICE:  trig_row_after(23, skidoo) AFTER ROW UPDATE ON bar2
+ NOTICE:  OLD: (3,333,33),NEW: (3,433,33)
+ NOTICE:  trig_row_after(23, skidoo) AFTER ROW UPDATE ON bar2
+ NOTICE:  OLD: (4,344,44),NEW: (4,444,44)
+ NOTICE:  trig_row_after(23, skidoo) AFTER ROW UPDATE ON bar2
+ NOTICE:  OLD: (7,277,77),NEW: (7,377,77)
+ explain (verbose, costs off)
+ delete from bar where f2 < 400;
+                                          QUERY PLAN                           
               
+ 
---------------------------------------------------------------------------------------------
+  Delete on public.bar
+    Delete on public.bar
+    Foreign Delete on public.bar2
+      Remote SQL: DELETE FROM public.loct2 WHERE ctid = $1 RETURNING f1, f2, f3
+    ->  Seq Scan on public.bar
+          Output: bar.ctid
+          Filter: (bar.f2 < 400)
+    ->  Foreign Scan on public.bar2
+          Output: bar2.ctid, bar2.*
+          Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct2 WHERE ((f2 < 
400)) FOR UPDATE
+ (10 rows)
+ 
+ delete from bar where f2 < 400;
+ NOTICE:  trig_row_before(23, skidoo) BEFORE ROW DELETE ON bar2
+ NOTICE:  OLD: (7,377,77)
+ NOTICE:  trig_row_after(23, skidoo) AFTER ROW DELETE ON bar2
+ NOTICE:  OLD: (7,377,77)
+ -- cleanup
+ DROP TRIGGER trig_row_before ON bar2;
+ DROP TRIGGER trig_row_after ON bar2;
  drop table foo cascade;
  NOTICE:  drop cascades to foreign table foo2
  drop table bar cascade;
*** a/contrib/postgres_fdw/sql/postgres_fdw.sql
--- b/contrib/postgres_fdw/sql/postgres_fdw.sql
***************
*** 1609,1614 **** explain (verbose, costs off)
--- 1609,1634 ----
  update bar set f2 = f2 + 100 returning *;
  update bar set f2 = f2 + 100 returning *;
  
+ -- Test that UPDATE/DELETE with inherited target works with row-level triggers
+ CREATE TRIGGER trig_row_before
+ BEFORE UPDATE OR DELETE ON bar2
+ FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo');
+ 
+ CREATE TRIGGER trig_row_after
+ AFTER UPDATE OR DELETE ON bar2
+ FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo');
+ 
+ explain (verbose, costs off)
+ update bar set f2 = f2 + 100;
+ update bar set f2 = f2 + 100;
+ 
+ explain (verbose, costs off)
+ delete from bar where f2 < 400;
+ delete from bar where f2 < 400;
+ 
+ -- cleanup
+ DROP TRIGGER trig_row_before ON bar2;
+ DROP TRIGGER trig_row_after ON bar2;
  drop table foo cascade;
  drop table bar cascade;
  drop table loct1;
*** a/doc/src/sgml/rules.sgml
--- b/doc/src/sgml/rules.sgml
***************
*** 167,178 ****
  
      <para>
          <command>DELETE</command> commands don't need a normal target list
!         because they don't produce any result.  Instead, the rule system
          adds a special <acronym>CTID</> entry to the empty target list,
          to allow the executor to find the row to be deleted.
          (<acronym>CTID</> is added when the result relation is an ordinary
!         table.  If it is a view, a whole-row variable is added instead,
!         as described in <xref linkend="rules-views-update">.)
      </para>
  
      <para>
--- 167,178 ----
  
      <para>
          <command>DELETE</command> commands don't need a normal target list
!         because they don't produce any result.  Instead, the planner
          adds a special <acronym>CTID</> entry to the empty target list,
          to allow the executor to find the row to be deleted.
          (<acronym>CTID</> is added when the result relation is an ordinary
!         table.  If it is a view, a whole-row variable is added instead, by
!         the rule system, as described in <xref linkend="rules-views-update">.)
      </para>
  
      <para>
***************
*** 194,200 ****
          column = expression</literal> part of the command.  The planner will
          handle missing columns by inserting expressions that copy the values
          from the old row into the new one.  Just as for <command>DELETE</>,
!         the rule system adds a <acronym>CTID</> or whole-row variable so that
          the executor can identify the old row to be updated.
      </para>
  
--- 194,200 ----
          column = expression</literal> part of the command.  The planner will
          handle missing columns by inserting expressions that copy the values
          from the old row into the new one.  Just as for <command>DELETE</>,
!         a <acronym>CTID</> or whole-row variable is added so that
          the executor can identify the old row to be updated.
      </para>
  
*** a/src/backend/executor/nodeModifyTable.c
--- b/src/backend/executor/nodeModifyTable.c
***************
*** 1661,1666 **** ExecModifyTable(ModifyTableState *node)
--- 1661,1667 ----
                EvalPlanQualSetSlot(&node->mt_epqstate, planSlot);
                slot = planSlot;
  
+               tupleid = NULL;
                oldtuple = NULL;
                if (junkfilter != NULL)
                {
*** a/src/backend/optimizer/plan/planner.c
--- b/src/backend/optimizer/plan/planner.c
***************
*** 1458,1464 **** grouping_planner(PlannerInfo *root, bool inheritance_update,
                                 double tuple_fraction)
  {
        Query      *parse = root->parse;
!       List       *tlist = parse->targetList;
        int64           offset_est = 0;
        int64           count_est = 0;
        double          limit_tuples = -1.0;
--- 1458,1464 ----
                                 double tuple_fraction)
  {
        Query      *parse = root->parse;
!       List       *tlist;
        int64           offset_est = 0;
        int64           count_est = 0;
        double          limit_tuples = -1.0;
***************
*** 1588,1594 **** grouping_planner(PlannerInfo *root, bool inheritance_update,
                }
  
                /* Preprocess targetlist */
!               tlist = preprocess_targetlist(root, tlist);
  
                if (parse->onConflict)
                        parse->onConflict->onConflictSet =
--- 1588,1594 ----
                }
  
                /* Preprocess targetlist */
!               tlist = preprocess_targetlist(root);
  
                if (parse->onConflict)
                        parse->onConflict->onConflictSet =
*** a/src/backend/optimizer/prep/preptlist.c
--- b/src/backend/optimizer/prep/preptlist.c
***************
*** 4,23 ****
   *      Routines to preprocess the parse tree target list
   *
   * For INSERT and UPDATE queries, the targetlist must contain an entry for
!  * each attribute of the target relation in the correct order.  For all query
!  * types, we may need to add junk tlist entries for Vars used in the RETURNING
!  * list and row ID information needed for SELECT FOR UPDATE locking and/or
!  * EvalPlanQual checking.
   *
!  * The rewriter's rewriteTargetListIU and rewriteTargetListUD routines
!  * also do preprocessing of the targetlist.  The division of labor between
!  * here and there is partially historical, but it's not entirely arbitrary.
!  * In particular, consider an UPDATE across an inheritance tree.  What the
!  * rewriter does need be done only once (because it depends only on the
!  * properties of the parent relation).  What's done here has to be done over
!  * again for each child relation, because it depends on the column list of
!  * the child, which might have more columns and/or a different column order
!  * than the parent.
   *
   * The fact that rewriteTargetListIU sorts non-resjunk tlist entries by column
   * position, which expand_targetlist depends on, violates the above comment
--- 4,24 ----
   *      Routines to preprocess the parse tree target list
   *
   * For INSERT and UPDATE queries, the targetlist must contain an entry for
!  * each attribute of the target relation in the correct order.  For UPDATE and
!  * DELETE queries, it must also contain a junk tlist entry needed to allow the
!  * executor to identify the physical locations of the rows to be updated or
!  * deleted.  For all query types, we may need to add junk tlist entries for
!  * Vars used in the RETURNING list and row ID information needed for SELECT
!  * FOR UPDATE locking and/or EvalPlanQual checking.
   *
!  * The rewriter's rewriteTargetListIU routine also does preprocessing of the
!  * targetlist.  The division of labor between here and there is partially
!  * historical, but it's not entirely arbitrary.  In particular, consider an
!  * UPDATE across an inheritance tree.  What the rewriter does need be done
!  * only once (because it depends only on the properties of the parent
!  * relation).  What's done here has to be done over again for each child
!  * relation, because it depends on the column list of the child, which might
!  * have more columns and/or a different column order than the parent.
   *
   * The fact that rewriteTargetListIU sorts non-resjunk tlist entries by column
   * position, which expand_targetlist depends on, violates the above comment
***************
*** 41,46 ****
--- 42,48 ----
  #include "access/heapam.h"
  #include "access/sysattr.h"
  #include "catalog/pg_type.h"
+ #include "foreign/fdwapi.h"
  #include "nodes/makefuncs.h"
  #include "optimizer/prep.h"
  #include "optimizer/tlist.h"
***************
*** 52,57 ****
--- 54,61 ----
  
  static List *expand_targetlist(List *tlist, int command_type,
                                  Index result_relation, List *range_table);
+ static void rewrite_targetlist(Query *parse,
+                                 Index result_relation, List *range_table);
  
  
  /*
***************
*** 61,72 **** static List *expand_targetlist(List *tlist, int command_type,
   *      Returns the new targetlist.
   */
  List *
! preprocess_targetlist(PlannerInfo *root, List *tlist)
  {
        Query      *parse = root->parse;
        int                     result_relation = parse->resultRelation;
        List       *range_table = parse->rtable;
        CmdType         command_type = parse->commandType;
        ListCell   *lc;
  
        /*
--- 65,77 ----
   *      Returns the new targetlist.
   */
  List *
! preprocess_targetlist(PlannerInfo *root)
  {
        Query      *parse = root->parse;
        int                     result_relation = parse->resultRelation;
        List       *range_table = parse->rtable;
        CmdType         command_type = parse->commandType;
+       List       *tlist;
        ListCell   *lc;
  
        /*
***************
*** 82,87 **** preprocess_targetlist(PlannerInfo *root, List *tlist)
--- 87,102 ----
        }
  
        /*
+        * For UPDATE/DELETE, add a necessary junk column needed to allow the
+        * executor to identify the physical locations of the rows to be updated
+        * or deleted.
+        */
+       if (command_type == CMD_UPDATE || command_type == CMD_DELETE)
+               rewrite_targetlist(parse, result_relation, range_table);
+ 
+       tlist = parse->targetList;
+ 
+       /*
         * for heap_form_tuple to work, the targetlist must match the exact 
order
         * of the attributes. We also need to fill in any missing attributes. 
-ay
         * 10/94
***************
*** 391,396 **** expand_targetlist(List *tlist, int command_type,
--- 406,504 ----
        return new_tlist;
  }
  
+ /*
+  * rewrite_targetlist - rewrite UPDATE/DELETE targetlist as needed
+  *
+  * This function adds a "junk" TLE that is needed to allow the executor to
+  * find the original row for the update or delete.  When the target relation
+  * is a regular table, the junk TLE emits the ctid attribute of the original
+  * row.  When the target relation is a foreign table, we let the FDW decide
+  * what to add.
+  *
+  * For UPDATE queries, this is applied after rewriteTargetListIU.  The
+  * ordering isn't actually critical at the moment.
+  */
+ static void
+ rewrite_targetlist(Query *parse, Index result_relation, List *range_table)
+ {
+       Var                *var = NULL;
+       const char *attrname;
+       TargetEntry *tle;
+       RangeTblEntry *target_rte;
+       Relation target_relation;
+ 
+       target_rte = rt_fetch(result_relation, range_table);
+ 
+       /*
+        * We assume that the relation was already locked by the rewriter, so
+        * we need no lock here.
+        */
+       target_relation = heap_open(target_rte->relid, NoLock);
+ 
+       if (target_relation->rd_rel->relkind == RELKIND_RELATION ||
+               target_relation->rd_rel->relkind == RELKIND_MATVIEW ||
+               target_relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+       {
+               /*
+                * Emit CTID so that executor can find the row to update or 
delete.
+                */
+               var = makeVar(result_relation,
+                                         SelfItemPointerAttributeNumber,
+                                         TIDOID,
+                                         -1,
+                                         InvalidOid,
+                                         0);
+ 
+               attrname = "ctid";
+       }
+       else if (target_relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+       {
+               /*
+                * Let the foreign table's FDW add whatever junk TLEs it wants.
+                */
+               FdwRoutine *fdwroutine;
+ 
+               fdwroutine = GetFdwRoutineForRelation(target_relation, false);
+ 
+               if (fdwroutine->AddForeignUpdateTargets != NULL)
+                       fdwroutine->AddForeignUpdateTargets(parse, target_rte,
+                                                                               
                target_relation);
+ 
+               /*
+                * If we have a row-level trigger corresponding to the 
operation, emit
+                * a whole-row Var so that executor will have the "old" row to 
pass to
+                * the trigger.  Alas, this misses system columns.
+                */
+               if (target_relation->trigdesc &&
+                       ((parse->commandType == CMD_UPDATE &&
+                         (target_relation->trigdesc->trig_update_after_row ||
+                          target_relation->trigdesc->trig_update_before_row)) 
||
+                        (parse->commandType == CMD_DELETE &&
+                         (target_relation->trigdesc->trig_delete_after_row ||
+                          target_relation->trigdesc->trig_delete_before_row))))
+               {
+                       var = makeWholeRowVar(target_rte,
+                                                                 
result_relation,
+                                                                 0,
+                                                                 false);
+ 
+                       attrname = "wholerow";
+               }
+       }
+ 
+       if (var != NULL)
+       {
+               tle = makeTargetEntry((Expr *) var,
+                                                         
list_length(parse->targetList) + 1,
+                                                         pstrdup(attrname),
+                                                         true);
+ 
+               parse->targetList = lappend(parse->targetList, tle);
+       }
+ 
+       heap_close(target_relation, NoLock);
+ }
+ 
  
  /*
   * Locate PlanRowMark for given RT index, or return NULL if none
*** a/src/backend/rewrite/rewriteHandler.c
--- b/src/backend/rewrite/rewriteHandler.c
***************
*** 72,79 **** static TargetEntry *process_matched_tle(TargetEntry *src_tle,
  static Node *get_assignment_input(Node *node);
  static void rewriteValuesRTE(RangeTblEntry *rte, Relation target_relation,
                                 List *attrnos);
- static void rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte,
-                                       Relation target_relation);
  static void markQueryForLocking(Query *qry, Node *jtnode,
                                        LockClauseStrength strength, 
LockWaitPolicy waitPolicy,
                                        bool pushedDown);
--- 72,77 ----
***************
*** 1288,1390 **** rewriteValuesRTE(RangeTblEntry *rte, Relation 
target_relation, List *attrnos)
  
  
  /*
-  * rewriteTargetListUD - rewrite UPDATE/DELETE targetlist as needed
-  *
-  * This function adds a "junk" TLE that is needed to allow the executor to
-  * find the original row for the update or delete.  When the target relation
-  * is a regular table, the junk TLE emits the ctid attribute of the original
-  * row.  When the target relation is a view, there is no ctid, so we instead
-  * emit a whole-row Var that will contain the "old" values of the view row.
-  * If it's a foreign table, we let the FDW decide what to add.
-  *
-  * For UPDATE queries, this is applied after rewriteTargetListIU.  The
-  * ordering isn't actually critical at the moment.
-  */
- static void
- rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte,
-                                       Relation target_relation)
- {
-       Var                *var = NULL;
-       const char *attrname;
-       TargetEntry *tle;
- 
-       if (target_relation->rd_rel->relkind == RELKIND_RELATION ||
-               target_relation->rd_rel->relkind == RELKIND_MATVIEW ||
-               target_relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
-       {
-               /*
-                * Emit CTID so that executor can find the row to update or 
delete.
-                */
-               var = makeVar(parsetree->resultRelation,
-                                         SelfItemPointerAttributeNumber,
-                                         TIDOID,
-                                         -1,
-                                         InvalidOid,
-                                         0);
- 
-               attrname = "ctid";
-       }
-       else if (target_relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
-       {
-               /*
-                * Let the foreign table's FDW add whatever junk TLEs it wants.
-                */
-               FdwRoutine *fdwroutine;
- 
-               fdwroutine = GetFdwRoutineForRelation(target_relation, false);
- 
-               if (fdwroutine->AddForeignUpdateTargets != NULL)
-                       fdwroutine->AddForeignUpdateTargets(parsetree, 
target_rte,
-                                                                               
                target_relation);
- 
-               /*
-                * If we have a row-level trigger corresponding to the 
operation, emit
-                * a whole-row Var so that executor will have the "old" row to 
pass to
-                * the trigger.  Alas, this misses system columns.
-                */
-               if (target_relation->trigdesc &&
-                       ((parsetree->commandType == CMD_UPDATE &&
-                         (target_relation->trigdesc->trig_update_after_row ||
-                          target_relation->trigdesc->trig_update_before_row)) 
||
-                        (parsetree->commandType == CMD_DELETE &&
-                         (target_relation->trigdesc->trig_delete_after_row ||
-                          target_relation->trigdesc->trig_delete_before_row))))
-               {
-                       var = makeWholeRowVar(target_rte,
-                                                                 
parsetree->resultRelation,
-                                                                 0,
-                                                                 false);
- 
-                       attrname = "wholerow";
-               }
-       }
-       else
-       {
-               /*
-                * Emit whole-row Var so that executor will have the "old" view 
row to
-                * pass to the INSTEAD OF trigger.
-                */
-               var = makeWholeRowVar(target_rte,
-                                                         
parsetree->resultRelation,
-                                                         0,
-                                                         false);
- 
-               attrname = "wholerow";
-       }
- 
-       if (var != NULL)
-       {
-               tle = makeTargetEntry((Expr *) var,
-                                                         
list_length(parsetree->targetList) + 1,
-                                                         pstrdup(attrname),
-                                                         true);
- 
-               parsetree->targetList = lappend(parsetree->targetList, tle);
-       }
- }
- 
- 
- /*
   * matchLocks -
   *      match the list of locks and returns the matching rules
   */
--- 1286,1291 ----
***************
*** 1497,1502 **** ApplyRetrieveRule(Query *parsetree,
--- 1398,1405 ----
                                 parsetree->commandType == CMD_DELETE)
                {
                        RangeTblEntry *newrte;
+                       Var                *var;
+                       TargetEntry *tle;
  
                        rte = rt_fetch(rt_index, parsetree->rtable);
                        newrte = copyObject(rte);
***************
*** 1527,1532 **** ApplyRetrieveRule(Query *parsetree,
--- 1430,1448 ----
                        ChangeVarNodes((Node *) parsetree->returningList, 
rt_index,
                                                   parsetree->resultRelation, 
0);
  
+                       /*
+                        * To allow the executor to find the original view row 
to pass to
+                        * the INSTEAD OF trigger, we add a whole-row reference 
to the old
+                        * RTE to the query's targetlist.
+                        */
+                       var = makeWholeRowVar(rte, rt_index, 0, false);
+                       tle = makeTargetEntry((Expr *) var,
+                                                                 
list_length(parsetree->targetList) + 1,
+                                                                 
pstrdup("wholerow"),
+                                                                 true);
+ 
+                       parsetree->targetList = lappend(parsetree->targetList, 
tle);
+ 
                        /* Now, continue with expanding the original view RTE */
                }
                else
***************
*** 2967,2992 **** rewriteTargetView(Query *parsetree, Relation view)
        view_rte->securityQuals = NIL;
  
        /*
-        * For UPDATE/DELETE, rewriteTargetListUD will have added a wholerow 
junk
-        * TLE for the view to the end of the targetlist, which we no longer 
need.
-        * Remove it to avoid unnecessary work when we process the targetlist.
-        * Note that when we recurse through rewriteQuery a new junk TLE will be
-        * added to allow the executor to find the proper row in the new target
-        * relation.  (So, if we failed to do this, we might have multiple junk
-        * TLEs with the same name, which would be disastrous.)
-        */
-       if (parsetree->commandType != CMD_INSERT)
-       {
-               TargetEntry *tle = (TargetEntry *) llast(parsetree->targetList);
- 
-               Assert(tle->resjunk);
-               Assert(IsA(tle->expr, Var) &&
-                          ((Var *) tle->expr)->varno == 
parsetree->resultRelation &&
-                          ((Var *) tle->expr)->varattno == 0);
-               parsetree->targetList = list_delete_ptr(parsetree->targetList, 
tle);
-       }
- 
-       /*
         * Now update all Vars in the outer query that reference the view to
         * reference the appropriate column of the base relation instead.
         */
--- 2883,2888 ----
***************
*** 3347,3357 **** RewriteQuery(Query *parsetree, List *rewrite_events)
                                                                        
parsetree->override,
                                                                        
rt_entry_relation,
                                                                        
parsetree->resultRelation, NULL);
-                       rewriteTargetListUD(parsetree, rt_entry, 
rt_entry_relation);
                }
                else if (event == CMD_DELETE)
                {
!                       rewriteTargetListUD(parsetree, rt_entry, 
rt_entry_relation);
                }
                else
                        elog(ERROR, "unrecognized commandType: %d", (int) 
event);
--- 3243,3252 ----
                                                                        
parsetree->override,
                                                                        
rt_entry_relation,
                                                                        
parsetree->resultRelation, NULL);
                }
                else if (event == CMD_DELETE)
                {
!                       /* Nothing to do here */
                }
                else
                        elog(ERROR, "unrecognized commandType: %d", (int) 
event);
*** a/src/include/optimizer/prep.h
--- b/src/include/optimizer/prep.h
***************
*** 38,44 **** extern Expr *canonicalize_qual(Expr *qual);
  /*
   * prototypes for preptlist.c
   */
! extern List *preprocess_targetlist(PlannerInfo *root, List *tlist);
  
  extern List *preprocess_onconflict_targetlist(List *tlist,
                                                                 int 
result_relation, List *range_table);
--- 38,44 ----
  /*
   * prototypes for preptlist.c
   */
! extern List *preprocess_targetlist(PlannerInfo *root);
  
  extern List *preprocess_onconflict_targetlist(List *tlist,
                                                                 int 
result_relation, List *range_table);
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to