mirror of
https://github.com/postgres/postgres.git
synced 2025-06-30 21:42:05 +03:00
Modified files for MERGE
This commit is contained in:
@ -946,6 +946,9 @@ ExplainNode(PlanState *planstate, List *ancestors,
|
||||
case CMD_DELETE:
|
||||
pname = operation = "Delete";
|
||||
break;
|
||||
case CMD_MERGE:
|
||||
pname = operation = "Merge";
|
||||
break;
|
||||
default:
|
||||
pname = "???";
|
||||
break;
|
||||
@ -3007,6 +3010,10 @@ show_modifytable_info(ModifyTableState *mtstate, List *ancestors,
|
||||
operation = "Delete";
|
||||
foperation = "Foreign Delete";
|
||||
break;
|
||||
case CMD_MERGE:
|
||||
operation = "Merge";
|
||||
foperation = "Foreign Merge";
|
||||
break;
|
||||
default:
|
||||
operation = "???";
|
||||
foperation = "Foreign ???";
|
||||
@ -3129,6 +3136,32 @@ show_modifytable_info(ModifyTableState *mtstate, List *ancestors,
|
||||
other_path, 0, es);
|
||||
}
|
||||
}
|
||||
else if (node->operation == CMD_MERGE)
|
||||
{
|
||||
/* EXPLAIN ANALYZE display of actual outcome for each tuple proposed */
|
||||
if (es->analyze && mtstate->ps.instrument)
|
||||
{
|
||||
double total;
|
||||
double insert_path;
|
||||
double update_path;
|
||||
double delete_path;
|
||||
double skipped_path;
|
||||
|
||||
InstrEndLoop(mtstate->mt_plans[0]->instrument);
|
||||
|
||||
/* count the number of source rows */
|
||||
total = mtstate->mt_plans[0]->instrument->ntuples;
|
||||
insert_path = mtstate->ps.instrument->nfiltered1;
|
||||
update_path = mtstate->ps.instrument->nfiltered2;
|
||||
delete_path = mtstate->ps.instrument->nfiltered3;
|
||||
skipped_path = total - insert_path - update_path - delete_path;
|
||||
|
||||
ExplainPropertyFloat("Tuples Inserted", NULL, insert_path, 0, es);
|
||||
ExplainPropertyFloat("Tuples Updated", NULL, update_path, 0, es);
|
||||
ExplainPropertyFloat("Tuples Deleted", NULL, delete_path, 0, es);
|
||||
ExplainPropertyFloat("Tuples Skipped", NULL, skipped_path, 0, es);
|
||||
}
|
||||
}
|
||||
|
||||
if (labeltargets)
|
||||
ExplainCloseGroup("Target Tables", "Target Tables", false, es);
|
||||
|
@ -151,6 +151,7 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString,
|
||||
case CMD_INSERT:
|
||||
case CMD_UPDATE:
|
||||
case CMD_DELETE:
|
||||
case CMD_MERGE:
|
||||
/* OK */
|
||||
break;
|
||||
default:
|
||||
|
@ -85,7 +85,8 @@ static HeapTuple GetTupleForTrigger(EState *estate,
|
||||
ResultRelInfo *relinfo,
|
||||
ItemPointer tid,
|
||||
LockTupleMode lockmode,
|
||||
TupleTableSlot **newSlot);
|
||||
TupleTableSlot **newSlot,
|
||||
HeapUpdateFailureData *hufdp);
|
||||
static bool TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
|
||||
Trigger *trigger, TriggerEvent event,
|
||||
Bitmapset *modifiedCols,
|
||||
@ -2729,7 +2730,8 @@ bool
|
||||
ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
|
||||
ResultRelInfo *relinfo,
|
||||
ItemPointer tupleid,
|
||||
HeapTuple fdw_trigtuple)
|
||||
HeapTuple fdw_trigtuple,
|
||||
HeapUpdateFailureData *hufdp)
|
||||
{
|
||||
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
|
||||
bool result = true;
|
||||
@ -2743,7 +2745,7 @@ ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
|
||||
if (fdw_trigtuple == NULL)
|
||||
{
|
||||
trigtuple = GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
|
||||
LockTupleExclusive, &newSlot);
|
||||
LockTupleExclusive, &newSlot, hufdp);
|
||||
if (trigtuple == NULL)
|
||||
return false;
|
||||
}
|
||||
@ -2814,6 +2816,7 @@ ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
|
||||
relinfo,
|
||||
tupleid,
|
||||
LockTupleExclusive,
|
||||
NULL,
|
||||
NULL);
|
||||
else
|
||||
trigtuple = fdw_trigtuple;
|
||||
@ -2951,7 +2954,8 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
|
||||
ResultRelInfo *relinfo,
|
||||
ItemPointer tupleid,
|
||||
HeapTuple fdw_trigtuple,
|
||||
TupleTableSlot *slot)
|
||||
TupleTableSlot *slot,
|
||||
HeapUpdateFailureData *hufdp)
|
||||
{
|
||||
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
|
||||
HeapTuple slottuple = ExecMaterializeSlot(slot);
|
||||
@ -2972,7 +2976,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
|
||||
{
|
||||
/* get a copy of the on-disk tuple we are planning to update */
|
||||
trigtuple = GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
|
||||
lockmode, &newSlot);
|
||||
lockmode, &newSlot, hufdp);
|
||||
if (trigtuple == NULL)
|
||||
return NULL; /* cancel the update action */
|
||||
}
|
||||
@ -3092,6 +3096,7 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
|
||||
relinfo,
|
||||
tupleid,
|
||||
LockTupleExclusive,
|
||||
NULL,
|
||||
NULL);
|
||||
else
|
||||
trigtuple = fdw_trigtuple;
|
||||
@ -3240,7 +3245,8 @@ GetTupleForTrigger(EState *estate,
|
||||
ResultRelInfo *relinfo,
|
||||
ItemPointer tid,
|
||||
LockTupleMode lockmode,
|
||||
TupleTableSlot **newSlot)
|
||||
TupleTableSlot **newSlot,
|
||||
HeapUpdateFailureData *hufdp)
|
||||
{
|
||||
Relation relation = relinfo->ri_RelationDesc;
|
||||
HeapTupleData tuple;
|
||||
@ -3266,6 +3272,11 @@ ltrmark:;
|
||||
estate->es_output_cid,
|
||||
lockmode, LockWaitBlock,
|
||||
false, &buffer, &hufd);
|
||||
|
||||
/* Let the caller know about failure reason, if any. */
|
||||
if (hufdp)
|
||||
*hufdp = hufd;
|
||||
|
||||
switch (test)
|
||||
{
|
||||
case HeapTupleSelfUpdated:
|
||||
@ -3302,10 +3313,17 @@ ltrmark:;
|
||||
/* it was updated, so look at the updated version */
|
||||
TupleTableSlot *epqslot;
|
||||
|
||||
/*
|
||||
* If we're running MERGE then we must install the
|
||||
* new tuple in the slot of the underlying join query and
|
||||
* not the result relation itself. If the join does not
|
||||
* yield any tuple, the caller will take the necessary
|
||||
* action.
|
||||
*/
|
||||
epqslot = EvalPlanQual(estate,
|
||||
epqstate,
|
||||
relation,
|
||||
relinfo->ri_RangeTableIndex,
|
||||
GetEPQRangeTableIndex(relinfo),
|
||||
lockmode,
|
||||
&hufd.ctid,
|
||||
hufd.xmax);
|
||||
@ -3828,8 +3846,14 @@ struct AfterTriggersTableData
|
||||
bool before_trig_done; /* did we already queue BS triggers? */
|
||||
bool after_trig_done; /* did we already queue AS triggers? */
|
||||
AfterTriggerEventList after_trig_events; /* if so, saved list pointer */
|
||||
Tuplestorestate *old_tuplestore; /* "old" transition table, if any */
|
||||
Tuplestorestate *new_tuplestore; /* "new" transition table, if any */
|
||||
/* "old" transition table for UPDATE, if any */
|
||||
Tuplestorestate *old_upd_tuplestore;
|
||||
/* "new" transition table for UPDATE, if any */
|
||||
Tuplestorestate *new_upd_tuplestore;
|
||||
/* "old" transition table for DELETE, if any */
|
||||
Tuplestorestate *old_del_tuplestore;
|
||||
/* "new" transition table INSERT, if any */
|
||||
Tuplestorestate *new_ins_tuplestore;
|
||||
};
|
||||
|
||||
static AfterTriggersData afterTriggers;
|
||||
@ -4296,13 +4320,19 @@ AfterTriggerExecute(AfterTriggerEvent event,
|
||||
{
|
||||
if (LocTriggerData.tg_trigger->tgoldtable)
|
||||
{
|
||||
LocTriggerData.tg_oldtable = evtshared->ats_table->old_tuplestore;
|
||||
if (TRIGGER_FIRED_BY_UPDATE(evtshared->ats_event))
|
||||
LocTriggerData.tg_oldtable = evtshared->ats_table->old_upd_tuplestore;
|
||||
else
|
||||
LocTriggerData.tg_oldtable = evtshared->ats_table->old_del_tuplestore;
|
||||
evtshared->ats_table->closed = true;
|
||||
}
|
||||
|
||||
if (LocTriggerData.tg_trigger->tgnewtable)
|
||||
{
|
||||
LocTriggerData.tg_newtable = evtshared->ats_table->new_tuplestore;
|
||||
if (TRIGGER_FIRED_BY_INSERT(evtshared->ats_event))
|
||||
LocTriggerData.tg_newtable = evtshared->ats_table->new_ins_tuplestore;
|
||||
else
|
||||
LocTriggerData.tg_newtable = evtshared->ats_table->new_upd_tuplestore;
|
||||
evtshared->ats_table->closed = true;
|
||||
}
|
||||
}
|
||||
@ -4637,8 +4667,10 @@ TransitionCaptureState *
|
||||
MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
|
||||
{
|
||||
TransitionCaptureState *state;
|
||||
bool need_old,
|
||||
need_new;
|
||||
bool need_old_upd,
|
||||
need_new_upd,
|
||||
need_old_del,
|
||||
need_new_ins;
|
||||
AfterTriggersTableData *table;
|
||||
MemoryContext oldcxt;
|
||||
ResourceOwner saveResourceOwner;
|
||||
@ -4650,23 +4682,31 @@ MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
|
||||
switch (cmdType)
|
||||
{
|
||||
case CMD_INSERT:
|
||||
need_old = false;
|
||||
need_new = trigdesc->trig_insert_new_table;
|
||||
need_old_upd = need_old_del = need_new_upd = false;
|
||||
need_new_ins = trigdesc->trig_insert_new_table;
|
||||
break;
|
||||
case CMD_UPDATE:
|
||||
need_old = trigdesc->trig_update_old_table;
|
||||
need_new = trigdesc->trig_update_new_table;
|
||||
need_old_upd = trigdesc->trig_update_old_table;
|
||||
need_new_upd = trigdesc->trig_update_new_table;
|
||||
need_old_del = need_new_ins = false;
|
||||
break;
|
||||
case CMD_DELETE:
|
||||
need_old = trigdesc->trig_delete_old_table;
|
||||
need_new = false;
|
||||
need_old_del = trigdesc->trig_delete_old_table;
|
||||
need_old_upd = need_new_upd = need_new_ins = false;
|
||||
break;
|
||||
case CMD_MERGE:
|
||||
need_old_upd = trigdesc->trig_update_old_table;
|
||||
need_new_upd = trigdesc->trig_update_new_table;
|
||||
need_old_del = trigdesc->trig_delete_old_table;
|
||||
need_new_ins = trigdesc->trig_insert_new_table;
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unexpected CmdType: %d", (int) cmdType);
|
||||
need_old = need_new = false; /* keep compiler quiet */
|
||||
/* keep compiler quiet */
|
||||
need_old_upd = need_new_upd = need_old_del = need_new_ins = false;
|
||||
break;
|
||||
}
|
||||
if (!need_old && !need_new)
|
||||
if (!need_old_upd && !need_new_upd && !need_new_ins && !need_old_del)
|
||||
return NULL;
|
||||
|
||||
/* Check state, like AfterTriggerSaveEvent. */
|
||||
@ -4696,10 +4736,14 @@ MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
|
||||
saveResourceOwner = CurrentResourceOwner;
|
||||
CurrentResourceOwner = CurTransactionResourceOwner;
|
||||
|
||||
if (need_old && table->old_tuplestore == NULL)
|
||||
table->old_tuplestore = tuplestore_begin_heap(false, false, work_mem);
|
||||
if (need_new && table->new_tuplestore == NULL)
|
||||
table->new_tuplestore = tuplestore_begin_heap(false, false, work_mem);
|
||||
if (need_old_upd && table->old_upd_tuplestore == NULL)
|
||||
table->old_upd_tuplestore = tuplestore_begin_heap(false, false, work_mem);
|
||||
if (need_new_upd && table->new_upd_tuplestore == NULL)
|
||||
table->new_upd_tuplestore = tuplestore_begin_heap(false, false, work_mem);
|
||||
if (need_old_del && table->old_del_tuplestore == NULL)
|
||||
table->old_del_tuplestore = tuplestore_begin_heap(false, false, work_mem);
|
||||
if (need_new_ins && table->new_ins_tuplestore == NULL)
|
||||
table->new_ins_tuplestore = tuplestore_begin_heap(false, false, work_mem);
|
||||
|
||||
CurrentResourceOwner = saveResourceOwner;
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
@ -4888,12 +4932,20 @@ AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
|
||||
{
|
||||
AfterTriggersTableData *table = (AfterTriggersTableData *) lfirst(lc);
|
||||
|
||||
ts = table->old_tuplestore;
|
||||
table->old_tuplestore = NULL;
|
||||
ts = table->old_upd_tuplestore;
|
||||
table->old_upd_tuplestore = NULL;
|
||||
if (ts)
|
||||
tuplestore_end(ts);
|
||||
ts = table->new_tuplestore;
|
||||
table->new_tuplestore = NULL;
|
||||
ts = table->new_upd_tuplestore;
|
||||
table->new_upd_tuplestore = NULL;
|
||||
if (ts)
|
||||
tuplestore_end(ts);
|
||||
ts = table->old_del_tuplestore;
|
||||
table->old_del_tuplestore = NULL;
|
||||
if (ts)
|
||||
tuplestore_end(ts);
|
||||
ts = table->new_ins_tuplestore;
|
||||
table->new_ins_tuplestore = NULL;
|
||||
if (ts)
|
||||
tuplestore_end(ts);
|
||||
}
|
||||
@ -5744,12 +5796,28 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
|
||||
newtup == NULL));
|
||||
|
||||
if (oldtup != NULL &&
|
||||
((event == TRIGGER_EVENT_DELETE && delete_old_table) ||
|
||||
(event == TRIGGER_EVENT_UPDATE && update_old_table)))
|
||||
(event == TRIGGER_EVENT_DELETE && delete_old_table))
|
||||
{
|
||||
Tuplestorestate *old_tuplestore;
|
||||
|
||||
old_tuplestore = transition_capture->tcs_private->old_tuplestore;
|
||||
old_tuplestore = transition_capture->tcs_private->old_del_tuplestore;
|
||||
|
||||
if (map != NULL)
|
||||
{
|
||||
HeapTuple converted = do_convert_tuple(oldtup, map);
|
||||
|
||||
tuplestore_puttuple(old_tuplestore, converted);
|
||||
pfree(converted);
|
||||
}
|
||||
else
|
||||
tuplestore_puttuple(old_tuplestore, oldtup);
|
||||
}
|
||||
if (oldtup != NULL &&
|
||||
(event == TRIGGER_EVENT_UPDATE && update_old_table))
|
||||
{
|
||||
Tuplestorestate *old_tuplestore;
|
||||
|
||||
old_tuplestore = transition_capture->tcs_private->old_upd_tuplestore;
|
||||
|
||||
if (map != NULL)
|
||||
{
|
||||
@ -5762,12 +5830,30 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
|
||||
tuplestore_puttuple(old_tuplestore, oldtup);
|
||||
}
|
||||
if (newtup != NULL &&
|
||||
((event == TRIGGER_EVENT_INSERT && insert_new_table) ||
|
||||
(event == TRIGGER_EVENT_UPDATE && update_new_table)))
|
||||
(event == TRIGGER_EVENT_INSERT && insert_new_table))
|
||||
{
|
||||
Tuplestorestate *new_tuplestore;
|
||||
|
||||
new_tuplestore = transition_capture->tcs_private->new_tuplestore;
|
||||
new_tuplestore = transition_capture->tcs_private->new_ins_tuplestore;
|
||||
|
||||
if (original_insert_tuple != NULL)
|
||||
tuplestore_puttuple(new_tuplestore, original_insert_tuple);
|
||||
else if (map != NULL)
|
||||
{
|
||||
HeapTuple converted = do_convert_tuple(newtup, map);
|
||||
|
||||
tuplestore_puttuple(new_tuplestore, converted);
|
||||
pfree(converted);
|
||||
}
|
||||
else
|
||||
tuplestore_puttuple(new_tuplestore, newtup);
|
||||
}
|
||||
if (newtup != NULL &&
|
||||
(event == TRIGGER_EVENT_UPDATE && update_new_table))
|
||||
{
|
||||
Tuplestorestate *new_tuplestore;
|
||||
|
||||
new_tuplestore = transition_capture->tcs_private->new_upd_tuplestore;
|
||||
|
||||
if (original_insert_tuple != NULL)
|
||||
tuplestore_puttuple(new_tuplestore, original_insert_tuple);
|
||||
|
Reference in New Issue
Block a user