On 2018/03/26 23:20, Alvaro Herrera wrote:
> Pushed now.

Thank you!

> Amit Langote wrote:
>> On 2018/03/24 9:23, Alvaro Herrera wrote:
> 
>>> To fix this, I had to completely rework the "get partition parent root"
>>> stuff into "get list of ancestors of this partition".
>>
>> I wondered if a is_partition_ancestor(partrelid, ancestorid) isn't enough
>> instead of creating a list of ancestors and then looping over it as you've
>> done, but maybe what you have here is fine.
> 
> Yeah, I wondered about doing it that way too (since you can stop looking
> early), but decided that I didn't like repeatedly opening pg_inherits
> for each level.  Anyway the most common case is a single level, and in
> rare cases two levels ... I don't think we're going to see much more
> than that.  So it doesn't matter too much.  We can refine later anyway,
> if this becomes a hot spot (I doubt it TBH).

Yes, I suppose.

>>> * General code style improvements, comment rewording, etc.
>>
>> There was one comment in Fujita-san's review he posted on Friday [1] that
>> doesn't seem to be addressed in v10, which I think we probably should.  It
>> was this comment:
>>
>> "ExecBuildProjectionInfo is called without setting the tuple descriptor of
>> mtstate->mt_conflproj to tupDesc.  That might work at least for now, but I
>> think it's a good thing to set it appropriately to make that future proof."
>>
>> All of his other comments seem to have been taken care of in v10.  I have
>> fixed the above one in the attached updated version.
> 
> I was of two minds about this item myself; we don't use the tupdesc for
> anything at that point and I expect more things would break if we
> required that.  But I don't think it hurts, so I kept it.
> 
> 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.

> 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.

Thanks,
Amit
diff --git a/src/backend/executor/execPartition.c 
b/src/backend/executor/execPartition.c
index 9a13188649..f1a972e235 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -557,7 +557,6 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
                        {
                                List       *onconflset;
                                TupleDesc       tupDesc;
-                               bool            found_whole_row;
 
                                leaf_part_rri->ri_onConflict = 
makeNode(OnConflictSetState);
 
@@ -571,12 +570,10 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
                                onconflset = (List *) copyObject((Node *) 
node->onConflictSet);
                                onconflset =
                                        map_partition_varattnos(onconflset, 
INNER_VAR, partrel,
-                                                                               
        firstResultRel, &found_whole_row);
-                               Assert(!found_whole_row);
+                                                                               
        firstResultRel, NULL);
                                onconflset =
                                        map_partition_varattnos(onconflset, 
firstVarno, partrel,
-                                                                               
        firstResultRel, &found_whole_row);
-                               Assert(!found_whole_row);
+                                                                               
        firstResultRel, NULL);
 
                                /* Finally, adjust this tlist to match the 
partition. */
                                onconflset = adjust_partition_tlist(onconflset, 
map);
@@ -609,12 +606,10 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
                                        clause = copyObject((List *) 
node->onConflictWhere);
                                        clause = 
map_partition_varattnos(clause, INNER_VAR,
                                                                                
                         partrel, firstResultRel,
-                                                                               
                         &found_whole_row);
-                                       Assert(!found_whole_row);
+                                                                               
                         NULL);
                                        clause = 
map_partition_varattnos(clause, firstVarno,
                                                                                
                         partrel, firstResultRel,
-                                                                               
                         &found_whole_row);
-                                       Assert(!found_whole_row);
+                                                                               
                         NULL);
                                        
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;

Reply via email to