mirror of
https://github.com/postgres/postgres.git
synced 2025-04-22 23:02:54 +03:00
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
This commit is contained in:
parent
3a2d636598
commit
158b7bc6d7
@ -24,6 +24,7 @@
|
|||||||
#include "nodes/makefuncs.h"
|
#include "nodes/makefuncs.h"
|
||||||
#include "partitioning/partbounds.h"
|
#include "partitioning/partbounds.h"
|
||||||
#include "partitioning/partprune.h"
|
#include "partitioning/partprune.h"
|
||||||
|
#include "rewrite/rewriteManip.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/partcache.h"
|
#include "utils/partcache.h"
|
||||||
#include "utils/rel.h"
|
#include "utils/rel.h"
|
||||||
@ -307,8 +308,12 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
|
|||||||
ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
|
ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
|
||||||
Relation rootrel = resultRelInfo->ri_RelationDesc,
|
Relation rootrel = resultRelInfo->ri_RelationDesc,
|
||||||
partrel;
|
partrel;
|
||||||
|
Relation firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc;
|
||||||
ResultRelInfo *leaf_part_rri;
|
ResultRelInfo *leaf_part_rri;
|
||||||
MemoryContext oldContext;
|
MemoryContext oldContext;
|
||||||
|
AttrNumber *part_attnos = NULL;
|
||||||
|
bool found_whole_row;
|
||||||
|
bool equalTupdescs;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We locked all the partitions in ExecSetupPartitionTupleRouting
|
* We locked all the partitions in ExecSetupPartitionTupleRouting
|
||||||
@ -356,6 +361,10 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
|
|||||||
(node != NULL &&
|
(node != NULL &&
|
||||||
node->onConflictAction != ONCONFLICT_NONE));
|
node->onConflictAction != ONCONFLICT_NONE));
|
||||||
|
|
||||||
|
/* if tuple descs are identical, we don't need to map the attrs */
|
||||||
|
equalTupdescs = equalTupleDescs(RelationGetDescr(partrel),
|
||||||
|
RelationGetDescr(firstResultRel));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Build WITH CHECK OPTION constraints for the partition. Note that we
|
* Build WITH CHECK OPTION constraints for the partition. Note that we
|
||||||
* didn't build the withCheckOptionList for partitions within the planner,
|
* didn't build the withCheckOptionList for partitions within the planner,
|
||||||
@ -369,7 +378,6 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
|
|||||||
List *wcoExprs = NIL;
|
List *wcoExprs = NIL;
|
||||||
ListCell *ll;
|
ListCell *ll;
|
||||||
int firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex;
|
int firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex;
|
||||||
Relation firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* In the case of INSERT on a partitioned table, there is only one
|
* In the case of INSERT on a partitioned table, there is only one
|
||||||
@ -397,8 +405,22 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
|
|||||||
/*
|
/*
|
||||||
* Convert Vars in it to contain this partition's attribute numbers.
|
* Convert Vars in it to contain this partition's attribute numbers.
|
||||||
*/
|
*/
|
||||||
wcoList = map_partition_varattnos(wcoList, firstVarno,
|
if (!equalTupdescs)
|
||||||
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)
|
foreach(ll, wcoList)
|
||||||
{
|
{
|
||||||
WithCheckOption *wco = castNode(WithCheckOption, lfirst(ll));
|
WithCheckOption *wco = castNode(WithCheckOption, lfirst(ll));
|
||||||
@ -425,7 +447,6 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
|
|||||||
ExprContext *econtext;
|
ExprContext *econtext;
|
||||||
List *returningList;
|
List *returningList;
|
||||||
int firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex;
|
int firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex;
|
||||||
Relation firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc;
|
|
||||||
|
|
||||||
/* See the comment above for WCO lists. */
|
/* See the comment above for WCO lists. */
|
||||||
Assert((node->operation == CMD_INSERT &&
|
Assert((node->operation == CMD_INSERT &&
|
||||||
@ -443,12 +464,26 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
|
|||||||
*/
|
*/
|
||||||
returningList = linitial(node->returningLists);
|
returningList = linitial(node->returningLists);
|
||||||
|
|
||||||
/*
|
if (!equalTupdescs)
|
||||||
* Convert Vars in it to contain this partition's attribute numbers.
|
{
|
||||||
*/
|
/*
|
||||||
returningList = map_partition_varattnos(returningList, firstVarno,
|
* Convert Vars in it to contain this partition's attribute numbers.
|
||||||
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;
|
leaf_part_rri->ri_returningList = returningList;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -473,7 +508,6 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
|
|||||||
{
|
{
|
||||||
TupleConversionMap *map = proute->parent_child_tupconv_maps[partidx];
|
TupleConversionMap *map = proute->parent_child_tupconv_maps[partidx];
|
||||||
int firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex;
|
int firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex;
|
||||||
Relation firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc;
|
|
||||||
TupleDesc partrelDesc = RelationGetDescr(partrel);
|
TupleDesc partrelDesc = RelationGetDescr(partrel);
|
||||||
ExprContext *econtext = mtstate->ps.ps_ExprContext;
|
ExprContext *econtext = mtstate->ps.ps_ExprContext;
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
@ -549,17 +583,33 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
|
|||||||
* target relation (firstVarno).
|
* target relation (firstVarno).
|
||||||
*/
|
*/
|
||||||
onconflset = (List *) copyObject((Node *) node->onConflictSet);
|
onconflset = (List *) copyObject((Node *) node->onConflictSet);
|
||||||
onconflset =
|
if (!equalTupdescs)
|
||||||
map_partition_varattnos(onconflset, INNER_VAR, partrel,
|
{
|
||||||
firstResultRel, &found_whole_row);
|
if (part_attnos == NULL)
|
||||||
Assert(!found_whole_row);
|
part_attnos =
|
||||||
onconflset =
|
convert_tuples_by_name_map(RelationGetDescr(partrel),
|
||||||
map_partition_varattnos(onconflset, firstVarno, partrel,
|
RelationGetDescr(firstResultRel),
|
||||||
firstResultRel, &found_whole_row);
|
gettext_noop("could not convert row type"));
|
||||||
Assert(!found_whole_row);
|
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. */
|
/* Finally, adjust this tlist to match the partition. */
|
||||||
onconflset = adjust_partition_tlist(onconflset, map);
|
onconflset = adjust_partition_tlist(onconflset, map);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Build UPDATE SET's projection info. The user of this
|
* Build UPDATE SET's projection info. The user of this
|
||||||
@ -587,14 +637,25 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
|
|||||||
List *clause;
|
List *clause;
|
||||||
|
|
||||||
clause = copyObject((List *) node->onConflictWhere);
|
clause = copyObject((List *) node->onConflictWhere);
|
||||||
clause = map_partition_varattnos(clause, INNER_VAR,
|
if (!equalTupdescs)
|
||||||
partrel, firstResultRel,
|
{
|
||||||
&found_whole_row);
|
clause = (List *)
|
||||||
Assert(!found_whole_row);
|
map_variable_attnos((Node *) clause,
|
||||||
clause = map_partition_varattnos(clause, firstVarno,
|
INNER_VAR, 0,
|
||||||
partrel, firstResultRel,
|
part_attnos,
|
||||||
&found_whole_row);
|
RelationGetDescr(firstResultRel)->natts,
|
||||||
Assert(!found_whole_row);
|
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 =
|
leaf_part_rri->ri_onConflict->oc_WhereClause =
|
||||||
ExecInitQual((List *) clause, &mtstate->ps);
|
ExecInitQual((List *) clause, &mtstate->ps);
|
||||||
}
|
}
|
||||||
|
@ -884,4 +884,20 @@ insert into parted_conflict values (40, 'forty');
|
|||||||
insert into parted_conflict_1 values (40, 'cuarenta')
|
insert into parted_conflict_1 values (40, 'cuarenta')
|
||||||
on conflict (a) do update set b = excluded.b;
|
on conflict (a) do update set b = excluded.b;
|
||||||
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
|
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, 'cincuenta', 1);
|
||||||
|
insert into parted_conflict values (50, 'cincuenta', 2)
|
||||||
|
on conflict (a, b) do update set (a, b, c) = row(excluded.*)
|
||||||
|
where parted_conflict = (50, text 'cincuenta', 1) and
|
||||||
|
excluded = (50, text 'cincuenta', 2);
|
||||||
|
-- should see (50, 'cincuenta', 2)
|
||||||
|
select * from parted_conflict order by a;
|
||||||
|
a | b | c
|
||||||
|
----+-----------+---
|
||||||
|
50 | cincuenta | 2
|
||||||
|
(1 row)
|
||||||
|
|
||||||
drop table parted_conflict;
|
drop table parted_conflict;
|
||||||
|
@ -559,3 +559,21 @@ insert into parted_conflict values (40, 'forty');
|
|||||||
insert into parted_conflict_1 values (40, 'cuarenta')
|
insert into parted_conflict_1 values (40, 'cuarenta')
|
||||||
on conflict (a) do update set b = excluded.b;
|
on conflict (a) do update set b = excluded.b;
|
||||||
drop table parted_conflict;
|
drop table parted_conflict;
|
||||||
|
|
||||||
|
-- test whole-row Vars in ON CONFLICT expressions
|
||||||
|
create table parted_conflict (a int, b text, c int) partition by range (a);
|
||||||
|
create table parted_conflict_1 (drp text, c int, a int, b text);
|
||||||
|
alter table parted_conflict_1 drop column drp;
|
||||||
|
create unique index on parted_conflict (a, b);
|
||||||
|
alter table parted_conflict attach partition parted_conflict_1 for values from (0) to (1000);
|
||||||
|
truncate parted_conflict;
|
||||||
|
insert into parted_conflict values (50, 'cincuenta', 1);
|
||||||
|
insert into parted_conflict values (50, 'cincuenta', 2)
|
||||||
|
on conflict (a, b) do update set (a, b, c) = row(excluded.*)
|
||||||
|
where parted_conflict = (50, text 'cincuenta', 1) and
|
||||||
|
excluded = (50, text 'cincuenta', 2);
|
||||||
|
|
||||||
|
-- should see (50, 'cincuenta', 2)
|
||||||
|
select * from parted_conflict order by a;
|
||||||
|
|
||||||
|
drop table parted_conflict;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user