Amit Langote wrote:

> Actually, after your comment on my original patch [1], I did make it work
> for multiple levels by teaching the partition initialization code to find
> a given partition's indexes that are inherited from the root table (that
> is the table mentioned in command).  So, after a tuple is routed to a
> partition, we switch from the original arbiterIndexes list to the one we
> created for the partition, which must contain OIDs corresponding to those
> in the original list.  After all, for each of the parent's indexes that
> the planner put into the original arbiterIndexes list, there must exist an
> index in each of the leaf partitions.

Oh, your solution for this seems simple enough.  Silly me, I was trying
to implement it in a quite roundabout way.  Thanks.  (I do wonder if we
should save the "root" reloid in the relcache).

> I had also observed when working on the patch that various TupleTableSlots
> used by the ON CONFLICT DO UPDATE code must be based on TupleDesc of the
> inheritance-translated target list (DO UPDATE SET target list).  In fact,
> that has to take into account also the dropped columns; we may have
> dropped columns either in parent or in a partition or in both at same or
> different attnum positions.  That means, simple map_partition_varattnos()
> translation doesn't help in this case.

Yeah, I was aware these corner cases could become a problem though I
hadn't gotten around to testing them yet.  Thanks for all your work on
this.

The usage of the few optimizer/prep/ functions that are currently static
doesn't fill me with joy.  These functions have weird APIs because
they're static so we don't rally care, but once we export them we should
strive to be more careful.  I'd rather stay away from just exporting
them all, so I chose to encapsulate those calls in a single function and
export only expand_targetlist from preptlist.c, keeping the others
static in prepunion.c.  In the attached patch set, I put an API change
(work on tupdescs rather than full-blown relations) for a couple of
those functions as 0001, then your patch as 0002, then a few fixups of
my own.  (0002 is not bit-by-bit identical to yours; I think I had to
fix some merge conflict with 0001, but should be pretty much the same).

