summaryrefslogtreecommitdiff
path: root/src/backend/executor/nodeModifyTable.c
diff options
context:
space:
mode:
authorPavan Deolasee2017-06-14 05:42:18 +0000
committerPavan Deolasee2017-06-14 05:42:18 +0000
commit15dd5274c323fb93e4e3ea9ad2185aaaec10f79c (patch)
tree9dafb4c7f735d9429ea461dc792933af87493c33 /src/backend/executor/nodeModifyTable.c
parentdfbb88e3bbb526dcb204b456b9e5cfd9d10d0d0a (diff)
parentd5cb3bab564e0927ffac7c8729eacf181a12dd40 (diff)
Merge from PG master upto d5cb3bab564e0927ffac7c8729eacf181a12dd40
This is the result of the "git merge remotes/PGSQL/master" upto the said commit point. We have done some basic analysis, fixed compilation problems etc, but bulk of the logical problems in conflict resolution etc will be handled by subsequent commits.
Diffstat (limited to 'src/backend/executor/nodeModifyTable.c')
-rw-r--r--src/backend/executor/nodeModifyTable.c316
1 files changed, 278 insertions, 38 deletions
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 439e36ee3a..0ee82e3add 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -4,7 +4,7 @@
* routines to handle ModifyTable nodes.
*
* Portions Copyright (c) 2012-2014, TransLattice, Inc.
- * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
@@ -46,6 +46,7 @@
#include "foreign/fdwapi.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
+#include "parser/parsetree.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
@@ -176,7 +177,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
econtext->ecxt_outertuple = planSlot;
/* Compute the RETURNING expressions */
- return ExecProject(projectReturning, NULL);
+ return ExecProject(projectReturning);
}
/*
@@ -195,10 +196,25 @@ ExecCheckHeapTupleVisible(EState *estate,
if (!IsolationUsesXactSnapshot())
return;
+ /*
+ * We need buffer pin and lock to call HeapTupleSatisfiesVisibility.
+ * Caller should be holding pin, but not lock.
+ */
+ LockBuffer(buffer, BUFFER_LOCK_SHARE);
if (!HeapTupleSatisfiesVisibility(tuple, estate->es_snapshot, buffer))
- ereport(ERROR,
- (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
+ {
+ /*
+ * We should not raise a serialization failure if the conflict is
+ * against a tuple inserted by our own transaction, even if it's not
+ * visible to our snapshot. (This would happen, for example, if
+ * conflicting keys are proposed for insertion in a single command.)
+ */
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple->t_data)))
+ ereport(ERROR,
+ (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
+ }
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
}
/*
@@ -244,9 +260,11 @@ ExecInsert(ModifyTableState *mtstate,
{
HeapTuple tuple;
ResultRelInfo *resultRelInfo;
+ ResultRelInfo *saved_resultRelInfo = NULL;
Relation resultRelationDesc;
Oid newId;
List *recheckIndexes = NIL;
+ TupleTableSlot *result = NULL;
/*
* get the heap tuple out of the tuple table slot, making sure we have a
@@ -258,6 +276,67 @@ ExecInsert(ModifyTableState *mtstate,
* get information on the (current) result relation
*/
resultRelInfo = estate->es_result_relation_info;
+
+ /* Determine the partition to heap_insert the tuple into */
+ if (mtstate->mt_partition_dispatch_info)
+ {
+ int leaf_part_index;
+ TupleConversionMap *map;
+
+ /*
+ * Away we go ... If we end up not finding a partition after all,
+ * ExecFindPartition() does not return and errors out instead.
+ * Otherwise, the returned value is to be used as an index into arrays
+ * mt_partitions[] and mt_partition_tupconv_maps[] that will get us
+ * the ResultRelInfo and TupleConversionMap for the partition,
+ * respectively.
+ */
+ leaf_part_index = ExecFindPartition(resultRelInfo,
+ mtstate->mt_partition_dispatch_info,
+ slot,
+ estate);
+ Assert(leaf_part_index >= 0 &&
+ leaf_part_index < mtstate->mt_num_partitions);
+
+ /*
+ * Save the old ResultRelInfo and switch to the one corresponding to
+ * the selected partition.
+ */
+ saved_resultRelInfo = resultRelInfo;
+ resultRelInfo = mtstate->mt_partitions + leaf_part_index;
+
+ /* We do not yet have a way to insert into a foreign partition */
+ if (resultRelInfo->ri_FdwRoutine)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot route inserted tuples to a foreign table")));
+
+ /* For ExecInsertIndexTuples() to work on the partition's indexes */
+ estate->es_result_relation_info = resultRelInfo;
+
+ /*
+ * We might need to convert from the parent rowtype to the partition
+ * rowtype.
+ */
+ map = mtstate->mt_partition_tupconv_maps[leaf_part_index];
+ if (map)
+ {
+ Relation partrel = resultRelInfo->ri_RelationDesc;
+
+ tuple = do_convert_tuple(tuple, map);
+
+ /*
+ * We must use the partition's tuple descriptor from this point
+ * on, until we're finished dealing with the partition. Use the
+ * dedicated slot for that.
+ */
+ slot = mtstate->mt_partition_tuple_slot;
+ Assert(slot != NULL);
+ ExecSetSlotDescriptor(slot, RelationGetDescr(partrel));
+ ExecStoreTuple(tuple, slot, InvalidBuffer, true);
+ }
+ }
+
resultRelationDesc = resultRelInfo->ri_RelationDesc;
/*
* If the result relation has OIDs, force the tuple's OID to zero so that
@@ -354,7 +433,7 @@ ExecInsert(ModifyTableState *mtstate,
/*
* Check the constraints of the tuple
*/
- if (resultRelationDesc->rd_att->constr)
+ if (resultRelationDesc->rd_att->constr || resultRelInfo->ri_PartitionCheck)
ExecConstraints(resultRelInfo, slot, estate);
if (onconflict != ONCONFLICT_NONE && resultRelInfo->ri_NumIndices > 0)
@@ -513,9 +592,12 @@ ExecInsert(ModifyTableState *mtstate,
/* Process RETURNING if present */
if (resultRelInfo->ri_projectReturning)
- return ExecProcessReturning(resultRelInfo, slot, planSlot);
+ result = ExecProcessReturning(resultRelInfo, slot, planSlot);
- return NULL;
+ if (saved_resultRelInfo)
+ estate->es_result_relation_info = saved_resultRelInfo;
+
+ return result;
}
/* ----------------------------------------------------------------
@@ -906,9 +988,11 @@ lreplace:;
resultRelInfo, slot, estate);
/*
- * Check the constraints of the tuple
+ * Check the constraints of the tuple. Note that we pass the same
+ * slot for the orig_slot argument, because unlike ExecInsert(), no
+ * tuple-routing is performed here, hence the slot remains unchanged.
*/
- if (resultRelationDesc->rd_att->constr)
+ if (resultRelationDesc->rd_att->constr || resultRelInfo->ri_PartitionCheck)
ExecConstraints(resultRelInfo, slot, estate);
/*
@@ -1068,7 +1152,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
{
ExprContext *econtext = mtstate->ps.ps_ExprContext;
Relation relation = resultRelInfo->ri_RelationDesc;
- List *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
+ ExprState *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
HeapTupleData tuple;
HeapUpdateFailureData hufd;
LockTupleMode lockmode;
@@ -1187,7 +1271,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
econtext->ecxt_innertuple = excludedSlot;
econtext->ecxt_outertuple = NULL;
- if (!ExecQual(onConflictSetWhere, econtext, false))
+ if (!ExecQual(onConflictSetWhere, econtext))
{
ReleaseBuffer(buffer);
InstrCountFiltered1(&mtstate->ps, 1);
@@ -1217,7 +1301,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
}
/* Project the new tuple version */
- ExecProject(resultRelInfo->ri_onConflictSetProj, NULL);
+ ExecProject(resultRelInfo->ri_onConflictSetProj);
/*
* Note that it is possible that the target tuple has been modified in
@@ -1245,19 +1329,29 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
static void
fireBSTriggers(ModifyTableState *node)
{
+ ResultRelInfo *resultRelInfo = node->resultRelInfo;
+
+ /*
+ * If the node modifies a partitioned table, we must fire its triggers.
+ * Note that in that case, node->resultRelInfo points to the first leaf
+ * partition, not the root table.
+ */
+ if (node->rootResultRelInfo != NULL)
+ resultRelInfo = node->rootResultRelInfo;
+
switch (node->operation)
{
case CMD_INSERT:
- ExecBSInsertTriggers(node->ps.state, node->resultRelInfo);
+ ExecBSInsertTriggers(node->ps.state, resultRelInfo);
if (node->mt_onconflict == ONCONFLICT_UPDATE)
ExecBSUpdateTriggers(node->ps.state,
- node->resultRelInfo);
+ resultRelInfo);
break;
case CMD_UPDATE:
- ExecBSUpdateTriggers(node->ps.state, node->resultRelInfo);
+ ExecBSUpdateTriggers(node->ps.state, resultRelInfo);
break;
case CMD_DELETE:
- ExecBSDeleteTriggers(node->ps.state, node->resultRelInfo);
+ ExecBSDeleteTriggers(node->ps.state, resultRelInfo);
break;
default:
elog(ERROR, "unknown operation");
@@ -1271,19 +1365,29 @@ fireBSTriggers(ModifyTableState *node)
static void
fireASTriggers(ModifyTableState *node)
{
+ ResultRelInfo *resultRelInfo = node->resultRelInfo;
+
+ /*
+ * If the node modifies a partitioned table, we must fire its triggers.
+ * Note that in that case, node->resultRelInfo points to the first leaf
+ * partition, not the root table.
+ */
+ if (node->rootResultRelInfo != NULL)
+ resultRelInfo = node->rootResultRelInfo;
+
switch (node->operation)
{
case CMD_INSERT:
if (node->mt_onconflict == ONCONFLICT_UPDATE)
ExecASUpdateTriggers(node->ps.state,
- node->resultRelInfo);
- ExecASInsertTriggers(node->ps.state, node->resultRelInfo);
+ resultRelInfo);
+ ExecASInsertTriggers(node->ps.state, resultRelInfo);
break;
case CMD_UPDATE:
- ExecASUpdateTriggers(node->ps.state, node->resultRelInfo);
+ ExecASUpdateTriggers(node->ps.state, resultRelInfo);
break;
case CMD_DELETE:
- ExecASDeleteTriggers(node->ps.state, node->resultRelInfo);
+ ExecASDeleteTriggers(node->ps.state, resultRelInfo);
break;
default:
elog(ERROR, "unknown operation");
@@ -1551,6 +1655,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
Plan *subplan;
ListCell *l;
int i;
+ Relation rel;
/* check for unsupported flags */
Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
@@ -1561,7 +1666,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
mtstate = makeNode(ModifyTableState);
mtstate->ps.plan = (Plan *) node;
mtstate->ps.state = estate;
- mtstate->ps.targetlist = NIL; /* not actually used */
mtstate->operation = operation;
mtstate->canSetTag = node->canSetTag;
@@ -1569,6 +1673,12 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
mtstate->mt_plans = (PlanState **) palloc0(sizeof(PlanState *) * nplans);
mtstate->resultRelInfo = estate->es_result_relations + node->resultRelIndex;
+
+ /* If modifying a partitioned table, initialize the root table info */
+ if (node->rootResultRelIndex >= 0)
+ mtstate->rootResultRelInfo = estate->es_root_result_relations +
+ node->rootResultRelIndex;
+
mtstate->mt_arowmarks = (List **) palloc0(sizeof(List *) * nplans);
mtstate->mt_nplans = nplans;
mtstate->mt_onconflict = node->onConflictAction;
@@ -1642,6 +1752,44 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
estate->es_result_relation_info = saved_resultRelInfo;
+ /* The root table RT index is at the head of the partitioned_rels list */
+ if (node->partitioned_rels)
+ {
+ Index root_rti;
+ Oid root_oid;
+
+ root_rti = linitial_int(node->partitioned_rels);
+ root_oid = getrelid(root_rti, estate->es_range_table);
+ rel = heap_open(root_oid, NoLock); /* locked by InitPlan */
+ }
+ else
+ rel = mtstate->resultRelInfo->ri_RelationDesc;
+
+ /* Build state for INSERT tuple routing */
+ if (operation == CMD_INSERT &&
+ rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ {
+ PartitionDispatch *partition_dispatch_info;
+ ResultRelInfo *partitions;
+ TupleConversionMap **partition_tupconv_maps;
+ TupleTableSlot *partition_tuple_slot;
+ int num_parted,
+ num_partitions;
+
+ ExecSetupPartitionTupleRouting(rel,
+ &partition_dispatch_info,
+ &partitions,
+ &partition_tupconv_maps,
+ &partition_tuple_slot,
+ &num_parted, &num_partitions);
+ mtstate->mt_partition_dispatch_info = partition_dispatch_info;
+ mtstate->mt_num_dispatch = num_parted;
+ mtstate->mt_partitions = partitions;
+ mtstate->mt_num_partitions = num_partitions;
+ mtstate->mt_partition_tupconv_maps = partition_tupconv_maps;
+ mtstate->mt_partition_tuple_slot = partition_tuple_slot;
+ }
+
/*
* Initialize any WITH CHECK OPTION constraints if needed.
*/
@@ -1656,7 +1804,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
foreach(ll, wcoList)
{
WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
- ExprState *wcoExpr = ExecInitExpr((Expr *) wco->qual,
+ ExprState *wcoExpr = ExecInitQual((List *) wco->qual,
mtstate->mt_plans[i]);
wcoExprs = lappend(wcoExprs, wcoExpr);
@@ -1669,12 +1817,53 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
}
/*
+ * Build WITH CHECK OPTION constraints for each leaf partition rel. Note
+ * that we didn't build the withCheckOptionList for each partition within
+ * the planner, but simple translation of the varattnos for each partition
+ * will suffice. This only occurs for the INSERT case; UPDATE/DELETE
+ * cases are handled above.
+ */
+ if (node->withCheckOptionLists != NIL && mtstate->mt_num_partitions > 0)
+ {
+ List *wcoList;
+
+ Assert(operation == CMD_INSERT);
+ resultRelInfo = mtstate->mt_partitions;
+ wcoList = linitial(node->withCheckOptionLists);
+ for (i = 0; i < mtstate->mt_num_partitions; i++)
+ {
+ Relation partrel = resultRelInfo->ri_RelationDesc;
+ List *mapped_wcoList;
+ List *wcoExprs = NIL;
+ ListCell *ll;
+
+ /* varno = node->nominalRelation */
+ mapped_wcoList = map_partition_varattnos(wcoList,
+ node->nominalRelation,
+ partrel, rel);
+ foreach(ll, mapped_wcoList)
+ {
+ WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
+ ExprState *wcoExpr = ExecInitQual((List *) wco->qual,
+ mtstate->mt_plans[i]);
+
+ wcoExprs = lappend(wcoExprs, wcoExpr);
+ }
+
+ resultRelInfo->ri_WithCheckOptions = mapped_wcoList;
+ resultRelInfo->ri_WithCheckOptionExprs = wcoExprs;
+ resultRelInfo++;
+ }
+ }
+
+ /*
* Initialize RETURNING projections if needed.
*/
if (node->returningLists)
{
TupleTableSlot *slot;
ExprContext *econtext;
+ List *returningList;
/*
* Initialize result tuple slot and assign its rowtype using the first
@@ -1689,8 +1878,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
slot = mtstate->ps.ps_ResultTupleSlot;
/* Need an econtext too */
- econtext = CreateExprContext(estate);
- mtstate->ps.ps_ExprContext = econtext;
+ if (mtstate->ps.ps_ExprContext == NULL)
+ ExecAssignExprContext(estate, &mtstate->ps);
+ econtext = mtstate->ps.ps_ExprContext;
/*
* Build a projection for each result rel.
@@ -1699,11 +1889,33 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
foreach(l, node->returningLists)
{
List *rlist = (List *) lfirst(l);
- List *rliststate;
- rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
resultRelInfo->ri_projectReturning =
- ExecBuildProjectionInfo(rliststate, econtext, slot,
+ ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
+ resultRelInfo->ri_RelationDesc->rd_att);
+ resultRelInfo++;
+ }
+
+ /*
+ * Build a projection for each leaf partition rel. Note that we
+ * didn't build the returningList for each partition within the
+ * planner, but simple translation of the varattnos for each partition
+ * will suffice. This only occurs for the INSERT case; UPDATE/DELETE
+ * are handled above.
+ */
+ resultRelInfo = mtstate->mt_partitions;
+ returningList = linitial(node->returningLists);
+ for (i = 0; i < mtstate->mt_num_partitions; i++)
+ {
+ Relation partrel = resultRelInfo->ri_RelationDesc;
+ List *rlist;
+
+ /* varno = node->nominalRelation */
+ rlist = map_partition_varattnos(returningList,
+ node->nominalRelation,
+ partrel, rel);
+ resultRelInfo->ri_projectReturning =
+ ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
resultRelInfo->ri_RelationDesc->rd_att);
resultRelInfo++;
}
@@ -1721,6 +1933,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
mtstate->ps.ps_ExprContext = NULL;
}
+ /* Close the root partitioned rel if we opened it above. */
+ if (rel != mtstate->resultRelInfo->ri_RelationDesc)
+ heap_close(rel, NoLock);
+
/*
* If needed, Initialize target list, projection and qual for ON CONFLICT
* DO UPDATE.
@@ -1729,7 +1945,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
if (node->onConflictAction == ONCONFLICT_UPDATE)
{
ExprContext *econtext;
- ExprState *setexpr;
TupleDesc tupDesc;
/* insert may only have one plan, inheritance is not expanded */
@@ -1755,11 +1970,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state);
ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc);
- /* build UPDATE SET expression and projection state */
- setexpr = ExecInitExpr((Expr *) node->onConflictSet, &mtstate->ps);
+ /* build UPDATE SET projection state */
resultRelInfo->ri_onConflictSetProj =
- ExecBuildProjectionInfo((List *) setexpr, econtext,
- mtstate->mt_conflproj,
+ ExecBuildProjectionInfo(node->onConflictSet, econtext,
+ mtstate->mt_conflproj, &mtstate->ps,
resultRelInfo->ri_RelationDesc->rd_att);
/* build DO UPDATE WHERE clause expression */
@@ -1767,10 +1981,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
{
ExprState *qualexpr;
- qualexpr = ExecInitExpr((Expr *) node->onConflictWhere,
+ qualexpr = ExecInitQual((List *) node->onConflictWhere,
&mtstate->ps);
- resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr;
+ resultRelInfo->ri_onConflictSetWhere = qualexpr;
}
}
@@ -1782,11 +1996,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
*/
foreach(l, node->rowMarks)
{
- PlanRowMark *rc = (PlanRowMark *) lfirst(l);
+ PlanRowMark *rc = lfirst_node(PlanRowMark, l);
ExecRowMark *erm;
- Assert(IsA(rc, PlanRowMark));
-
/* ignore "parent" rowmarks; they are irrelevant at runtime */
if (rc->isParent)
continue;
@@ -1873,7 +2085,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
if (relkind == RELKIND_RELATION ||
- relkind == RELKIND_MATVIEW)
+ relkind == RELKIND_MATVIEW ||
+ relkind == RELKIND_PARTITIONED_TABLE)
{
j->jf_junkAttNo = ExecFindJunkAttribute(j, "ctid");
if (!AttributeNumberIsValid(j->jf_junkAttNo))
@@ -1959,6 +2172,33 @@ ExecEndModifyTable(ModifyTableState *node)
}
/*
+ * Close all the partitioned tables, leaf partitions, and their indices
+ *
+ * Remember node->mt_partition_dispatch_info[0] corresponds to the root
+ * partitioned table, which we must not try to close, because it is the
+ * main target table of the query that will be closed by ExecEndPlan().
+ * Also, tupslot is NULL for the root partitioned table.
+ */
+ for (i = 1; i < node->mt_num_dispatch; i++)
+ {
+ PartitionDispatch pd = node->mt_partition_dispatch_info[i];
+
+ heap_close(pd->reldesc, NoLock);
+ ExecDropSingleTupleTableSlot(pd->tupslot);
+ }
+ for (i = 0; i < node->mt_num_partitions; i++)
+ {
+ ResultRelInfo *resultRelInfo = node->mt_partitions + i;
+
+ ExecCloseIndices(resultRelInfo);
+ heap_close(resultRelInfo->ri_RelationDesc, NoLock);
+ }
+
+ /* Release the standalone partition tuple descriptor, if any */
+ if (node->mt_partition_tuple_slot)
+ ExecDropSingleTupleTableSlot(node->mt_partition_tuple_slot);
+
+ /*
* Free the exprcontext
*/
ExecFreeExprContext(&node->ps);