#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"
Relation resultRelationDesc;
Oid newId;
List *recheckIndexes = NIL;
- TupleTableSlot *oldslot = slot,
- *result = NULL;
+ TupleTableSlot *result = NULL;
/*
* get the heap tuple out of the tuple table slot, making sure we have a
}
else
{
+ /*
+ * We always check the partition constraint, including when the tuple
+ * got here via tuple-routing. However we don't need to in the latter
+ * case if no BR trigger is defined on the partition. Note that a BR
+ * trigger might modify the tuple such that the partition constraint
+ * is no longer satisfied, so we need to check in that case.
+ */
+ bool check_partition_constr =
+ (resultRelInfo->ri_PartitionCheck != NIL);
+
/*
* Constraints might reference the tableoid column, so initialize
* t_tableOid before evaluating them.
resultRelInfo, slot, estate);
/*
- * Check the constraints of the tuple
+ * No need though if the tuple has been routed, and a BR trigger
+ * doesn't exist.
*/
- if (resultRelationDesc->rd_att->constr || resultRelInfo->ri_PartitionCheck)
- ExecConstraints(resultRelInfo, slot, oldslot, estate);
+ if (saved_resultRelInfo != NULL &&
+ !(resultRelInfo->ri_TrigDesc &&
+ resultRelInfo->ri_TrigDesc->trig_insert_before_row))
+ check_partition_constr = false;
+
+ /* Check the constraints of the tuple */
+ if (resultRelationDesc->rd_att->constr || check_partition_constr)
+ ExecConstraints(resultRelInfo, slot, estate);
if (onconflict != ONCONFLICT_NONE && resultRelInfo->ri_NumIndices > 0)
{
* tuple-routing is performed here, hence the slot remains unchanged.
*/
if (resultRelationDesc->rd_att->constr || resultRelInfo->ri_PartitionCheck)
- ExecConstraints(resultRelInfo, slot, slot, estate);
+ ExecConstraints(resultRelInfo, slot, estate);
/*
* replace the heap tuple
{
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;
econtext->ecxt_innertuple = excludedSlot;
econtext->ecxt_outertuple = NULL;
- if (!ExecQual(onConflictSetWhere, econtext, false))
+ if (!ExecQual(onConflictSetWhere, econtext))
{
ReleaseBuffer(buffer);
InstrCountFiltered1(&mtstate->ps, 1);
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");
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");
elog(ERROR, "ctid is NULL");
tupleid = (ItemPointer) DatumGetPointer(datum);
- tuple_ctid = *tupleid; /* be sure we don't free
- * ctid!! */
+ tuple_ctid = *tupleid; /* be sure we don't free ctid!! */
tupleid = &tuple_ctid;
}
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;
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;
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 */
- rel = mtstate->resultRelInfo->ri_RelationDesc;
if (operation == CMD_INSERT &&
rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
{
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);
}
/*
- * 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.
+ * 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;
+ List *wcoList;
+ PlanState *plan;
- Assert(operation == CMD_INSERT);
- resultRelInfo = mtstate->mt_partitions;
+ /*
+ * In case of INSERT on partitioned tables, there is only one plan.
+ * Likewise, there is only one WITH CHECK OPTIONS list, not one per
+ * partition. We make a copy of the WCO qual for each partition; note
+ * that, if there are SubPlans in there, they all end up attached to
+ * the one parent Plan node.
+ */
+ Assert(operation == CMD_INSERT &&
+ list_length(node->withCheckOptionLists) == 1 &&
+ mtstate->mt_nplans == 1);
wcoList = linitial(node->withCheckOptionLists);
+ plan = mtstate->mt_plans[0];
+ resultRelInfo = mtstate->mt_partitions;
for (i = 0; i < mtstate->mt_num_partitions; i++)
{
Relation partrel = resultRelInfo->ri_RelationDesc;
partrel, rel);
foreach(ll, mapped_wcoList)
{
- WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
- ExprState *wcoExpr = ExecInitExpr((Expr *) wco->qual,
- mtstate->mt_plans[i]);
+ WithCheckOption *wco = castNode(WithCheckOption, lfirst(ll));
+ ExprState *wcoExpr = ExecInitQual(castNode(List, wco->qual),
+ plan);
wcoExprs = lappend(wcoExprs, wcoExpr);
}
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.
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++;
}
for (i = 0; i < mtstate->mt_num_partitions; i++)
{
Relation partrel = resultRelInfo->ri_RelationDesc;
- List *rlist,
- *rliststate;
+ List *rlist;
/* varno = node->nominalRelation */
rlist = map_partition_varattnos(returningList,
node->nominalRelation,
partrel, rel);
- 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++;
}
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.
if (node->onConflictAction == ONCONFLICT_UPDATE)
{
ExprContext *econtext;
- ExprState *setexpr;
TupleDesc tupDesc;
/* insert may only have one plan, inheritance is not expanded */
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 */
{
ExprState *qualexpr;
- qualexpr = ExecInitExpr((Expr *) node->onConflictWhere,
+ qualexpr = ExecInitQual((List *) node->onConflictWhere,
&mtstate->ps);
- resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr;
+ resultRelInfo->ri_onConflictSetWhere = qualexpr;
}
}
*/
foreach(l, node->rowMarks)
{
- PlanRowMark *rc = castNode(PlanRowMark, lfirst(l));
+ PlanRowMark *rc = lfirst_node(PlanRowMark, l);
ExecRowMark *erm;
/* ignore "parent" rowmarks; they are irrelevant at runtime */