On 2018/04/10 11:56, Amit Langote wrote:
> On 2018/03/27 13:27, Amit Langote wrote:
>> On 2018/03/26 23:20, Alvaro Herrera wrote:
>>> The one thing I wasn't terribly in love with is the four calls to
>>> map_partition_varattnos(), creating the attribute map four times ... but
>>> we already have it in the TupleConversionMap, no?  Looks like we could
>>> save a bunch of work there.
>>
>> Hmm, actually we can't use that map, assuming you're talking about the
>> following map:
>>
>>   TupleConversionMap *map = proute->parent_child_tupconv_maps[partidx];
>>
>> We can only use that to tell if we need converting expressions (as we
>> currently do), but it cannot be used to actually convert the expressions.
>> The map in question is for use by do_convert_tuple(), not to map varattnos
>> in Vars using map_variable_attnos().
>>
>> But it's definitely a bit undesirable to have various
>> map_partition_varattnos() calls within ExecInitPartitionInfo() to
>> initialize the same information (the map) multiple times.
> 
> I will try to think of doing something about this later this week.

The solution I came up with is to call map_variable_attnos() directly,
instead of going through map_partition_varattnos() every time, after first
creating the attribute map ourselves.

>>> And a final item is: can we have a whole-row expression in the clauses?
>>> We currently don't handle those either, not even to throw an error.
>>> [figures a test case] ... and now that I test it, it does crash!
>>>
>>> create table part (a int primary key, b text) partition by range (a);
>>> create table part1 (b text, a int not null);
>>> alter table part attach partition part1 for values from (1) to (1000);
>>> insert into part values (1, 'two') on conflict (a)
>>>   do update set b = format('%s (was %s)', excluded.b, part.b)
>>>   where part.* *<> (1, text 'two');
>>>
>>> I think this means we should definitely handle found_whole_row.  (If you
>>> create part1 in the normal way, it works as you'd expect.)
>>
>> I agree.  That means we simply remove the Assert after the
>> map_partition_varattnos call.
>>
>>> I'm going to close a few other things first, then come back to this.
>>
>> Attached find a patch to fix the whole-row expression issue.  I added your
>> test to insert_conflict.sql.

Combined the above patch into the attached patch.

Thanks,
Amit
From a90decd69a42bebdb6e07c8268686c0500f8c48e Mon Sep 17 00:00:00 2001
From: amit <amitlangot...@gmail.com>
Date: Mon, 16 Apr 2018 17:31:42 +0900
Subject: [PATCH v2] Couple of fixes for ExecInitPartitionInfo

First, avoid repeated calling of map_partition_varattnos.  To do that,
generate the rootrel -> partrel attribute conversion map ourselves
just once and call map_variable_attnos() directly with it.

Second, support conversion of whole-row variables that appear in
ON CONFLICT expressions.  Add relevant test.
---
 src/backend/executor/execPartition.c          | 88 ++++++++++++++++++++-------
 src/test/regress/expected/insert_conflict.out | 16 +++++
 src/test/regress/sql/insert_conflict.sql      | 14 +++++
 3 files changed, 97 insertions(+), 21 deletions(-)

diff --git a/src/backend/executor/execPartition.c 
b/src/backend/executor/execPartition.c
index 218645d43b..1727e111bb 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -24,6 +24,7 @@
 #include "nodes/makefuncs.h"
 #include "partitioning/partbounds.h"
 #include "partitioning/partprune.h"
+#include "rewrite/rewriteManip.h"
 #include "utils/lsyscache.h"
 #include "utils/partcache.h"
 #include "utils/rel.h"
@@ -309,6 +310,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
                                partrel;
        ResultRelInfo *leaf_part_rri;
        MemoryContext oldContext;
+       AttrNumber *part_attnos = NULL;
+       bool            found_whole_row;
 
        /*
         * We locked all the partitions in ExecSetupPartitionTupleRouting
@@ -397,8 +400,19 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
                /*
                 * Convert Vars in it to contain this partition's attribute 
numbers.
                 */
-               wcoList = map_partition_varattnos(wcoList, firstVarno,
-                                                                               
  partrel, firstResultRel, NULL);
+               part_attnos =
+                       convert_tuples_by_name_map(RelationGetDescr(partrel),
+                                                                          
RelationGetDescr(firstResultRel),
+                                                                          
gettext_noop("could not convert row type"));
+               wcoList = (List *)
+                               map_variable_attnos((Node *) wcoList,
+                                                                       
firstVarno, 0,
+                                                                       
part_attnos,
+                                                                       
RelationGetDescr(firstResultRel)->natts,
+                                                                       
RelationGetForm(partrel)->reltype,
+                                                                       
&found_whole_row);
+               /* We ignore the value of found_whole_row. */
+
                foreach(ll, wcoList)
                {
                        WithCheckOption *wco = castNode(WithCheckOption, 
lfirst(ll));
@@ -446,9 +460,20 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
                /*
                 * Convert Vars in it to contain this partition's attribute 
numbers.
                 */
-               returningList = map_partition_varattnos(returningList, 
firstVarno,
-                                                                               
                partrel, firstResultRel,
-                                                                               
                NULL);
+               if (part_attnos == NULL)
+                       part_attnos =
+                               
convert_tuples_by_name_map(RelationGetDescr(partrel),
+                                                                               
   RelationGetDescr(firstResultRel),
+                                                                               
   gettext_noop("could not convert row type"));
+               returningList = (List *)
+                               map_variable_attnos((Node *) returningList,
+                                                                       
firstVarno, 0,
+                                                                       
part_attnos,
+                                                                       
RelationGetDescr(firstResultRel)->natts,
+                                                                       
RelationGetForm(partrel)->reltype,
+                                                                       
&found_whole_row);
+               /* We ignore the value of found_whole_row. */
+
                leaf_part_rri->ri_returningList = returningList;
 
                /*
@@ -549,14 +574,27 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
                                 * target relation (firstVarno).
                                 */
                                onconflset = (List *) copyObject((Node *) 
node->onConflictSet);
-                               onconflset =
-                                       map_partition_varattnos(onconflset, 
INNER_VAR, partrel,
-                                                                               
        firstResultRel, &found_whole_row);
-                               Assert(!found_whole_row);
-                               onconflset =
-                                       map_partition_varattnos(onconflset, 
firstVarno, partrel,
-                                                                               
        firstResultRel, &found_whole_row);
-                               Assert(!found_whole_row);
+                               if (part_attnos == NULL)
+                                       part_attnos =
+                                               
convert_tuples_by_name_map(RelationGetDescr(partrel),
+                                                                               
                   RelationGetDescr(firstResultRel),
+                                                                               
                   gettext_noop("could not convert row type"));
+                               onconflset = (List *)
+                                       map_variable_attnos((Node *) onconflset,
+                                                                               
INNER_VAR, 0,
+                                                                               
part_attnos,
+                                                                               
RelationGetDescr(firstResultRel)->natts,
+                                                                               
RelationGetForm(partrel)->reltype,
+                                                                               
&found_whole_row);
+                               /* We ignore the value of found_whole_row. */
+                               onconflset = (List *)
+                                       map_variable_attnos((Node *) onconflset,
+                                                                               
firstVarno, 0,
+                                                                               
part_attnos,
+                                                                               
RelationGetDescr(firstResultRel)->natts,
+                                                                               
RelationGetForm(partrel)->reltype,
+                                                                               
&found_whole_row);
+                               /* We ignore the value of found_whole_row. */
 
                                /* Finally, adjust this tlist to match the 
partition. */
                                onconflset = adjust_partition_tlist(onconflset, 
map);
@@ -587,14 +625,22 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
                                        List       *clause;
 
                                        clause = copyObject((List *) 
node->onConflictWhere);
-                                       clause = 
map_partition_varattnos(clause, INNER_VAR,
-                                                                               
                         partrel, firstResultRel,
-                                                                               
                         &found_whole_row);
-                                       Assert(!found_whole_row);
-                                       clause = 
map_partition_varattnos(clause, firstVarno,
-                                                                               
                         partrel, firstResultRel,
-                                                                               
                         &found_whole_row);
-                                       Assert(!found_whole_row);
+                                       clause = (List *)
+                                               map_variable_attnos((Node *) 
clause,
+                                                                               
        INNER_VAR, 0,
+                                                                               
        part_attnos,
+                                                                               
        RelationGetDescr(firstResultRel)->natts,
+                                                                               
        RelationGetForm(partrel)->reltype,
+                                                                               
        &found_whole_row);
+                                       /* We ignore the value of 
found_whole_row. */
+                                       clause = (List *)
+                                               map_variable_attnos((Node *) 
clause,
+                                                                               
        firstVarno, 0,
+                                                                               
        part_attnos,
+                                                                               
        RelationGetDescr(firstResultRel)->natts,
+                                                                               
        RelationGetForm(partrel)->reltype,
+                                                                               
        &found_whole_row);
+                                       /* We ignore the value of 
found_whole_row. */
                                        
leaf_part_rri->ri_onConflict->oc_WhereClause =
                                                ExecInitQual((List *) clause, 
&mtstate->ps);
                                }
diff --git a/src/test/regress/expected/insert_conflict.out 
b/src/test/regress/expected/insert_conflict.out
index 2d7061fa1b..66ca1839bc 100644
--- a/src/test/regress/expected/insert_conflict.out
+++ b/src/test/regress/expected/insert_conflict.out
@@ -884,4 +884,20 @@ insert into parted_conflict values (40, 'forty');
 insert into parted_conflict_1 values (40, 'cuarenta')
   on conflict (a) do update set b = excluded.b;
 ERROR:  there is no unique or exclusion constraint matching the ON CONFLICT 
specification
+-- test whole-row Vars in ON CONFLICT expressions
+create unique index on parted_conflict (a, b);
+alter table parted_conflict add c int;
+truncate parted_conflict;
+insert into parted_conflict values (50, 'cuarenta', 1);
+insert into parted_conflict values (50, 'cuarenta', 2)
+  on conflict (a, b) do update set (a, b, c) = row(excluded.*)
+  where parted_conflict = (50, text 'cuarenta', 1) and
+        excluded = (50, text 'cuarenta', 2);
+-- should see (50, 'cuarenta', 2)
+select * from parted_conflict order by a;
+ a  |    b     | c 
+----+----------+---
+ 50 | cuarenta | 2
+(1 row)
+
 drop table parted_conflict;
diff --git a/src/test/regress/sql/insert_conflict.sql 
b/src/test/regress/sql/insert_conflict.sql
index 6c50fd61eb..fb30530a54 100644
--- a/src/test/regress/sql/insert_conflict.sql
+++ b/src/test/regress/sql/insert_conflict.sql
@@ -558,4 +558,18 @@ create table parted_conflict_1_1 partition of 
parted_conflict_1 for values from
 insert into parted_conflict values (40, 'forty');
 insert into parted_conflict_1 values (40, 'cuarenta')
   on conflict (a) do update set b = excluded.b;
+
+-- test whole-row Vars in ON CONFLICT expressions
+create unique index on parted_conflict (a, b);
+alter table parted_conflict add c int;
+truncate parted_conflict;
+insert into parted_conflict values (50, 'cuarenta', 1);
+insert into parted_conflict values (50, 'cuarenta', 2)
+  on conflict (a, b) do update set (a, b, c) = row(excluded.*)
+  where parted_conflict = (50, text 'cuarenta', 1) and
+        excluded = (50, text 'cuarenta', 2);
+
+-- should see (50, 'cuarenta', 2)
+select * from parted_conflict order by a;
+
 drop table parted_conflict;
-- 
2.11.0

Reply via email to