ResultRelInfo *resultRelInfo;
EState *estate = CreateExecutorState(); /* for ExecConstraints() */
ExprContext *econtext;
- TupleTableSlot *slot;
+ TupleTableSlot *myslot;
MemoryContext oldcontext = CurrentMemoryContext;
ErrorContextCallback errcontext;
CommandId mycid = GetCurrentCommandId(true);
estate->es_result_relation_info = resultRelInfo;
/* Set up a tuple slot too */
- slot = ExecInitExtraTupleSlot(estate);
- ExecSetSlotDescriptor(slot, tupDesc);
+ myslot = ExecInitExtraTupleSlot(estate);
+ ExecSetSlotDescriptor(myslot, tupDesc);
+ /* Triggers might need a slot as well */
+ estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate);
/* Prepare to catch AFTER triggers. */
AfterTriggerBeginQuery();
for (;;)
{
+ TupleTableSlot *slot;
bool skip_tuple;
Oid loaded_oid = InvalidOid;
/* Triggers and stuff need to be invoked in query context. */
MemoryContextSwitchTo(oldcontext);
+ /* Place tuple in tuple slot --- but slot shouldn't free it */
+ slot = myslot;
+ ExecStoreTuple(tuple, slot, InvalidBuffer, false);
+
skip_tuple = false;
/* BEFORE ROW INSERT Triggers */
if (resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_insert_before_row)
{
- HeapTuple newtuple;
-
- newtuple = ExecBRInsertTriggers(estate, resultRelInfo, tuple);
+ slot = ExecBRInsertTriggers(estate, resultRelInfo, slot);
- if (newtuple == NULL) /* "do nothing" */
+ if (slot == NULL) /* "do nothing" */
skip_tuple = true;
- else if (newtuple != tuple) /* modified by Trigger(s) */
- {
- heap_freetuple(tuple);
- tuple = newtuple;
- }
+ else /* trigger might have changed tuple */
+ tuple = ExecMaterializeSlot(slot);
}
if (!skip_tuple)
{
List *recheckIndexes = NIL;
- /* Place tuple in tuple slot */
- ExecStoreTuple(tuple, slot, InvalidBuffer, false);
-
/* Check the constraints of the tuple */
if (cstate->rel->rd_att->constr)
ExecConstraints(resultRelInfo, slot, estate);
false, NULL, NULL, NIL, NULL);
}
-HeapTuple
+TupleTableSlot *
ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
- HeapTuple trigtuple)
+ TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple newtuple = trigtuple;
+ HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple newtuple = slottuple;
HeapTuple oldtuple;
TriggerData LocTriggerData;
int i;
relinfo->ri_TrigFunctions,
relinfo->ri_TrigInstrument,
GetPerTupleMemoryContext(estate));
- if (oldtuple != newtuple && oldtuple != trigtuple)
+ if (oldtuple != newtuple && oldtuple != slottuple)
heap_freetuple(oldtuple);
if (newtuple == NULL)
- break;
+ return NULL; /* "do nothing" */
+ }
+
+ if (newtuple != slottuple)
+ {
+ /*
+ * Return the modified tuple using the es_trig_tuple_slot. We assume
+ * the tuple was allocated in per-tuple memory context, and therefore
+ * will go away by itself. The tuple table slot should not try to
+ * clear it.
+ */
+ TupleTableSlot *newslot = estate->es_trig_tuple_slot;
+ TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
+
+ if (newslot->tts_tupleDescriptor != tupdesc)
+ ExecSetSlotDescriptor(newslot, tupdesc);
+ ExecStoreTuple(newtuple, newslot, InvalidBuffer, false);
+ slot = newslot;
}
- return newtuple;
+ return slot;
}
void
true, NULL, trigtuple, recheckIndexes, NULL);
}
-HeapTuple
+TupleTableSlot *
ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
- HeapTuple trigtuple)
+ TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple newtuple = trigtuple;
+ HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple newtuple = slottuple;
HeapTuple oldtuple;
TriggerData LocTriggerData;
int i;
relinfo->ri_TrigFunctions,
relinfo->ri_TrigInstrument,
GetPerTupleMemoryContext(estate));
- if (oldtuple != newtuple && oldtuple != trigtuple)
+ if (oldtuple != newtuple && oldtuple != slottuple)
heap_freetuple(oldtuple);
if (newtuple == NULL)
- break;
+ return NULL; /* "do nothing" */
+ }
+
+ if (newtuple != slottuple)
+ {
+ /*
+ * Return the modified tuple using the es_trig_tuple_slot. We assume
+ * the tuple was allocated in per-tuple memory context, and therefore
+ * will go away by itself. The tuple table slot should not try to
+ * clear it.
+ */
+ TupleTableSlot *newslot = estate->es_trig_tuple_slot;
+ TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
+
+ if (newslot->tts_tupleDescriptor != tupdesc)
+ ExecSetSlotDescriptor(newslot, tupdesc);
+ ExecStoreTuple(newtuple, newslot, InvalidBuffer, false);
+ slot = newslot;
}
- return newtuple;
+ return slot;
}
void
GetModifiedColumns(relinfo, estate));
}
-HeapTuple
+TupleTableSlot *
ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
ResultRelInfo *relinfo,
- ItemPointer tupleid, HeapTuple newtuple)
+ ItemPointer tupleid, TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
+ HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple newtuple = slottuple;
TriggerData LocTriggerData;
HeapTuple trigtuple;
HeapTuple oldtuple;
- HeapTuple intuple = newtuple;
TupleTableSlot *newSlot;
int i;
Bitmapset *modifiedCols;
+ /* get a copy of the on-disk tuple we are planning to update */
trigtuple = GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
&newSlot);
if (trigtuple == NULL)
- return NULL;
+ return NULL; /* cancel the update action */
/*
- * In READ COMMITTED isolation level it's possible that newtuple was
+ * In READ COMMITTED isolation level it's possible that target tuple was
* changed due to concurrent update. In that case we have a raw subplan
- * output tuple and need to run it through the junk filter.
+ * output tuple in newSlot, and need to run it through the junk filter to
+ * produce an insertable tuple.
+ *
+ * Caution: more than likely, the passed-in slot is the same as the
+ * junkfilter's output slot, so we are clobbering the original value of
+ * slottuple by doing the filtering. This is OK since neither we nor our
+ * caller have any more interest in the prior contents of that slot.
*/
if (newSlot != NULL)
- intuple = newtuple = ExecRemoveJunk(relinfo->ri_junkFilter, newSlot);
+ {
+ slot = ExecFilterJunk(relinfo->ri_junkFilter, newSlot);
+ slottuple = ExecMaterializeSlot(slot);
+ newtuple = slottuple;
+ }
modifiedCols = GetModifiedColumns(relinfo, estate);
relinfo->ri_TrigFunctions,
relinfo->ri_TrigInstrument,
GetPerTupleMemoryContext(estate));
- if (oldtuple != newtuple && oldtuple != intuple)
+ if (oldtuple != newtuple && oldtuple != slottuple)
heap_freetuple(oldtuple);
if (newtuple == NULL)
- break;
+ {
+ heap_freetuple(trigtuple);
+ return NULL; /* "do nothing" */
+ }
}
heap_freetuple(trigtuple);
- return newtuple;
+
+ if (newtuple != slottuple)
+ {
+ /*
+ * Return the modified tuple using the es_trig_tuple_slot. We assume
+ * the tuple was allocated in per-tuple memory context, and therefore
+ * will go away by itself. The tuple table slot should not try to
+ * clear it.
+ */
+ TupleTableSlot *newslot = estate->es_trig_tuple_slot;
+ TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
+
+ if (newslot->tts_tupleDescriptor != tupdesc)
+ ExecSetSlotDescriptor(newslot, tupdesc);
+ ExecStoreTuple(newtuple, newslot, InvalidBuffer, false);
+ slot = newslot;
+ }
+ return slot;
}
void
}
}
-HeapTuple
+TupleTableSlot *
ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
- HeapTuple oldtuple, HeapTuple newtuple)
+ HeapTuple trigtuple, TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
+ HeapTuple slottuple = ExecMaterializeSlot(slot);
+ HeapTuple newtuple = slottuple;
TriggerData LocTriggerData;
- HeapTuple intuple = newtuple;
- HeapTuple rettuple;
+ HeapTuple oldtuple;
int i;
LocTriggerData.type = T_TriggerData;
TRIGGER_TYPE_UPDATE))
continue;
if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
- NULL, oldtuple, newtuple))
+ NULL, trigtuple, newtuple))
continue;
- LocTriggerData.tg_trigtuple = oldtuple;
- LocTriggerData.tg_newtuple = newtuple;
+ LocTriggerData.tg_trigtuple = trigtuple;
+ LocTriggerData.tg_newtuple = oldtuple = newtuple;
LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
LocTriggerData.tg_newtuplebuf = InvalidBuffer;
LocTriggerData.tg_trigger = trigger;
- rettuple = ExecCallTriggerFunc(&LocTriggerData,
+ newtuple = ExecCallTriggerFunc(&LocTriggerData,
i,
relinfo->ri_TrigFunctions,
relinfo->ri_TrigInstrument,
GetPerTupleMemoryContext(estate));
- if (newtuple != rettuple && newtuple != intuple)
- heap_freetuple(newtuple);
- newtuple = rettuple;
+ if (oldtuple != newtuple && oldtuple != slottuple)
+ heap_freetuple(oldtuple);
if (newtuple == NULL)
- break;
+ return NULL; /* "do nothing" */
+ }
+
+ if (newtuple != slottuple)
+ {
+ /*
+ * Return the modified tuple using the es_trig_tuple_slot. We assume
+ * the tuple was allocated in per-tuple memory context, and therefore
+ * will go away by itself. The tuple table slot should not try to
+ * clear it.
+ */
+ TupleTableSlot *newslot = estate->es_trig_tuple_slot;
+ TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
+
+ if (newslot->tts_tupleDescriptor != tupdesc)
+ ExecSetSlotDescriptor(newslot, tupdesc);
+ ExecStoreTuple(newtuple, newslot, InvalidBuffer, false);
+ slot = newslot;
}
- return newtuple;
+ return slot;
}
void
if (resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_insert_before_row)
{
- HeapTuple newtuple;
+ slot = ExecBRInsertTriggers(estate, resultRelInfo, slot);
- newtuple = ExecBRInsertTriggers(estate, resultRelInfo, tuple);
-
- if (newtuple == NULL) /* "do nothing" */
+ if (slot == NULL) /* "do nothing" */
return NULL;
- if (newtuple != tuple) /* modified by Trigger(s) */
- {
- /*
- * Put the modified tuple into a slot for convenience of routines
- * below. We assume the tuple was allocated in per-tuple memory
- * context, and therefore will go away by itself. The tuple table
- * slot should not try to clear it.
- */
- TupleTableSlot *newslot = estate->es_trig_tuple_slot;
- TupleDesc tupdesc = RelationGetDescr(resultRelationDesc);
-
- if (newslot->tts_tupleDescriptor != tupdesc)
- ExecSetSlotDescriptor(newslot, tupdesc);
- ExecStoreTuple(newtuple, newslot, InvalidBuffer, false);
- slot = newslot;
- tuple = newtuple;
- }
+ /* trigger might have changed tuple */
+ tuple = ExecMaterializeSlot(slot);
}
/* INSTEAD OF ROW INSERT Triggers */
if (resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_insert_instead_row)
{
- HeapTuple newtuple;
+ slot = ExecIRInsertTriggers(estate, resultRelInfo, slot);
- newtuple = ExecIRInsertTriggers(estate, resultRelInfo, tuple);
-
- if (newtuple == NULL) /* "do nothing" */
+ if (slot == NULL) /* "do nothing" */
return NULL;
- if (newtuple != tuple) /* modified by Trigger(s) */
- {
- /*
- * Put the modified tuple into a slot for convenience of routines
- * below. We assume the tuple was allocated in per-tuple memory
- * context, and therefore will go away by itself. The tuple table
- * slot should not try to clear it.
- */
- TupleTableSlot *newslot = estate->es_trig_tuple_slot;
- TupleDesc tupdesc = RelationGetDescr(resultRelationDesc);
-
- if (newslot->tts_tupleDescriptor != tupdesc)
- ExecSetSlotDescriptor(newslot, tupdesc);
- ExecStoreTuple(newtuple, newslot, InvalidBuffer, false);
- slot = newslot;
- tuple = newtuple;
- }
+ /* trigger might have changed tuple */
+ tuple = ExecMaterializeSlot(slot);
newId = InvalidOid;
}
if (resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_update_before_row)
{
- HeapTuple newtuple;
+ slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
+ tupleid, slot);
- newtuple = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
- tupleid, tuple);
-
- if (newtuple == NULL) /* "do nothing" */
+ if (slot == NULL) /* "do nothing" */
return NULL;
- if (newtuple != tuple) /* modified by Trigger(s) */
- {
- /*
- * Put the modified tuple into a slot for convenience of routines
- * below. We assume the tuple was allocated in per-tuple memory
- * context, and therefore will go away by itself. The tuple table
- * slot should not try to clear it.
- */
- TupleTableSlot *newslot = estate->es_trig_tuple_slot;
- TupleDesc tupdesc = RelationGetDescr(resultRelationDesc);
-
- if (newslot->tts_tupleDescriptor != tupdesc)
- ExecSetSlotDescriptor(newslot, tupdesc);
- ExecStoreTuple(newtuple, newslot, InvalidBuffer, false);
- slot = newslot;
- tuple = newtuple;
- }
+ /* trigger might have changed tuple */
+ tuple = ExecMaterializeSlot(slot);
}
/* INSTEAD OF ROW UPDATE Triggers */
resultRelInfo->ri_TrigDesc->trig_update_instead_row)
{
HeapTupleData oldtup;
- HeapTuple newtuple;
Assert(oldtuple != NULL);
oldtup.t_data = oldtuple;
ItemPointerSetInvalid(&(oldtup.t_self));
oldtup.t_tableOid = InvalidOid;
- newtuple = ExecIRUpdateTriggers(estate, resultRelInfo,
- &oldtup, tuple);
+ slot = ExecIRUpdateTriggers(estate, resultRelInfo,
+ &oldtup, slot);
- if (newtuple == NULL) /* "do nothing" */
+ if (slot == NULL) /* "do nothing" */
return NULL;
- if (newtuple != tuple) /* modified by Trigger(s) */
- {
- /*
- * Put the modified tuple into a slot for convenience of routines
- * below. We assume the tuple was allocated in per-tuple memory
- * context, and therefore will go away by itself. The tuple table
- * slot should not try to clear it.
- */
- TupleTableSlot *newslot = estate->es_trig_tuple_slot;
- TupleDesc tupdesc = RelationGetDescr(resultRelationDesc);
-
- if (newslot->tts_tupleDescriptor != tupdesc)
- ExecSetSlotDescriptor(newslot, tupdesc);
- ExecStoreTuple(newtuple, newslot, InvalidBuffer, false);
- slot = newslot;
- tuple = newtuple;
- }
+ /* trigger might have changed tuple */
+ tuple = ExecMaterializeSlot(slot);
}
else
{