But looking further, I think there is much cruft that has accumulated in
those functions (because they've gotten simplified over time), and we
could do some additional cleanup surgery.  For example, there is no
reason to pass a list pointer to make_inh_translation_list(); we could
just return it.  And then we don't have to cons up a fake AppendRelInfo
with all dummy values that adjust_inherited_tlist doesn't even care
about.  I think there was a point for all these contortions back at some
point (visible by checking git history of this code), but it all seems
useless now.

Re. the "ugly hack" comments in adjust_inherited_tlist(), I'm confused:
expand_targetlist() runs *after*, not before, so how could it have
affected the result?  I'm obviously confused about what
expand_targetlist call this comment is talking about.  Anyway I wanted
to make it use resjunk entries instead, but that broke some other case
that I didn't have time to research yesterday.  I'll get back to this
soon, but in the meantime, here's what I have.

-- 
Álvaro Herrera                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
>From d5b3b7c252da2c2a25bd9b8de40a03f2d5f30081 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvhe...@alvh.no-ip.org>
Date: Thu, 1 Mar 2018 19:58:50 -0300
Subject: [PATCH v3 1/3] Make some static functions work on TupleDesc rather
 than Relation

---
 src/backend/optimizer/prep/preptlist.c | 21 +++++++++++----------
 src/backend/optimizer/prep/prepunion.c | 26 ++++++++++++++++----------
 2 files changed, 27 insertions(+), 20 deletions(-)

diff --git a/src/backend/optimizer/prep/preptlist.c 
b/src/backend/optimizer/prep/preptlist.c
index 8603feef2b..8c94dd4f59 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -116,7 +116,8 @@ preprocess_targetlist(PlannerInfo *root)
        tlist = parse->targetList;
        if (command_type == CMD_INSERT || command_type == CMD_UPDATE)
                tlist = expand_targetlist(tlist, command_type,
-                                                                 
result_relation, target_relation);
+                                                                 
result_relation,
+                                                                 
RelationGetDescr(target_relation));
 
        /*
         * Add necessary junk columns for rowmarked rels.  These values are 
needed
@@ -230,7 +231,7 @@ preprocess_targetlist(PlannerInfo *root)
                        expand_targetlist(parse->onConflict->onConflictSet,
                                                          CMD_UPDATE,
                                                          result_relation,
-                                                         target_relation);
+                                                         
RelationGetDescr(target_relation));
 
        if (target_relation)
                heap_close(target_relation, NoLock);
@@ -247,13 +248,13 @@ preprocess_targetlist(PlannerInfo *root)
 
 /*
  * expand_targetlist
- *       Given a target list as generated by the parser and a result relation,
- *       add targetlist entries for any missing attributes, and ensure the
- *       non-junk attributes appear in proper field order.
+ *       Given a target list as generated by the parser and a result relation's
+ *       tuple descriptor, add targetlist entries for any missing attributes, 
and
+ *       ensure the non-junk attributes appear in proper field order.
  */
 static List *
 expand_targetlist(List *tlist, int command_type,
-                                 Index result_relation, Relation rel)
+                                 Index result_relation, TupleDesc tupdesc)
 {
        List       *new_tlist = NIL;
        ListCell   *tlist_item;
@@ -266,14 +267,14 @@ expand_targetlist(List *tlist, int command_type,
         * The rewriter should have already ensured that the TLEs are in correct
         * order; but we have to insert TLEs for any missing attributes.
         *
-        * Scan the tuple description in the relation's relcache entry to make
-        * sure we have all the user attributes in the right order.
+        * Scan the tuple description to make sure we have all the user 
attributes
+        * in the right order.
         */
-       numattrs = RelationGetNumberOfAttributes(rel);
+       numattrs = tupdesc->natts;
 
        for (attrno = 1; attrno <= numattrs; attrno++)
        {
-               Form_pg_attribute att_tup = TupleDescAttr(rel->rd_att, attrno - 
1);
+               Form_pg_attribute att_tup = TupleDescAttr(tupdesc, attrno - 1);
                TargetEntry *new_tle = NULL;
 
                if (tlist_item != NULL)
diff --git a/src/backend/optimizer/prep/prepunion.c 
b/src/backend/optimizer/prep/prepunion.c
index b586f941a8..7949829b31 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -113,8 +113,9 @@ static void expand_single_inheritance_child(PlannerInfo 
*root,
                                                                PlanRowMark 
*top_parentrc, Relation childrel,
                                                                List 
**appinfos, RangeTblEntry **childrte_p,
                                                                Index 
*childRTindex_p);
-static void make_inh_translation_list(Relation oldrelation,
-                                                 Relation newrelation,
+static void make_inh_translation_list(TupleDesc old_tupdesc,
+                                                 TupleDesc new_tupdesc,
+                                                 char *new_rel_name,
                                                  Index newvarno,
                                                  List **translated_vars);
 static Bitmapset *translate_col_privs(const Bitmapset *parent_privs,
@@ -1730,7 +1731,10 @@ expand_single_inheritance_child(PlannerInfo *root, 
RangeTblEntry *parentrte,
                appinfo->child_relid = childRTindex;
                appinfo->parent_reltype = parentrel->rd_rel->reltype;
                appinfo->child_reltype = childrel->rd_rel->reltype;
-               make_inh_translation_list(parentrel, childrel, childRTindex,
+               make_inh_translation_list(RelationGetDescr(parentrel),
+                                                                 
RelationGetDescr(childrel),
+                                                                 
RelationGetRelationName(childrel),
+                                                                 childRTindex,
                                                                  
&appinfo->translated_vars);
                appinfo->parent_reloid = parentOID;
                *appinfos = lappend(*appinfos, appinfo);
@@ -1794,16 +1798,18 @@ expand_single_inheritance_child(PlannerInfo *root, 
RangeTblEntry *parentrte,
  * For paranoia's sake, we match type/collation as well as attribute name.
  */
 static void
-make_inh_translation_list(Relation oldrelation, Relation newrelation,
+make_inh_translation_list(TupleDesc old_tupdesc, TupleDesc new_tupdesc,
+                                                 char *new_rel_name,
                                                  Index newvarno,
                                                  List **translated_vars)
 {
        List       *vars = NIL;
-       TupleDesc       old_tupdesc = RelationGetDescr(oldrelation);
-       TupleDesc       new_tupdesc = RelationGetDescr(newrelation);
        int                     oldnatts = old_tupdesc->natts;
        int                     newnatts = new_tupdesc->natts;
        int                     old_attno;
+       bool            equal_tupdescs;
+
+       equal_tupdescs = equalTupleDescs(old_tupdesc, new_tupdesc);
 
        for (old_attno = 0; old_attno < oldnatts; old_attno++)
        {
@@ -1830,7 +1836,7 @@ make_inh_translation_list(Relation oldrelation, Relation 
newrelation,
                 * When we are generating the "translation list" for the parent 
table
                 * of an inheritance set, no need to search for matches.
                 */
-               if (oldrelation == newrelation)
+               if (equal_tupdescs)
                {
                        vars = lappend(vars, makeVar(newvarno,
                                                                                
 (AttrNumber) (old_attno + 1),
@@ -1867,16 +1873,16 @@ make_inh_translation_list(Relation oldrelation, 
Relation newrelation,
                        }
                        if (new_attno >= newnatts)
                                elog(ERROR, "could not find inherited attribute 
\"%s\" of relation \"%s\"",
-                                        attname, 
RelationGetRelationName(newrelation));
+                                        attname, new_rel_name);
                }
 
                /* Found it, check type and collation match */
                if (atttypid != att->atttypid || atttypmod != att->atttypmod)
                        elog(ERROR, "attribute \"%s\" of relation \"%s\" does 
not match parent's type",
-                                attname, RelationGetRelationName(newrelation));
+                                attname, new_rel_name);
                if (attcollation != att->attcollation)
                        elog(ERROR, "attribute \"%s\" of relation \"%s\" does 
not match parent's collation",
-                                attname, RelationGetRelationName(newrelation));
+                                attname, new_rel_name);
 
                vars = lappend(vars, makeVar(newvarno,
                                                                         
(AttrNumber) (new_attno + 1),
-- 
2.11.0

>From 84b1310b3abdcd91a20a82399fa59a566f62b7ac Mon Sep 17 00:00:00 2001
From: amit <amitlangot...@gmail.com>
Date: Wed, 28 Feb 2018 17:58:00 +0900
Subject: [PATCH v3 2/3] Fix ON CONFLICT to work with partitioned tables

---
 doc/src/sgml/ddl.sgml                         |  15 ---
 src/backend/catalog/heap.c                    |   2 +-
 src/backend/catalog/partition.c               |  36 ++++--
 src/backend/commands/tablecmds.c              |  15 ++-
 src/backend/executor/execPartition.c          | 170 +++++++++++++++++++++++++-
 src/backend/executor/nodeModifyTable.c        |  30 +++++
 src/backend/optimizer/prep/preptlist.c        |   4 +-
 src/backend/optimizer/prep/prepunion.c        |  25 +++-
 src/backend/parser/analyze.c                  |   7 --
 src/include/catalog/partition.h               |   2 +-
 src/include/executor/execPartition.h          |  17 +++
 src/include/optimizer/prep.h                  |  11 ++
 src/test/regress/expected/insert_conflict.out |  73 +++++++++--
 src/test/regress/sql/insert_conflict.sql      |  64 ++++++++--
 14 files changed, 404 insertions(+), 67 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 2b879ead4b..b2b3485b83 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -3325,21 +3325,6 @@ ALTER TABLE measurement ATTACH PARTITION 
measurement_y2008m02
 
      <listitem>
       <para>
-       Using the <literal>ON CONFLICT</literal> clause with partitioned tables
-       will cause an error if the conflict target is specified (see
-       <xref linkend="sql-on-conflict" /> for more details on how the clause
-       works).  Therefore, it is not possible to specify
-       <literal>DO UPDATE</literal> as the alternative action, because
-       specifying the conflict target is mandatory in that case.  On the other
-       hand, specifying <literal>DO NOTHING</literal> as the alternative action
-       works fine provided the conflict target is not specified.  In that case,
-       unique constraints (or exclusion constraints) of the individual leaf
-       partitions are considered.
-      </para>
-     </listitem>
-
-     <listitem>
-      <para>
        When an <command>UPDATE</command> causes a row to move from one
        partition to another, there is a chance that another concurrent
        <command>UPDATE</command> or <command>DELETE</command> misses this row.
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index cf36ce4add..6d49c41217 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -1777,7 +1777,7 @@ heap_drop_with_catalog(Oid relid)
                elog(ERROR, "cache lookup failed for relation %u", relid);
        if (((Form_pg_class) GETSTRUCT(tuple))->relispartition)
        {
-               parentOid = get_partition_parent(relid);
+               parentOid = get_partition_parent(relid, false);
                LockRelationOid(parentOid, AccessExclusiveLock);
 
                /*
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index fcf7655553..9d1ad09595 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -192,6 +192,7 @@ static int  
get_partition_bound_num_indexes(PartitionBoundInfo b);
 static int     get_greatest_modulus(PartitionBoundInfo b);
 static uint64 compute_hash_value(int partnatts, FmgrInfo *partsupfunc,
                                                                 Datum *values, 
bool *isnull);
+static Oid get_partition_parent_recurse(Oid relid, bool getroot);
 
 /*
  * RelationBuildPartitionDesc
@@ -1392,14 +1393,25 @@ check_default_allows_bound(Relation parent, Relation 
default_rel,
  * when it is known that the relation is a partition.
  */
 Oid
-get_partition_parent(Oid relid)
+get_partition_parent(Oid relid, bool getroot)
+{
+       Oid             parentOid = get_partition_parent_recurse(relid, 
getroot);
+
+       if (parentOid == InvalidOid)
+               elog(ERROR, "could not find parent of relation %u", relid);
+
+       return parentOid;
+}
+
+static Oid
+get_partition_parent_recurse(Oid relid, bool getroot)
 {
        Form_pg_inherits form;
        Relation        catalogRelation;
        SysScanDesc scan;
        ScanKeyData key[2];
        HeapTuple       tuple;
-       Oid                     result;
+       Oid                     result = InvalidOid;
 
        catalogRelation = heap_open(InheritsRelationId, AccessShareLock);
 
@@ -1416,15 +1428,25 @@ get_partition_parent(Oid relid)
                                                          NULL, 2, key);
 
        tuple = systable_getnext(scan);
-       if (!HeapTupleIsValid(tuple))
-               elog(ERROR, "could not find tuple for parent of relation %u", 
relid);
+       if (HeapTupleIsValid(tuple))
+       {
+               form = (Form_pg_inherits) GETSTRUCT(tuple);
+               result = form->inhparent;
 
-       form = (Form_pg_inherits) GETSTRUCT(tuple);
-       result = form->inhparent;
+               if (getroot)
+                       result = get_partition_parent_recurse(result, getroot);
+       }
 
        systable_endscan(scan);
        heap_close(catalogRelation, AccessShareLock);
 
+       /*
+        * If we recursed and got InvalidOid as parent, that means we reached 
the
+        * root of this partition tree in the form of 'relid' itself.
+        */
+       if (getroot && !OidIsValid(result))
+               return relid;
+
        return result;
 }
 
@@ -2505,7 +2527,7 @@ generate_partition_qual(Relation rel)
                return copyObject(rel->rd_partcheck);
 
        /* Grab at least an AccessShareLock on the parent table */
-       parent = heap_open(get_partition_parent(RelationGetRelid(rel)),
+       parent = heap_open(get_partition_parent(RelationGetRelid(rel), false),
                                           AccessShareLock);
 
        /* Get pg_class.relpartbound */
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 74e020bffc..6e103f26ca 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -1292,7 +1292,7 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid 
relOid, Oid oldRelOid,
         */
        if (is_partition && relOid != oldRelOid)
        {
-               state->partParentOid = get_partition_parent(relOid);
+               state->partParentOid = get_partition_parent(relOid, false);
                if (OidIsValid(state->partParentOid))
                        LockRelationOid(state->partParentOid, 
AccessExclusiveLock);
        }
@@ -5843,7 +5843,8 @@ ATExecDropNotNull(Relation rel, const char *colName, 
LOCKMODE lockmode)
        /* If rel is partition, shouldn't drop NOT NULL if parent has the same 
*/
        if (rel->rd_rel->relispartition)
        {
-               Oid                     parentId = 
get_partition_parent(RelationGetRelid(rel));
+               Oid                     parentId = 
get_partition_parent(RelationGetRelid(rel),
+                                                                               
                        false);
                Relation        parent = heap_open(parentId, AccessShareLock);
                TupleDesc       tupDesc = RelationGetDescr(parent);
                AttrNumber      parent_attnum;
@@ -14346,7 +14347,7 @@ ATExecDetachPartition(Relation rel, RangeVar *name)
                if (!has_superclass(idxid))
                        continue;
 
-               Assert((IndexGetRelation(get_partition_parent(idxid), false) ==
+               Assert((IndexGetRelation(get_partition_parent(idxid, false), 
false) ==
                           RelationGetRelid(rel)));
 
                idx = index_open(idxid, AccessExclusiveLock);
@@ -14475,7 +14476,7 @@ ATExecAttachPartitionIdx(List **wqueue, Relation 
parentIdx, RangeVar *name)
 
        /* Silently do nothing if already in the right state */
        currParent = !has_superclass(partIdxId) ? InvalidOid :
-               get_partition_parent(partIdxId);
+               get_partition_parent(partIdxId, false);
        if (currParent != RelationGetRelid(parentIdx))
        {
                IndexInfo  *childInfo;
@@ -14708,8 +14709,10 @@ validatePartitionedIndex(Relation partedIdx, Relation 
partedTbl)
                /* make sure we see the validation we just did */
                CommandCounterIncrement();
 
-               parentIdxId = get_partition_parent(RelationGetRelid(partedIdx));
-               parentTblId = get_partition_parent(RelationGetRelid(partedTbl));
+               parentIdxId = get_partition_parent(RelationGetRelid(partedIdx),
+                                                                               
   false);
+               parentTblId = get_partition_parent(RelationGetRelid(partedTbl),
+                                                                               
   false);
                parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
                parentTbl = relation_open(parentTblId, AccessExclusiveLock);
                Assert(!parentIdx->rd_index->indisvalid);
diff --git a/src/backend/executor/execPartition.c 
b/src/backend/executor/execPartition.c
index 54efc9e545..3f7b61dc37 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -19,6 +19,7 @@
 #include "executor/executor.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
+#include "optimizer/prep.h"
 #include "utils/lsyscache.h"
 #include "utils/rls.h"
 #include "utils/ruleutils.h"
@@ -109,6 +110,23 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate, 
Relation rel)
         */
        proute->partition_tuple_slot = MakeTupleTableSlot(NULL);
 
+       /*
+        * We might need these arrays for conflict checking and handling the
+        * DO UPDATE action
+        */
+       if (mtstate && mtstate->mt_onconflict != ONCONFLICT_NONE)
+       {
+               proute->partition_arbiter_indexes = (List **)
+                                                                               
        palloc(proute->num_partitions *
+                                                                               
                   sizeof(List *));
+               proute->partition_conflproj_slots = (TupleTableSlot **)
+                                                                               
        palloc(proute->num_partitions *
+                                                                               
                   sizeof(TupleTableSlot *));
+               proute->partition_existing_slots = (TupleTableSlot **)
+                                                                               
        palloc(proute->num_partitions *
+                                                                               
                   sizeof(TupleTableSlot *));
+       }
+
        i = 0;
        foreach(cell, leaf_parts)
        {
@@ -475,9 +493,6 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
                                                                        
&mtstate->ps, RelationGetDescr(partrel));
        }
 
-       Assert(proute->partitions[partidx] == NULL);
-       proute->partitions[partidx] = leaf_part_rri;
-
        /*
         * Save a tuple conversion map to convert a tuple routed to this 
partition
         * from the parent's type to the partition's.
@@ -487,6 +502,155 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
                                                           
RelationGetDescr(partrel),
                                                           gettext_noop("could 
not convert row type"));
 
+       /*
+        * Initialize information about this partition that's needed to handle
+        * the ON CONFLICT clause.
+        */
+       if (node && node->onConflictAction != ONCONFLICT_NONE)
+       {
+               TupleDesc       partrelDesc = RelationGetDescr(partrel);
+               TupleConversionMap *map = 
proute->parent_child_tupconv_maps[partidx];
+               TupleTableSlot *part_conflproj_slot,
+                                          *part_existing_slot;
+               int                     firstVarno = 
mtstate->resultRelInfo[0].ri_RangeTableIndex;
+               Relation        firstResultRel = 
mtstate->resultRelInfo[0].ri_RelationDesc;
+               ExprContext *econtext = mtstate->ps.ps_ExprContext;
+               ListCell *lc;
+               List     *my_arbiterindexes = NIL;
+
+               /*
+                * If the root parent and partition have the same tuple
+                * descriptor, just reuse the original projection and WHERE
+                * clause expressions for partition.
+                */
+               if (map == NULL)
+               {
+                       /* Use the existing slot. */
+                       part_existing_slot = mtstate->mt_existing;
+                       part_conflproj_slot = mtstate->mt_conflproj;
+                       leaf_part_rri->ri_onConflictSetProj =
+                                                                       
resultRelInfo->ri_onConflictSetProj;
+                       leaf_part_rri->ri_onConflictSetWhere =
+                                                                       
resultRelInfo->ri_onConflictSetWhere;
+               }
+               else
+               {
+                       /* Convert expressions contain partition's attnos. */
+                       List *conv_setproj;
+                       AppendRelInfo appinfo;
+                       TupleDesc       tupDesc;
+
+                       /* Need our own slot. */
+                       part_existing_slot =
+                                       
ExecInitExtraTupleSlot(mtstate->ps.state, partrelDesc);
+
+                       /* First convert references to EXCLUDED 
pseudo-relation. */
+                       conv_setproj = map_partition_varattnos((List *)
+                                                                               
                   node->onConflictSet,
+                                                                               
                   INNER_VAR,
+                                                                               
                   partrel,
+                                                                               
                   firstResultRel, NULL);
+                       /* Then convert references to main target relation. */
+                       conv_setproj = map_partition_varattnos((List *)
+                                                                               
                   conv_setproj,
+                                                                               
                   firstVarno,
+                                                                               
                   partrel,
+                                                                               
                   firstResultRel, NULL);
+
+                       /*
+                        * Need to fix the target entries' resnos too by using
+                        * inheritance translation.
+                        */
+                       appinfo.type = T_AppendRelInfo;
+                       appinfo.parent_relid = firstVarno;
+                       appinfo.parent_reltype = 
firstResultRel->rd_rel->reltype;
+                       appinfo.child_relid = partrel->rd_id;
+                       appinfo.child_reltype = partrel->rd_rel->reltype;
+                       appinfo.parent_reloid = firstResultRel->rd_id;
+                       make_inh_translation_list(firstResultRel, partrel,
+                                                                         1, /* 
dummy */
+                                                                         
&appinfo.translated_vars);
+                       conv_setproj = adjust_inherited_tlist((List *) 
conv_setproj,
+                                                                               
                  &appinfo);
+
+                       /*
+                        * Add any attributes that are missing in the source 
list, such
+                        * as, dropped columns in the partition.
+                        */
+                       conv_setproj = expand_targetlist(conv_setproj, 
CMD_UPDATE,
+                                                                               
         firstVarno, partrel);
+
+                       tupDesc = ExecTypeFromTL(conv_setproj, 
partrelDesc->tdhasoid);
+                       part_conflproj_slot = 
ExecInitExtraTupleSlot(mtstate->ps.state,
+                                                                               
                                 tupDesc);
+                       leaf_part_rri->ri_onConflictSetProj =
+                                       ExecBuildProjectionInfo(conv_setproj, 
econtext,
+                                                                               
        part_conflproj_slot,
+                                                                               
        &mtstate->ps, partrelDesc);
+
+                       /* For WHERE quals, map_partition_varattnos() suffices. 
*/
+                       if (node->onConflictWhere)
+                       {
+                               List *conv_where;
+                               ExprState  *qualexpr;
+
+                               /* First convert references to EXCLUDED 
pseudo-relation. */
+                               conv_where = map_partition_varattnos((List *)
+                                                                               
                         node->onConflictWhere,
+                                                                               
                         INNER_VAR,
+                                                                               
                         partrel,
+                                                                               
                         firstResultRel, NULL);
+                               /* Then convert references to main target 
relation. */
+                               conv_where = map_partition_varattnos((List *)
+                                                                               
                         conv_where,
+                                                                               
                         firstVarno,
+                                                                               
                         partrel, firstResultRel,
+                                                                               
                         NULL);
+                               qualexpr = ExecInitQual(conv_where, 
&mtstate->ps);
+                               leaf_part_rri->ri_onConflictSetWhere = qualexpr;
+                       }
+               }
+
+               /*
+                * Save away for use later.  Set mtstate->mt_existing and
+                * mtstate->mt_conflproj, respectively, to these values before
+                * calling ExecOnConflictUpdate().
+                */
+               proute->partition_existing_slots[partidx] = part_existing_slot;
+               proute->partition_conflproj_slots[partidx] = 
part_conflproj_slot;
+
+               /* Initialize arbiter indexes list, if any. */
+               foreach(lc, mtstate->mt_arbiterindexes)
+               {
+                       Oid             parentArbiterIndexOid = lfirst_oid(lc);
+                       int             i;
+
+                       /*
+                        * Find parentArbiterIndexOid's child in this partition 
and add it
+                        * to my_arbiterindexes.
+                        */
+                       for (i = 0; i < leaf_part_rri->ri_NumIndices; i++)
+                       {
+                               Relation index = 
leaf_part_rri->ri_IndexRelationDescs[i];
+                               Oid              indexOid = 
RelationGetRelid(index);
+
+                               if (parentArbiterIndexOid ==
+                                       get_partition_parent(indexOid, true))
+                                       my_arbiterindexes = 
lappend_oid(my_arbiterindexes,
+                                                                               
                        indexOid);
+                       }
+               }
+
+               /*
+                * Use this list instead of the original one containing parent's
+                * indexes.
+                */
+               proute->partition_arbiter_indexes[partidx] = my_arbiterindexes;
+       }
+
+       Assert(proute->partitions[partidx] == NULL);
+       proute->partitions[partidx] = leaf_part_rri;
+
        MemoryContextSwitchTo(oldContext);
 
        return leaf_part_rri;
diff --git a/src/backend/executor/nodeModifyTable.c 
b/src/backend/executor/nodeModifyTable.c
index c32928d9bd..0f9ca6586e 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -374,6 +374,24 @@ ExecInsert(ModifyTableState *mtstate,
                                                                                
  tuple,
                                                                                
  proute->partition_tuple_slot,
                                                                                
  &slot);
+
+               /* Switch to partition's ON CONFLICT information. */
+               if (arbiterIndexes)
+               {
+                       Assert(onconflict != ONCONFLICT_NONE);
+                       arbiterIndexes = 
proute->partition_arbiter_indexes[leaf_part_index];
+
+                       /* Use correct existing and projection slots for DO 
UPDATE */
+                       if (onconflict == ONCONFLICT_UPDATE)
+                       {
+                               
Assert(proute->partition_existing_slots[leaf_part_index]);
+                               mtstate->mt_existing =
+                                               
proute->partition_existing_slots[leaf_part_index];
+                               
Assert(proute->partition_conflproj_slots[leaf_part_index]);
+                               mtstate->mt_conflproj =
+                                               
proute->partition_conflproj_slots[leaf_part_index];
+                       }
+               }
        }
 
        resultRelationDesc = resultRelInfo->ri_RelationDesc;
@@ -1146,6 +1164,18 @@ lreplace:;
                        TupleConversionMap *tupconv_map;
 
                        /*
+                        * Disallow an INSERT ON CONFLICT DO UPDATE that causes 
the
+                        * original row to migrate to a different partition.  
Maybe this
+                        * can be implemented some day, but it seems a fringe 
feature with
+                        * little redeeming value.
+                        */
+                       if (((ModifyTable *) 
mtstate->ps.plan)->onConflictAction == ONCONFLICT_UPDATE)
+                               ereport(ERROR,
+                                               
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                errmsg("invalid ON UPDATE 
specification"),
+                                                errdetail("The result tuple 
would appear in a different partition than the original tuple.")));
+
+                       /*
                         * When an UPDATE is run on a leaf partition, we will 
not have
                         * partition tuple routing set up. In that case, fail 
with
                         * partition constraint violation error.
diff --git a/src/backend/optimizer/prep/preptlist.c 
b/src/backend/optimizer/prep/preptlist.c
index 8c94dd4f59..e2995e6592 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -53,8 +53,6 @@
 #include "utils/rel.h"
 
 
-static List *expand_targetlist(List *tlist, int command_type,
-                                 Index result_relation, Relation rel);
 
 
 /*
@@ -252,7 +250,7 @@ preprocess_targetlist(PlannerInfo *root)
  *       tuple descriptor, add targetlist entries for any missing attributes, 
and
  *       ensure the non-junk attributes appear in proper field order.
  */
-static List *
+List *
 expand_targetlist(List *tlist, int command_type,
                                  Index result_relation, TupleDesc tupdesc)
 {
diff --git a/src/backend/optimizer/prep/prepunion.c 
b/src/backend/optimizer/prep/prepunion.c
index 7949829b31..4153891f29 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -124,8 +124,6 @@ static Node *adjust_appendrel_attrs_mutator(Node *node,
                                                           
adjust_appendrel_attrs_context *context);
 static Relids adjust_child_relids(Relids relids, int nappinfos,
                                        AppendRelInfo **appinfos);
-static List *adjust_inherited_tlist(List *tlist,
-                                          AppendRelInfo *context);
 
 
 /*
@@ -2359,7 +2357,7 @@ adjust_child_relids_multilevel(PlannerInfo *root, Relids 
relids,
  *
  * Note that this is not needed for INSERT because INSERT isn't inheritable.
  */
-static List *
+List *
 adjust_inherited_tlist(List *tlist, AppendRelInfo *context)
 {
        bool            changed_it = false;
@@ -2380,6 +2378,13 @@ adjust_inherited_tlist(List *tlist, AppendRelInfo 
*context)
                if (tle->resjunk)
                        continue;                       /* ignore junk items */
 
+               /*
+                * ignore dummy tlist entry added by exapnd_targetlist() for
+                * dropped columns in the parent table.
+                */
+               if (IsA(tle->expr, Const) && ((Const *) tle->expr)->constisnull)
+                       continue;
+
                /* Look up the translation of this column: it must be a Var */
                if (tle->resno <= 0 ||
                        tle->resno > list_length(context->translated_vars))
@@ -2418,6 +2423,13 @@ adjust_inherited_tlist(List *tlist, AppendRelInfo 
*context)
                        if (tle->resjunk)
                                continue;               /* ignore junk items */
 
+                       /*
+                        * ignore dummy tlist entry added by 
exapnd_targetlist() for
+                        * dropped columns in the parent table.
+                        */
+                       if (IsA(tle->expr, Const) && ((Const *) 
tle->expr)->constisnull)
+                               continue;
+
                        if (tle->resno == attrno)
                                new_tlist = lappend(new_tlist, tle);
                        else if (tle->resno > attrno)
@@ -2432,6 +2444,13 @@ adjust_inherited_tlist(List *tlist, AppendRelInfo 
*context)
                if (!tle->resjunk)
                        continue;                       /* here, ignore 
non-junk items */
 
+               /*
+                * ignore dummy tlist entry added by exapnd_targetlist() for
+                * dropped columns in the parent table.
+                */
+               if (IsA(tle->expr, Const) && ((Const *) tle->expr)->constisnull)
+                       continue;
+
                tle->resno = attrno;
                new_tlist = lappend(new_tlist, tle);
                attrno++;
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index c3a9617f67..92696f0607 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -1025,13 +1025,6 @@ transformOnConflictClause(ParseState *pstate,
                TargetEntry *te;
                int                     attno;
 
-               if (targetrel->rd_partdesc)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                        errmsg("%s cannot be applied to 
partitioned table \"%s\"",
-                                                       "ON CONFLICT DO UPDATE",
-                                                       
RelationGetRelationName(targetrel))));
-
                /*
                 * All INSERT expressions have been parsed, get ready for 
potentially
                 * existing SET statements that need to be processed like an 
UPDATE.
diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h
index 2faf0ca26e..70ddb225a1 100644
--- a/src/include/catalog/partition.h
+++ b/src/include/catalog/partition.h
@@ -51,7 +51,7 @@ extern PartitionBoundInfo 
partition_bounds_copy(PartitionBoundInfo src,
 
 extern void check_new_partition_bound(char *relname, Relation parent,
                                                  PartitionBoundSpec *spec);
-extern Oid     get_partition_parent(Oid relid);
+extern Oid     get_partition_parent(Oid relid, bool getroot);
 extern List *get_qual_from_partbound(Relation rel, Relation parent,
                                                PartitionBoundSpec *spec);
 extern List *map_partition_varattnos(List *expr, int fromrel_varno,
diff --git a/src/include/executor/execPartition.h 
b/src/include/executor/execPartition.h
index 03a599ad57..9fc1ab6711 100644
--- a/src/include/executor/execPartition.h
+++ b/src/include/executor/execPartition.h
@@ -90,6 +90,20 @@ typedef struct PartitionDispatchData *PartitionDispatch;
  *                                                             given leaf 
partition's rowtype after that
  *                                                             partition is 
chosen for insertion by
  *                                                             tuple-routing.
+ * partition_arbiter_indexes   Array of Lists with each slot containing the
+ *                                                             list of OIDs of 
a given partition's indexes
+ *                                                             that are to be 
used as arbiter indexes for
+ *                                                             ON CONFLICT 
checking
+ * partition_conflproj_slots   Array of TupleTableSlots to hold tuples of
+ *                                                             ON CONFLICT DO 
UPDATE SET projections;
+ *                                                             contains NULL 
for partitions whose tuple
+ *                                                             descriptor 
exactly matches the root parent's
+ *                                                             (including 
dropped columns)
+ * partition_existing_slots            Array of TupleTableSlots to hold 
existing
+ *                                                             tuple during ON 
CONFLICT DO UPDATE handling;
+ *                                                             contains NULL 
for partitions whose tuple
+ *                                                             descriptor 
exactly matches the root parent's
+ *                                                             (including 
dropped columns)
  *-----------------------
  */
 typedef struct PartitionTupleRouting
@@ -106,6 +120,9 @@ typedef struct PartitionTupleRouting
        int                     num_subplan_partition_offsets;
        TupleTableSlot *partition_tuple_slot;
        TupleTableSlot *root_tuple_slot;
+       List      **partition_arbiter_indexes;
+       TupleTableSlot **partition_conflproj_slots;
+       TupleTableSlot **partition_existing_slots;
 } PartitionTupleRouting;
 
 extern PartitionTupleRouting *ExecSetupPartitionTupleRouting(ModifyTableState 
*mtstate,
diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h
index 89b7ef337c..d380b419d7 100644
--- a/src/include/optimizer/prep.h
+++ b/src/include/optimizer/prep.h
@@ -42,6 +42,10 @@ extern List *preprocess_targetlist(PlannerInfo *root);
 
 extern PlanRowMark *get_plan_rowmark(List *rowmarks, Index rtindex);
 
+typedef struct RelationData *Relation;
+extern List *expand_targetlist(List *tlist, int command_type,
+                                 Index result_relation, Relation rel);
+
 /*
  * prototypes for prepunion.c
  */
@@ -65,4 +69,11 @@ extern SpecialJoinInfo *build_child_join_sjinfo(PlannerInfo 
*root,
 extern Relids adjust_child_relids_multilevel(PlannerInfo *root, Relids relids,
                                                           Relids child_relids, 
Relids top_parent_relids);
 
+extern void make_inh_translation_list(Relation oldrelation,
+                                                 Relation newrelation,
+                                                 Index newvarno,
+                                                 List **translated_vars);
+extern List *adjust_inherited_tlist(List *tlist,
+                                          AppendRelInfo *context);
+
 #endif                                                 /* PREP_H */
diff --git a/src/test/regress/expected/insert_conflict.out 
b/src/test/regress/expected/insert_conflict.out
index 2650faedee..a9677f06e6 100644
--- a/src/test/regress/expected/insert_conflict.out
+++ b/src/test/regress/expected/insert_conflict.out
@@ -786,16 +786,67 @@ select * from selfconflict;
 (3 rows)
 
 drop table selfconflict;
--- check that the following works:
--- insert into partitioned_table on conflict do nothing
-create table parted_conflict_test (a int, b char) partition by list (a);
-create table parted_conflict_test_1 partition of parted_conflict_test (b 
unique) for values in (1);
+-- check ON CONFLICT handling with partitioned tables
+create table parted_conflict_test (a int unique, b char) partition by list (a);
+create table parted_conflict_test_1 partition of parted_conflict_test (b 
unique) for values in (1, 2);
+-- no indexes required here
 insert into parted_conflict_test values (1, 'a') on conflict do nothing;
-insert into parted_conflict_test values (1, 'a') on conflict do nothing;
--- however, on conflict do update is not supported yet
-insert into parted_conflict_test values (1) on conflict (b) do update set a = 
excluded.a;
-ERROR:  ON CONFLICT DO UPDATE cannot be applied to partitioned table 
"parted_conflict_test"
--- but it works OK if we target the partition directly
-insert into parted_conflict_test_1 values (1) on conflict (b) do
-update set a = excluded.a;
+-- index on a required, which does exist in parent
+insert into parted_conflict_test values (1, 'a') on conflict (a) do nothing;
+insert into parted_conflict_test values (1, 'a') on conflict (a) do update set 
b = excluded.b;
+-- targeting partition directly will work
+insert into parted_conflict_test_1 values (1, 'a') on conflict (a) do nothing;
+insert into parted_conflict_test_1 values (1, 'b') on conflict (a) do update 
set b = excluded.b;
+-- index on b required, which doesn't exist in parent
+insert into parted_conflict_test values (2, 'b') on conflict (b) do update set 
a = excluded.a;
+ERROR:  there is no unique or exclusion constraint matching the ON CONFLICT 
specification
+-- targeting partition directly will work
+insert into parted_conflict_test_1 values (2, 'b') on conflict (b) do update 
set a = excluded.a;
+-- should see (2, 'b')
+select * from parted_conflict_test order by a;
+ a | b 
+---+---
+ 2 | b
+(1 row)
+
+-- now check that DO UPDATE works correctly for target partition with
+-- different attribute numbers
+create table parted_conflict_test_2 (b char, a int unique);
+alter table parted_conflict_test attach partition parted_conflict_test_2 for 
values in (3);
+truncate parted_conflict_test;
+insert into parted_conflict_test values (3, 'a') on conflict (a) do update set 
b = excluded.b;
+insert into parted_conflict_test values (3, 'b') on conflict (a) do update set 
b = excluded.b;
+-- should see (3, 'b')
+select * from parted_conflict_test order by a;
+ a | b 
+---+---
+ 3 | b
+(1 row)
+
+-- case where parent will have a dropped column, but the partition won't
+alter table parted_conflict_test drop b, add b char;
+create table parted_conflict_test_3 partition of parted_conflict_test for 
values in (4);
+truncate parted_conflict_test;
+insert into parted_conflict_test (a, b) values (4, 'a') on conflict (a) do 
update set b = excluded.b;
+insert into parted_conflict_test (a, b) values (4, 'b') on conflict (a) do 
update set b = excluded.b where parted_conflict_test.b = 'a';
+-- should see (4, 'b')
+select * from parted_conflict_test order by a;
+ a | b 
+---+---
+ 4 | b
+(1 row)
+
+-- case with multi-level partitioning
+create table parted_conflict_test_4 partition of parted_conflict_test for 
values in (5) partition by list (a);
+create table parted_conflict_test_4_1 partition of parted_conflict_test_4 for 
values in (5);
+truncate parted_conflict_test;
+insert into parted_conflict_test (a, b) values (5, 'a') on conflict (a) do 
update set b = excluded.b;
+insert into parted_conflict_test (a, b) values (5, 'b') on conflict (a) do 
update set b = excluded.b where parted_conflict_test.b = 'a';
+-- should see (5, 'b')
+select * from parted_conflict_test order by a;
+ a | b 
+---+---
+ 5 | b
+(1 row)
+
 drop table parted_conflict_test;
diff --git a/src/test/regress/sql/insert_conflict.sql 
b/src/test/regress/sql/insert_conflict.sql
index 32c647e3f8..73122479a3 100644
--- a/src/test/regress/sql/insert_conflict.sql
+++ b/src/test/regress/sql/insert_conflict.sql
@@ -472,15 +472,59 @@ select * from selfconflict;
 
 drop table selfconflict;
 
--- check that the following works:
--- insert into partitioned_table on conflict do nothing
-create table parted_conflict_test (a int, b char) partition by list (a);
-create table parted_conflict_test_1 partition of parted_conflict_test (b 
unique) for values in (1);
+-- check ON CONFLICT handling with partitioned tables
+create table parted_conflict_test (a int unique, b char) partition by list (a);
+create table parted_conflict_test_1 partition of parted_conflict_test (b 
unique) for values in (1, 2);
+
+-- no indexes required here
 insert into parted_conflict_test values (1, 'a') on conflict do nothing;
-insert into parted_conflict_test values (1, 'a') on conflict do nothing;
--- however, on conflict do update is not supported yet
-insert into parted_conflict_test values (1) on conflict (b) do update set a = 
excluded.a;
--- but it works OK if we target the partition directly
-insert into parted_conflict_test_1 values (1) on conflict (b) do
-update set a = excluded.a;
+
+-- index on a required, which does exist in parent
+insert into parted_conflict_test values (1, 'a') on conflict (a) do nothing;
+insert into parted_conflict_test values (1, 'a') on conflict (a) do update set 
b = excluded.b;
+
+-- targeting partition directly will work
+insert into parted_conflict_test_1 values (1, 'a') on conflict (a) do nothing;
+insert into parted_conflict_test_1 values (1, 'b') on conflict (a) do update 
set b = excluded.b;
+
+-- index on b required, which doesn't exist in parent
+insert into parted_conflict_test values (2, 'b') on conflict (b) do update set 
a = excluded.a;
+
+-- targeting partition directly will work
+insert into parted_conflict_test_1 values (2, 'b') on conflict (b) do update 
set a = excluded.a;
+
+-- should see (2, 'b')
+select * from parted_conflict_test order by a;
+
+-- now check that DO UPDATE works correctly for target partition with
+-- different attribute numbers
+create table parted_conflict_test_2 (b char, a int unique);
+alter table parted_conflict_test attach partition parted_conflict_test_2 for 
values in (3);
+truncate parted_conflict_test;
+insert into parted_conflict_test values (3, 'a') on conflict (a) do update set 
b = excluded.b;
+insert into parted_conflict_test values (3, 'b') on conflict (a) do update set 
b = excluded.b;
+
+-- should see (3, 'b')
+select * from parted_conflict_test order by a;
+
+-- case where parent will have a dropped column, but the partition won't
+alter table parted_conflict_test drop b, add b char;
+create table parted_conflict_test_3 partition of parted_conflict_test for 
values in (4);
+truncate parted_conflict_test;
+insert into parted_conflict_test (a, b) values (4, 'a') on conflict (a) do 
update set b = excluded.b;
+insert into parted_conflict_test (a, b) values (4, 'b') on conflict (a) do 
update set b = excluded.b where parted_conflict_test.b = 'a';
+
+-- should see (4, 'b')
+select * from parted_conflict_test order by a;
+
+-- case with multi-level partitioning
+create table parted_conflict_test_4 partition of parted_conflict_test for 
values in (5) partition by list (a);
+create table parted_conflict_test_4_1 partition of parted_conflict_test_4 for 
values in (5);
+truncate parted_conflict_test;
+insert into parted_conflict_test (a, b) values (5, 'a') on conflict (a) do 
update set b = excluded.b;
+insert into parted_conflict_test (a, b) values (5, 'b') on conflict (a) do 
update set b = excluded.b where parted_conflict_test.b = 'a';
+
+-- should see (5, 'b')
+select * from parted_conflict_test order by a;
+
 drop table parted_conflict_test;
-- 
2.11.0

>From eb11d8fbe02530dc6f4fbfa162c59baae46df80c Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvhe...@alvh.no-ip.org>
Date: Wed, 28 Feb 2018 20:20:08 -0300
Subject: [PATCH v3 3/3] fixups

---
 src/backend/catalog/partition.c        | 52 ++++++++++++----------
 src/backend/executor/execPartition.c   | 81 +++++++++++++---------------------
 src/backend/optimizer/prep/prepunion.c | 59 ++++++++++++++++++++-----
 src/include/optimizer/prep.h           | 15 +++----
 4 files changed, 115 insertions(+), 92 deletions(-)

diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index 9d1ad09595..ef2ef3aa80 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -192,7 +192,7 @@ static int  
get_partition_bound_num_indexes(PartitionBoundInfo b);
 static int     get_greatest_modulus(PartitionBoundInfo b);
 static uint64 compute_hash_value(int partnatts, FmgrInfo *partsupfunc,
                                                                 Datum *values, 
bool *isnull);
-static Oid get_partition_parent_recurse(Oid relid, bool getroot);
+static Oid get_partition_parent_recurse(Relation inhRel, Oid relid, bool 
getroot);
 
 /*
  * RelationBuildPartitionDesc
@@ -1385,8 +1385,10 @@ check_default_allows_bound(Relation parent, Relation 
default_rel,
 
 /*
  * get_partition_parent
+ *             Obtain direct parent or topmost ancestor of given relation
  *
- * Returns inheritance parent of a partition by scanning pg_inherits
+ * Returns direct inheritance parent of a partition by scanning pg_inherits;
+ * or, if 'getroot' is true, the topmost parent in the inheritance hierarchy.
  *
  * Note: Because this function assumes that the relation whose OID is passed
  * as an argument will have precisely one parent, it should only be called
@@ -1395,26 +1397,32 @@ check_default_allows_bound(Relation parent, Relation 
default_rel,
 Oid
 get_partition_parent(Oid relid, bool getroot)
 {
-       Oid             parentOid = get_partition_parent_recurse(relid, 
getroot);
+       Relation        inhRel;
+       Oid             parentOid;
 
+       inhRel = heap_open(InheritsRelationId, AccessShareLock);
+
+       parentOid = get_partition_parent_recurse(inhRel, relid, getroot);
        if (parentOid == InvalidOid)
                elog(ERROR, "could not find parent of relation %u", relid);
 
+       heap_close(inhRel, AccessShareLock);
+
        return parentOid;
 }
 
+/*
+ * get_partition_parent_recurse
+ *             Recursive part of get_partition_parent
+ */
 static Oid
-get_partition_parent_recurse(Oid relid, bool getroot)
+get_partition_parent_recurse(Relation inhRel, Oid relid, bool getroot)
 {
-       Form_pg_inherits form;
-       Relation        catalogRelation;
        SysScanDesc scan;
        ScanKeyData key[2];
        HeapTuple       tuple;
        Oid                     result = InvalidOid;
 
-       catalogRelation = heap_open(InheritsRelationId, AccessShareLock);
-
        ScanKeyInit(&key[0],
                                Anum_pg_inherits_inhrelid,
                                BTEqualStrategyNumber, F_OIDEQ,
@@ -1424,28 +1432,26 @@ get_partition_parent_recurse(Oid relid, bool getroot)
                                BTEqualStrategyNumber, F_INT4EQ,
                                Int32GetDatum(1));
 
-       scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId, 
true,
+       /* Obtain the direct parent, and release resources before recursing */
+       scan = systable_beginscan(inhRel, InheritsRelidSeqnoIndexId, true,
                                                          NULL, 2, key);
-
        tuple = systable_getnext(scan);
        if (HeapTupleIsValid(tuple))
-       {
-               form = (Form_pg_inherits) GETSTRUCT(tuple);
-               result = form->inhparent;
-
-               if (getroot)
-                       result = get_partition_parent_recurse(result, getroot);
-       }
-
+               result = ((Form_pg_inherits) GETSTRUCT(tuple))->inhparent;
        systable_endscan(scan);
-       heap_close(catalogRelation, AccessShareLock);
 
        /*
-        * If we recursed and got InvalidOid as parent, that means we reached 
the
-        * root of this partition tree in the form of 'relid' itself.
+        * If we were asked to recurse, do so now.  Except that if we didn't 
get a
+        * valid parent, then the 'relid' argument was already the topmost 
parent,
+        * so return that.
         */
-       if (getroot && !OidIsValid(result))
-               return relid;
+       if (getroot)
+       {
+               if (OidIsValid(result))
+                       return get_partition_parent_recurse(inhRel, result, 
getroot);
+               else
+                       return relid;
+       }
 
        return result;
 }
diff --git a/src/backend/executor/execPartition.c 
b/src/backend/executor/execPartition.c
index 3f7b61dc37..7ea0295d3c 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -65,6 +65,7 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate, 
Relation rel)
        int                     num_update_rri = 0,
                                update_rri_index = 0;
        PartitionTupleRouting *proute;
+       int                     nparts;
 
        /*
         * Get the information about the partition tree after locking all the
@@ -75,14 +76,12 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate, 
Relation rel)
        proute->partition_dispatch_info =
                RelationGetPartitionDispatchInfo(rel, &proute->num_dispatch,
                                                                                
 &leaf_parts);
-       proute->num_partitions = list_length(leaf_parts);
-       proute->partitions = (ResultRelInfo **) palloc(proute->num_partitions *
-                                                                               
                   sizeof(ResultRelInfo *));
+       proute->num_partitions = nparts = list_length(leaf_parts);
+       proute->partitions =
+               (ResultRelInfo **) palloc(nparts * sizeof(ResultRelInfo *));
        proute->parent_child_tupconv_maps =
-               (TupleConversionMap **) palloc0(proute->num_partitions *
-                                                                               
sizeof(TupleConversionMap *));
-       proute->partition_oids = (Oid *) palloc(proute->num_partitions *
-                                                                               
        sizeof(Oid));
+               (TupleConversionMap **) palloc0(nparts * 
sizeof(TupleConversionMap *));
+       proute->partition_oids = (Oid *) palloc(nparts * sizeof(Oid));
 
        /* Set up details specific to the type of tuple routing we are doing. */
        if (mtstate && mtstate->operation == CMD_UPDATE)
@@ -116,15 +115,12 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate, 
Relation rel)
         */
        if (mtstate && mtstate->mt_onconflict != ONCONFLICT_NONE)
        {
-               proute->partition_arbiter_indexes = (List **)
-                                                                               
        palloc(proute->num_partitions *
-                                                                               
                   sizeof(List *));
-               proute->partition_conflproj_slots = (TupleTableSlot **)
-                                                                               
        palloc(proute->num_partitions *
-                                                                               
                   sizeof(TupleTableSlot *));
-               proute->partition_existing_slots = (TupleTableSlot **)
-                                                                               
        palloc(proute->num_partitions *
-                                                                               
                   sizeof(TupleTableSlot *));
+               proute->partition_arbiter_indexes =
+                       (List **) palloc(nparts * sizeof(List *));
+               proute->partition_conflproj_slots =
+                       (TupleTableSlot **) palloc(nparts * 
sizeof(TupleTableSlot *));
+               proute->partition_existing_slots =
+                       (TupleTableSlot **) palloc(nparts * 
sizeof(TupleTableSlot *));
        }
 
        i = 0;
@@ -537,48 +533,33 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
                {
                        /* Convert expressions contain partition's attnos. */
                        List *conv_setproj;
-                       AppendRelInfo appinfo;
                        TupleDesc       tupDesc;
 
                        /* Need our own slot. */
                        part_existing_slot =
                                        
ExecInitExtraTupleSlot(mtstate->ps.state, partrelDesc);
 
-                       /* First convert references to EXCLUDED 
pseudo-relation. */
-                       conv_setproj = map_partition_varattnos((List *)
-                                                                               
                   node->onConflictSet,
-                                                                               
                   INNER_VAR,
-                                                                               
                   partrel,
-                                                                               
                   firstResultRel, NULL);
+                       /*
+                        * First convert references to the EXCLUDED 
pseudo-relation, which
+                        * was set to INNER_VAR by set_plan_references.
+                        */
+                       conv_setproj =
+                               map_partition_varattnos((List *) 
node->onConflictSet,
+                                                                               
INNER_VAR, partrel,
+                                                                               
firstResultRel, NULL);
+
                        /* Then convert references to main target relation. */
-                       conv_setproj = map_partition_varattnos((List *)
-                                                                               
                   conv_setproj,
-                                                                               
                   firstVarno,
-                                                                               
                   partrel,
-                                                                               
                   firstResultRel, NULL);
+                       conv_setproj =
+                               map_partition_varattnos((List *) conv_setproj,
+                                                                               
firstVarno, partrel,
+                                                                               
firstResultRel, NULL);
 
-                       /*
-                        * Need to fix the target entries' resnos too by using
-                        * inheritance translation.
-                        */
-                       appinfo.type = T_AppendRelInfo;
-                       appinfo.parent_relid = firstVarno;
-                       appinfo.parent_reltype = 
firstResultRel->rd_rel->reltype;
-                       appinfo.child_relid = partrel->rd_id;
-                       appinfo.child_reltype = partrel->rd_rel->reltype;
-                       appinfo.parent_reloid = firstResultRel->rd_id;
-                       make_inh_translation_list(firstResultRel, partrel,
-                                                                         1, /* 
dummy */
-                                                                         
&appinfo.translated_vars);
-                       conv_setproj = adjust_inherited_tlist((List *) 
conv_setproj,
-                                                                               
                  &appinfo);
-
-                       /*
-                        * Add any attributes that are missing in the source 
list, such
-                        * as, dropped columns in the partition.
-                        */
-                       conv_setproj = expand_targetlist(conv_setproj, 
CMD_UPDATE,
-                                                                               
         firstVarno, partrel);
+                       conv_setproj =
+                               
adjust_and_expand_partition_tlist(RelationGetDescr(firstResultRel),
+                                                                               
                  RelationGetDescr(partrel),
+                                                                               
                  RelationGetRelationName(partrel),
+                                                                               
                  firstVarno,
+                                                                               
                  conv_setproj);
 
                        tupDesc = ExecTypeFromTL(conv_setproj, 
partrelDesc->tdhasoid);
                        part_conflproj_slot = 
ExecInitExtraTupleSlot(mtstate->ps.state,
diff --git a/src/backend/optimizer/prep/prepunion.c 
b/src/backend/optimizer/prep/prepunion.c
index 4153891f29..c11f6c20ab 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -124,6 +124,8 @@ static Node *adjust_appendrel_attrs_mutator(Node *node,
                                                           
adjust_appendrel_attrs_context *context);
 static Relids adjust_child_relids(Relids relids, int nappinfos,
                                        AppendRelInfo **appinfos);
+static List *adjust_inherited_tlist(List *tlist,
+                                          AppendRelInfo *context);
 
 
 /*
@@ -2357,7 +2359,7 @@ adjust_child_relids_multilevel(PlannerInfo *root, Relids 
relids,
  *
  * Note that this is not needed for INSERT because INSERT isn't inheritable.
  */
-List *
+static List *
 adjust_inherited_tlist(List *tlist, AppendRelInfo *context)
 {
        bool            changed_it = false;
@@ -2379,8 +2381,10 @@ adjust_inherited_tlist(List *tlist, AppendRelInfo 
*context)
                        continue;                       /* ignore junk items */
 
                /*
-                * ignore dummy tlist entry added by exapnd_targetlist() for
-                * dropped columns in the parent table.
+                * XXX ugly hack: must ignore dummy tlist entry added by
+                * expand_targetlist() for dropped columns in the parent table 
or we
+                * fail because there is no translation.  Must find a better 
way to
+                * deal with this case, though.
                 */
                if (IsA(tle->expr, Const) && ((Const *) tle->expr)->constisnull)
                        continue;
@@ -2423,10 +2427,7 @@ adjust_inherited_tlist(List *tlist, AppendRelInfo 
*context)
                        if (tle->resjunk)
                                continue;               /* ignore junk items */
 
-                       /*
-                        * ignore dummy tlist entry added by 
exapnd_targetlist() for
-                        * dropped columns in the parent table.
-                        */
+                       /* XXX ugly hack; see above */
                        if (IsA(tle->expr, Const) && ((Const *) 
tle->expr)->constisnull)
                                continue;
 
@@ -2444,10 +2445,7 @@ adjust_inherited_tlist(List *tlist, AppendRelInfo 
*context)
                if (!tle->resjunk)
                        continue;                       /* here, ignore 
non-junk items */
 
-               /*
-                * ignore dummy tlist entry added by exapnd_targetlist() for
-                * dropped columns in the parent table.
-                */
+               /* XXX ugly hack; see above */
                if (IsA(tle->expr, Const) && ((Const *) tle->expr)->constisnull)
                        continue;
 
@@ -2460,6 +2458,45 @@ adjust_inherited_tlist(List *tlist, AppendRelInfo 
*context)
 }
 
 /*
+ * Given a targetlist for the parentRel of the given varno, adjust it to be in
+ * the correct order and to contain all the needed elements for the given
+ * partition.
+ */
+List *
+adjust_and_expand_partition_tlist(TupleDesc parentDesc,
+                                                                 TupleDesc 
partitionDesc,
+                                                                 char 
*partitionRelname,
+                                                                 int 
parentVarno,
+                                                                 List 
*targetlist)
+{
+       AppendRelInfo appinfo;
+       List *result_tl;
+
+       /*
+        * Fist, fix the target entries' resnos, by using inheritance 
translation.
+        */
+       appinfo.type = T_AppendRelInfo;
+       appinfo.parent_relid = parentVarno;
+       appinfo.parent_reltype = InvalidOid; // parentRel->rd_rel->reltype;
+       appinfo.child_relid = -1;
+       appinfo.child_reltype = InvalidOid; // partrel->rd_rel->reltype;
+       appinfo.parent_reloid = 1; // dummy  parentRel->rd_id;
+       make_inh_translation_list(parentDesc, partitionDesc, partitionRelname,
+                                                         1, /* dummy */
+                                                         
&appinfo.translated_vars);
+       result_tl = adjust_inherited_tlist((List *) targetlist, &appinfo);
+
+       /*
+        * Add any attributes that are missing in the source list, such
+        * as dropped columns in the partition.
+        */
+       result_tl = expand_targetlist(result_tl, CMD_UPDATE,
+                                                                 parentVarno, 
partitionDesc);
+
+       return result_tl;
+}
+
+/*
  * adjust_appendrel_attrs_multilevel
  *       Apply Var translations from a toplevel appendrel parent down to a 
child.
  *
diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h
index d380b419d7..c5263f65dc 100644
--- a/src/include/optimizer/prep.h
+++ b/src/include/optimizer/prep.h
@@ -14,6 +14,7 @@
 #ifndef PREP_H
 #define PREP_H
 
+#include "access/tupdesc.h"
 #include "nodes/plannodes.h"
 #include "nodes/relation.h"
 
@@ -42,9 +43,8 @@ extern List *preprocess_targetlist(PlannerInfo *root);
 
 extern PlanRowMark *get_plan_rowmark(List *rowmarks, Index rtindex);
 
-typedef struct RelationData *Relation;
 extern List *expand_targetlist(List *tlist, int command_type,
-                                 Index result_relation, Relation rel);
+                                 Index result_relation, TupleDesc tupdesc);
 
 /*
  * prototypes for prepunion.c
@@ -69,11 +69,10 @@ extern SpecialJoinInfo *build_child_join_sjinfo(PlannerInfo 
*root,
 extern Relids adjust_child_relids_multilevel(PlannerInfo *root, Relids relids,
                                                           Relids child_relids, 
Relids top_parent_relids);
 
-extern void make_inh_translation_list(Relation oldrelation,
-                                                 Relation newrelation,
-                                                 Index newvarno,
-                                                 List **translated_vars);
-extern List *adjust_inherited_tlist(List *tlist,
-                                          AppendRelInfo *context);
+extern List *adjust_and_expand_partition_tlist(TupleDesc parentDesc,
+                                                                 TupleDesc 
partitionDesc,
+                                                                 char 
*partitionRelname,
+                                                                 int 
parentVarno,
+                                                                 List 
*targetlist);
 
 #endif                                                 /* PREP_H */
-- 
2.11.0

Reply via email to