Skip to content

Commit 158b7bc

Browse files
committed
Ignore whole-rows in INSERT/CONFLICT with partitioned tables
We had an Assert() preventing whole-row expressions from being used in the SET clause of INSERT ON CONFLICT, but it seems unnecessary, given some tests, so remove it. Add a new test to exercise the case. Still at ExecInitPartitionInfo, we used map_partition_varattnos (which constructs an attribute map, then calls map_variable_attnos) using the same two relations many times in different expressions and with different parameters. Constructing the map over and over is a waste. To avoid this repeated work, construct the map once, and use map_variable_attnos() directly instead. Author: Amit Langote, per comments by me (Álvaro) Discussion: https://postgr.es/m/20180326142016.m4st5e34chrzrknk@alvherre.pgsql
1 parent 3a2d636 commit 158b7bc

File tree

3 files changed

+125
-30
lines changed

3 files changed

+125
-30
lines changed

src/backend/executor/execPartition.c

Lines changed: 91 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "nodes/makefuncs.h"
2525
#include "partitioning/partbounds.h"
2626
#include "partitioning/partprune.h"
27+
#include "rewrite/rewriteManip.h"
2728
#include "utils/lsyscache.h"
2829
#include "utils/partcache.h"
2930
#include "utils/rel.h"
@@ -307,8 +308,12 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
307308
ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
308309
Relation rootrel = resultRelInfo->ri_RelationDesc,
309310
partrel;
311+
Relation firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc;
310312
ResultRelInfo *leaf_part_rri;
311313
MemoryContext oldContext;
314+
AttrNumber *part_attnos = NULL;
315+
bool found_whole_row;
316+
bool equalTupdescs;
312317

