mirror of
https://github.com/postgres/postgres.git
synced 2025-06-30 21:42:05 +03:00
Revert MERGE patch
This reverts commitsd204ef6377
,83454e3c2b
and a few more commits thereafter (complete list at the end) related to MERGE feature. While the feature was fully functional, with sufficient test coverage and necessary documentation, it was felt that some parts of the executor and parse-analyzer can use a different design and it wasn't possible to do that in the available time. So it was decided to revert the patch for PG11 and retry again in the future. Thanks again to all reviewers and bug reporters. List of commits reverted, in reverse chronological order:f1464c5380
Improve parse representation for MERGEddb4158579
MERGE syntax diagram correction530e69e59b
Allow cpluspluscheck to pass by renaming variable01b88b4df5
MERGE minor errata3af7b2b0d4
MERGE fix variable warning in non-assert buildsa5d86181ec
MERGE INSERT allows only one VALUES clause4b2d44031f
MERGE post-commit review4923550c20
Tab completion for MERGEaa3faa3c7a
WITH support in MERGE83454e3c2b
New files for MERGEd204ef6377
MERGE SQL Command following SQL:2016 Author: Pavan Deolasee Reviewed-by: Michael Paquier
This commit is contained in:
@ -84,8 +84,7 @@ static HeapTuple GetTupleForTrigger(EState *estate,
|
||||
ResultRelInfo *relinfo,
|
||||
ItemPointer tid,
|
||||
LockTupleMode lockmode,
|
||||
TupleTableSlot **newSlot,
|
||||
HeapUpdateFailureData *hufdp);
|
||||
TupleTableSlot **newSlot);
|
||||
static bool TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
|
||||
Trigger *trigger, TriggerEvent event,
|
||||
Bitmapset *modifiedCols,
|
||||
@ -95,12 +94,6 @@ static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata,
|
||||
FmgrInfo *finfo,
|
||||
Instrumentation *instr,
|
||||
MemoryContext per_tuple_context);
|
||||
static Tuplestorestate *AfterTriggerGetTransitionTable(int event,
|
||||
HeapTuple oldtup,
|
||||
HeapTuple newtup,
|
||||
TransitionCaptureState *transition_capture);
|
||||
static void TransitionTableAddTuple(HeapTuple heaptup, Tuplestorestate *tuplestore,
|
||||
TupleConversionMap *map);
|
||||
static void AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
|
||||
int event, bool row_trigger,
|
||||
HeapTuple oldtup, HeapTuple newtup,
|
||||
@ -2736,8 +2729,7 @@ bool
|
||||
ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
|
||||
ResultRelInfo *relinfo,
|
||||
ItemPointer tupleid,
|
||||
HeapTuple fdw_trigtuple,
|
||||
HeapUpdateFailureData *hufdp)
|
||||
HeapTuple fdw_trigtuple)
|
||||
{
|
||||
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
|
||||
bool result = true;
|
||||
@ -2751,7 +2743,7 @@ ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
|
||||
if (fdw_trigtuple == NULL)
|
||||
{
|
||||
trigtuple = GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
|
||||
LockTupleExclusive, &newSlot, hufdp);
|
||||
LockTupleExclusive, &newSlot);
|
||||
if (trigtuple == NULL)
|
||||
return false;
|
||||
}
|
||||
@ -2822,7 +2814,6 @@ ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
|
||||
relinfo,
|
||||
tupleid,
|
||||
LockTupleExclusive,
|
||||
NULL,
|
||||
NULL);
|
||||
else
|
||||
trigtuple = fdw_trigtuple;
|
||||
@ -2960,8 +2951,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
|
||||
ResultRelInfo *relinfo,
|
||||
ItemPointer tupleid,
|
||||
HeapTuple fdw_trigtuple,
|
||||
TupleTableSlot *slot,
|
||||
HeapUpdateFailureData *hufdp)
|
||||
TupleTableSlot *slot)
|
||||
{
|
||||
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
|
||||
HeapTuple slottuple = ExecMaterializeSlot(slot);
|
||||
@ -2982,7 +2972,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, hufdp);
|
||||
lockmode, &newSlot);
|
||||
if (trigtuple == NULL)
|
||||
return NULL; /* cancel the update action */
|
||||
}
|
||||
@ -3102,7 +3092,6 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
|
||||
relinfo,
|
||||
tupleid,
|
||||
LockTupleExclusive,
|
||||
NULL,
|
||||
NULL);
|
||||
else
|
||||
trigtuple = fdw_trigtuple;
|
||||
@ -3251,8 +3240,7 @@ GetTupleForTrigger(EState *estate,
|
||||
ResultRelInfo *relinfo,
|
||||
ItemPointer tid,
|
||||
LockTupleMode lockmode,
|
||||
TupleTableSlot **newSlot,
|
||||
HeapUpdateFailureData *hufdp)
|
||||
TupleTableSlot **newSlot)
|
||||
{
|
||||
Relation relation = relinfo->ri_RelationDesc;
|
||||
HeapTupleData tuple;
|
||||
@ -3278,11 +3266,6 @@ 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:
|
||||
@ -3324,17 +3307,10 @@ 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,
|
||||
GetEPQRangeTableIndex(relinfo),
|
||||
relinfo->ri_RangeTableIndex,
|
||||
lockmode,
|
||||
&hufd.ctid,
|
||||
hufd.xmax);
|
||||
@ -3857,22 +3833,8 @@ 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 */
|
||||
|
||||
/*
|
||||
* We maintain separate transaction tables for UPDATE/INSERT/DELETE since
|
||||
* MERGE can run all three actions in a single statement. Note that UPDATE
|
||||
* needs both old and new transition tables whereas INSERT needs only new
|
||||
* and DELETE needs only old.
|
||||
*/
|
||||
|
||||
/* "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;
|
||||
Tuplestorestate *old_tuplestore; /* "old" transition table, if any */
|
||||
Tuplestorestate *new_tuplestore; /* "new" transition table, if any */
|
||||
};
|
||||
|
||||
static AfterTriggersData afterTriggers;
|
||||
@ -4339,19 +4301,13 @@ AfterTriggerExecute(AfterTriggerEvent event,
|
||||
{
|
||||
if (LocTriggerData.tg_trigger->tgoldtable)
|
||||
{
|
||||
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;
|
||||
LocTriggerData.tg_oldtable = evtshared->ats_table->old_tuplestore;
|
||||
evtshared->ats_table->closed = true;
|
||||
}
|
||||
|
||||
if (LocTriggerData.tg_trigger->tgnewtable)
|
||||
{
|
||||
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;
|
||||
LocTriggerData.tg_newtable = evtshared->ats_table->new_tuplestore;
|
||||
evtshared->ats_table->closed = true;
|
||||
}
|
||||
}
|
||||
@ -4686,10 +4642,8 @@ TransitionCaptureState *
|
||||
MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
|
||||
{
|
||||
TransitionCaptureState *state;
|
||||
bool need_old_upd,
|
||||
need_new_upd,
|
||||
need_old_del,
|
||||
need_new_ins;
|
||||
bool need_old,
|
||||
need_new;
|
||||
AfterTriggersTableData *table;
|
||||
MemoryContext oldcxt;
|
||||
ResourceOwner saveResourceOwner;
|
||||
@ -4701,31 +4655,23 @@ MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
|
||||
switch (cmdType)
|
||||
{
|
||||
case CMD_INSERT:
|
||||
need_old_upd = need_old_del = need_new_upd = false;
|
||||
need_new_ins = trigdesc->trig_insert_new_table;
|
||||
need_old = false;
|
||||
need_new = trigdesc->trig_insert_new_table;
|
||||
break;
|
||||
case CMD_UPDATE:
|
||||
need_old_upd = trigdesc->trig_update_old_table;
|
||||
need_new_upd = trigdesc->trig_update_new_table;
|
||||
need_old_del = need_new_ins = false;
|
||||
need_old = trigdesc->trig_update_old_table;
|
||||
need_new = trigdesc->trig_update_new_table;
|
||||
break;
|
||||
case CMD_DELETE:
|
||||
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;
|
||||
need_old = trigdesc->trig_delete_old_table;
|
||||
need_new = false;
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unexpected CmdType: %d", (int) cmdType);
|
||||
/* keep compiler quiet */
|
||||
need_old_upd = need_new_upd = need_old_del = need_new_ins = false;
|
||||
need_old = need_new = false; /* keep compiler quiet */
|
||||
break;
|
||||
}
|
||||
if (!need_old_upd && !need_new_upd && !need_new_ins && !need_old_del)
|
||||
if (!need_old && !need_new)
|
||||
return NULL;
|
||||
|
||||
/* Check state, like AfterTriggerSaveEvent. */
|
||||
@ -4755,14 +4701,10 @@ MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
|
||||
saveResourceOwner = CurrentResourceOwner;
|
||||
CurrentResourceOwner = CurTransactionResourceOwner;
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
CurrentResourceOwner = saveResourceOwner;
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
@ -4951,20 +4893,12 @@ AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
|
||||
{
|
||||
AfterTriggersTableData *table = (AfterTriggersTableData *) lfirst(lc);
|
||||
|
||||
ts = table->old_upd_tuplestore;
|
||||
table->old_upd_tuplestore = NULL;
|
||||
ts = table->old_tuplestore;
|
||||
table->old_tuplestore = NULL;
|
||||
if (ts)
|
||||
tuplestore_end(ts);
|
||||
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;
|
||||
ts = table->new_tuplestore;
|
||||
table->new_tuplestore = NULL;
|
||||
if (ts)
|
||||
tuplestore_end(ts);
|
||||
}
|
||||
@ -5735,84 +5669,6 @@ AfterTriggerPendingOnRel(Oid relid)
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the transition table for the given event and depending on whether we are
|
||||
* processing the old or the new tuple.
|
||||
*/
|
||||
static Tuplestorestate *
|
||||
AfterTriggerGetTransitionTable(int event,
|
||||
HeapTuple oldtup,
|
||||
HeapTuple newtup,
|
||||
TransitionCaptureState *transition_capture)
|
||||
{
|
||||
Tuplestorestate *tuplestore = NULL;
|
||||
bool delete_old_table = transition_capture->tcs_delete_old_table;
|
||||
bool update_old_table = transition_capture->tcs_update_old_table;
|
||||
bool update_new_table = transition_capture->tcs_update_new_table;
|
||||
bool insert_new_table = transition_capture->tcs_insert_new_table;;
|
||||
|
||||
/*
|
||||
* For INSERT events newtup should be non-NULL, for DELETE events
|
||||
* oldtup should be non-NULL, whereas for UPDATE events normally both
|
||||
* oldtup and newtup are non-NULL. But for UPDATE events fired for
|
||||
* capturing transition tuples during UPDATE partition-key row
|
||||
* movement, oldtup is NULL when the event is for a row being inserted,
|
||||
* whereas newtup is NULL when the event is for a row being deleted.
|
||||
*/
|
||||
Assert(!(event == TRIGGER_EVENT_DELETE && delete_old_table &&
|
||||
oldtup == NULL));
|
||||
Assert(!(event == TRIGGER_EVENT_INSERT && insert_new_table &&
|
||||
newtup == NULL));
|
||||
|
||||
/*
|
||||
* We're called either for the newtup or the oldtup, but not both at the
|
||||
* same time.
|
||||
*/
|
||||
Assert((oldtup != NULL) ^ (newtup != NULL));
|
||||
|
||||
if (oldtup != NULL)
|
||||
{
|
||||
if (event == TRIGGER_EVENT_DELETE && delete_old_table)
|
||||
tuplestore = transition_capture->tcs_private->old_del_tuplestore;
|
||||
else if (event == TRIGGER_EVENT_UPDATE && update_old_table)
|
||||
tuplestore = transition_capture->tcs_private->old_upd_tuplestore;
|
||||
}
|
||||
|
||||
if (newtup != NULL)
|
||||
{
|
||||
if (event == TRIGGER_EVENT_INSERT && insert_new_table)
|
||||
tuplestore = transition_capture->tcs_private->new_ins_tuplestore;
|
||||
else if (event == TRIGGER_EVENT_UPDATE && update_new_table)
|
||||
tuplestore = transition_capture->tcs_private->new_upd_tuplestore;
|
||||
}
|
||||
|
||||
return tuplestore;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the given heap tuple to the given tuplestore, applying the conversion
|
||||
* map if necessary.
|
||||
*/
|
||||
static void
|
||||
TransitionTableAddTuple(HeapTuple heaptup, Tuplestorestate *tuplestore,
|
||||
TupleConversionMap *map)
|
||||
{
|
||||
/*
|
||||
* Nothing needs to be done if we don't have a tuplestore.
|
||||
*/
|
||||
if (tuplestore == NULL)
|
||||
return;
|
||||
|
||||
if (map != NULL)
|
||||
{
|
||||
HeapTuple converted = do_convert_tuple(heaptup, map);
|
||||
|
||||
tuplestore_puttuple(tuplestore, converted);
|
||||
pfree(converted);
|
||||
}
|
||||
else
|
||||
tuplestore_puttuple(tuplestore, heaptup);
|
||||
}
|
||||
|
||||
/* ----------
|
||||
* AfterTriggerSaveEvent()
|
||||
@ -5874,37 +5730,61 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
|
||||
{
|
||||
HeapTuple original_insert_tuple = transition_capture->tcs_original_insert_tuple;
|
||||
TupleConversionMap *map = transition_capture->tcs_map;
|
||||
bool delete_old_table = transition_capture->tcs_delete_old_table;
|
||||
bool update_old_table = transition_capture->tcs_update_old_table;
|
||||
bool update_new_table = transition_capture->tcs_update_new_table;
|
||||
bool insert_new_table = transition_capture->tcs_insert_new_table;;
|
||||
|
||||
/*
|
||||
* Capture the old tuple in the appropriate transition table based on
|
||||
* the event.
|
||||
* For INSERT events newtup should be non-NULL, for DELETE events
|
||||
* oldtup should be non-NULL, whereas for UPDATE events normally both
|
||||
* oldtup and newtup are non-NULL. But for UPDATE events fired for
|
||||
* capturing transition tuples during UPDATE partition-key row
|
||||
* movement, oldtup is NULL when the event is for a row being inserted,
|
||||
* whereas newtup is NULL when the event is for a row being deleted.
|
||||
*/
|
||||
if (oldtup != NULL)
|
||||
Assert(!(event == TRIGGER_EVENT_DELETE && delete_old_table &&
|
||||
oldtup == NULL));
|
||||
Assert(!(event == TRIGGER_EVENT_INSERT && insert_new_table &&
|
||||
newtup == NULL));
|
||||
|
||||
if (oldtup != NULL &&
|
||||
((event == TRIGGER_EVENT_DELETE && delete_old_table) ||
|
||||
(event == TRIGGER_EVENT_UPDATE && update_old_table)))
|
||||
{
|
||||
Tuplestorestate *tuplestore =
|
||||
AfterTriggerGetTransitionTable(event,
|
||||
oldtup,
|
||||
NULL,
|
||||
transition_capture);
|
||||
TransitionTableAddTuple(oldtup, tuplestore, map);
|
||||
Tuplestorestate *old_tuplestore;
|
||||
|
||||
old_tuplestore = transition_capture->tcs_private->old_tuplestore;
|
||||
|
||||
if (map != NULL)
|
||||
{
|
||||
HeapTuple converted = do_convert_tuple(oldtup, map);
|
||||
|
||||
tuplestore_puttuple(old_tuplestore, converted);
|
||||
pfree(converted);
|
||||
}
|
||||
else
|
||||
tuplestore_puttuple(old_tuplestore, oldtup);
|
||||
}
|
||||
|
||||
/*
|
||||
* Capture the new tuple in the appropriate transition table based on
|
||||
* the event.
|
||||
*/
|
||||
if (newtup != NULL)
|
||||
if (newtup != NULL &&
|
||||
((event == TRIGGER_EVENT_INSERT && insert_new_table) ||
|
||||
(event == TRIGGER_EVENT_UPDATE && update_new_table)))
|
||||
{
|
||||
Tuplestorestate *tuplestore =
|
||||
AfterTriggerGetTransitionTable(event,
|
||||
NULL,
|
||||
newtup,
|
||||
transition_capture);
|
||||
Tuplestorestate *new_tuplestore;
|
||||
|
||||
new_tuplestore = transition_capture->tcs_private->new_tuplestore;
|
||||
|
||||
if (original_insert_tuple != NULL)
|
||||
tuplestore_puttuple(tuplestore, original_insert_tuple);
|
||||
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
|
||||
TransitionTableAddTuple(newtup, tuplestore, map);
|
||||
tuplestore_puttuple(new_tuplestore, newtup);
|
||||
}
|
||||
|
||||
/*
|
||||
|
Reference in New Issue
Block a user