313318
/*
314319
* We locked all the partitions in ExecSetupPartitionTupleRouting
@@ -356,6 +361,10 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
356361
(node != NULL &&
357362
node->onConflictAction != ONCONFLICT_NONE));
358363

364+
/* if tuple descs are identical, we don't need to map the attrs */
365+
equalTupdescs = equalTupleDescs(RelationGetDescr(partrel),
366+
RelationGetDescr(firstResultRel));
367+
359368
/*
360369
* Build WITH CHECK OPTION constraints for the partition. Note that we
361370
* didn't build the withCheckOptionList for partitions within the planner,
@@ -369,7 +378,6 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
369378
List *wcoExprs = NIL;
370379
ListCell *ll;
371380
int firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex;
372-
Relation firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc;
373381

374382
/*
375383
* In the case of INSERT on a partitioned table, there is only one
@@ -397,8 +405,22 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
397405
/*
398406
* Convert Vars in it to contain this partition's attribute numbers.
399407
*/
400-
wcoList = map_partition_varattnos(wcoList, firstVarno,
401-
partrel, firstResultRel, NULL);
408+
if (!equalTupdescs)
409+
{
410+
part_attnos =
411+
convert_tuples_by_name_map(RelationGetDescr(partrel),
412+
RelationGetDescr(firstResultRel),
413+
gettext_noop("could not convert row type"));
414+
wcoList = (List *)
415+
map_variable_attnos((Node *) wcoList,
416+
firstVarno, 0,
417+
part_attnos,
418+
RelationGetDescr(firstResultRel)->natts,
419+
RelationGetForm(partrel)->reltype,
420+
&found_whole_row);
421+
/* We ignore the value of found_whole_row. */
422+
}
423+
402424
foreach(ll, wcoList)
403425
{
404426
WithCheckOption *wco = castNode(WithCheckOption, lfirst(ll));
@@ -425,7 +447,6 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
425447
ExprContext *econtext;
426448
List *returningList;
427449
int firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex;
428-
Relation firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc;
429450

430451
/* See the comment above for WCO lists. */
431452
Assert((node->operation == CMD_INSERT &&
@@ -443,12 +464,26 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
443464
*/
444465
returningList = linitial(node->returningLists);
445466

446-
/*
447-
* Convert Vars in it to contain this partition's attribute numbers.
448-
*/
449-
returningList = map_partition_varattnos(returningList, firstVarno,
450-
partrel, firstResultRel,
451-
NULL);
467+
if (!equalTupdescs)
468+
{
469+
/*
470+
* Convert Vars in it to contain this partition's attribute numbers.
471+
*/
472+
if (part_attnos == NULL)
473+
part_attnos =
474+
convert_tuples_by_name_map(RelationGetDescr(partrel),
475+
RelationGetDescr(firstResultRel),
476+
gettext_noop("could not convert row type"));
477+
returningList = (List *)
478+
map_variable_attnos((Node *) returningList,
479+
firstVarno, 0,
480+
part_attnos,
481+
RelationGetDescr(firstResultRel)->natts,
482+
RelationGetForm(partrel)->reltype,
483+
&found_whole_row);
484+
/* We ignore the value of found_whole_row. */
485+
}
486+
452487
leaf_part_rri->ri_returningList = returningList;
453488

454489
/*
@@ -473,7 +508,6 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
473508
{
474509
TupleConversionMap *map = proute->parent_child_tupconv_maps[partidx];
475510
int firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex;
476-
Relation firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc;
477511
TupleDesc partrelDesc = RelationGetDescr(partrel);
478512
ExprContext *econtext = mtstate->ps.ps_ExprContext;
479513
ListCell *lc;
@@ -549,17 +583,33 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
549583
* target relation (firstVarno).
550584
*/
551585
onconflset = (List *) copyObject((Node *) node->onConflictSet);
552-
onconflset =
553-
map_partition_varattnos(onconflset, INNER_VAR, partrel,
554-
firstResultRel, &found_whole_row);
555-
Assert(!found_whole_row);
556-
onconflset =
557-
map_partition_varattnos(onconflset, firstVarno, partrel,
558-
firstResultRel, &found_whole_row);
559-
Assert(!found_whole_row);
560-
561-
/* Finally, adjust this tlist to match the partition. */
562-
onconflset = adjust_partition_tlist(onconflset, map);
586+
if (!equalTupdescs)
587+
{
588+
if (part_attnos == NULL)
589+
part_attnos =
590+
convert_tuples_by_name_map(RelationGetDescr(partrel),
591+
RelationGetDescr(firstResultRel),
592+
gettext_noop("could not convert row type"));
593+
onconflset = (List *)
594+
map_variable_attnos((Node *) onconflset,
595+
INNER_VAR, 0,
596+
part_attnos,
597+
RelationGetDescr(firstResultRel)->natts,
598+
RelationGetForm(partrel)->reltype,
599+
&found_whole_row);
600+
/* We ignore the value of found_whole_row. */
601+
onconflset = (List *)
602+
map_variable_attnos((Node *) onconflset,
603+
firstVarno, 0,
604+
part_attnos,
605+
RelationGetDescr(firstResultRel)->natts,
606+
RelationGetForm(partrel)->reltype,
607+
&found_whole_row);
608+
/* We ignore the value of found_whole_row. */
609+
610+
/* Finally, adjust this tlist to match the partition. */
611+
onconflset = adjust_partition_tlist(onconflset, map);
612+
}
563613

564614
/*
565615
* Build UPDATE SET's projection info. The user of this
@@ -587,14 +637,25 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
587637
List *clause;
588638

589639
clause = copyObject((List *) node->onConflictWhere);
590-
clause = map_partition_varattnos(clause, INNER_VAR,
591-
partrel, firstResultRel,
592-
&found_whole_row);
593-
Assert(!found_whole_row);
594-
clause = map_partition_varattnos(clause, firstVarno,
595-
partrel, firstResultRel,
596-
&found_whole_row);
597-
Assert(!found_whole_row);
640+
if (!equalTupdescs)
641+
{
642+
clause = (List *)
643+
map_variable_attnos((Node *) clause,
644+
INNER_VAR, 0,
645+
part_attnos,
646+
RelationGetDescr(firstResultRel)->natts,
647+
RelationGetForm(partrel)->reltype,
648+
&found_whole_row);
649+
/* We ignore the value of found_whole_row. */
650+
clause = (List *)
651+
map_variable_attnos((Node *) clause,
652+
firstVarno, 0,
653+
part_attnos,
654+
RelationGetDescr(firstResultRel)->natts,
655+
RelationGetForm(partrel)->reltype,
656+
&found_whole_row);
657+
/* We ignore the value of found_whole_row. */
658+
}
598659
leaf_part_rri->ri_onConflict->oc_WhereClause =
599660
ExecInitQual((List *) clause, &mtstate->ps);
600661
}

src/test/regress/expected/insert_conflict.out

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -884,4 +884,20 @@ insert into parted_conflict values (40, 'forty');
884884
insert into parted_conflict_1 values (40, 'cuarenta')
885885
on conflict (a) do update set b = excluded.b;
886886
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
887+
-- test whole-row Vars in ON CONFLICT expressions
888+
create unique index on parted_conflict (a, b);
889+
alter table parted_conflict add c int;
890+
truncate parted_conflict;
891+
insert into parted_conflict values (50, 'cincuenta', 1);
892+
insert into parted_conflict values (50, 'cincuenta', 2)
893+
on conflict (a, b) do update set (a, b, c) = row(excluded.*)
894+
where parted_conflict = (50, text 'cincuenta', 1) and
895+
excluded = (50, text 'cincuenta', 2);
896+
-- should see (50, 'cincuenta', 2)
897+
select * from parted_conflict order by a;
898+
a | b | c
899+
----+-----------+---
900+
50 | cincuenta | 2
901+
(1 row)
902+
887903
drop table parted_conflict;

src/test/regress/sql/insert_conflict.sql

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,3 +559,21 @@ insert into parted_conflict values (40, 'forty');
559559
insert into parted_conflict_1 values (40, 'cuarenta')
560560
on conflict (a) do update set b = excluded.b;
561561
drop table parted_conflict;
562+
563+
-- test whole-row Vars in ON CONFLICT expressions
564+
create table parted_conflict (a int, b text, c int) partition by range (a);
565+
create table parted_conflict_1 (drp text, c int, a int, b text);
566+
alter table parted_conflict_1 drop column drp;
567+
create unique index on parted_conflict (a, b);
568+
alter table parted_conflict attach partition parted_conflict_1 for values from (0) to (1000);
569+
truncate parted_conflict;
570+
insert into parted_conflict values (50, 'cincuenta', 1);
571+
insert into parted_conflict values (50, 'cincuenta', 2)
572+
on conflict (a, b) do update set (a, b, c) = row(excluded.*)
573+
where parted_conflict = (50, text 'cincuenta', 1) and
574+
excluded = (50, text 'cincuenta', 2);
575+
576+
-- should see (50, 'cincuenta', 2)
577+
select * from parted_conflict order by a;
578+
579+
drop table parted_conflict;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy