diff options
Diffstat (limited to 'src/backend')
38 files changed, 177 insertions, 2840 deletions
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index fa415ab06f9..4fdb549099c 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -3248,7 +3248,6 @@ l1: result == HeapTupleUpdated || result == HeapTupleBeingUpdated); Assert(!(tp.t_data->t_infomask & HEAP_XMAX_INVALID)); - hufd->result = result; hufd->ctid = tp.t_data->t_ctid; hufd->xmax = HeapTupleHeaderGetUpdateXid(tp.t_data); if (result == HeapTupleSelfUpdated) @@ -3519,7 +3518,7 @@ simple_heap_delete(Relation relation, ItemPointer tid) HTSU_Result heap_update(Relation relation, ItemPointer otid, HeapTuple newtup, CommandId cid, Snapshot crosscheck, bool wait, - HeapUpdateFailureData *hufd) + HeapUpdateFailureData *hufd, LockTupleMode *lockmode) { HTSU_Result result; TransactionId xid = GetCurrentTransactionId(); @@ -3559,10 +3558,8 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup, infomask2_old_tuple, infomask_new_tuple, infomask2_new_tuple; - LockTupleMode lockmode; Assert(ItemPointerIsValid(otid)); - Assert(hufd != NULL); /* * Forbid this during a parallel operation, lest it allocate a combocid. @@ -3678,7 +3675,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup, */ if (!bms_overlap(modified_attrs, key_attrs)) { - lockmode = hufd->lockmode = LockTupleNoKeyExclusive; + *lockmode = LockTupleNoKeyExclusive; mxact_status = MultiXactStatusNoKeyUpdate; key_intact = true; @@ -3695,7 +3692,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup, } else { - lockmode = hufd->lockmode = LockTupleExclusive; + *lockmode = LockTupleExclusive; mxact_status = MultiXactStatusUpdate; key_intact = false; } @@ -3773,12 +3770,12 @@ l2: int remain; if (DoesMultiXactIdConflict((MultiXactId) xwait, infomask, - lockmode)) + *lockmode)) { LockBuffer(buffer, BUFFER_LOCK_UNLOCK); /* acquire tuple lock, if necessary */ - heap_acquire_tuplock(relation, &(oldtup.t_self), lockmode, + heap_acquire_tuplock(relation, &(oldtup.t_self), *lockmode, LockWaitBlock, &have_tuple_lock); /* wait for multixact */ @@ -3862,7 +3859,7 @@ l2: * lock. */ LockBuffer(buffer, BUFFER_LOCK_UNLOCK); - heap_acquire_tuplock(relation, &(oldtup.t_self), lockmode, + heap_acquire_tuplock(relation, &(oldtup.t_self), *lockmode, LockWaitBlock, &have_tuple_lock); XactLockTableWait(xwait, relation, &oldtup.t_self, XLTW_Update); @@ -3901,7 +3898,6 @@ l2: result == HeapTupleUpdated || result == HeapTupleBeingUpdated); Assert(!(oldtup.t_data->t_infomask & HEAP_XMAX_INVALID)); - hufd->result = result; hufd->ctid = oldtup.t_data->t_ctid; hufd->xmax = HeapTupleHeaderGetUpdateXid(oldtup.t_data); if (result == HeapTupleSelfUpdated) @@ -3910,7 +3906,7 @@ l2: hufd->cmax = InvalidCommandId; UnlockReleaseBuffer(buffer); if (have_tuple_lock) - UnlockTupleTuplock(relation, &(oldtup.t_self), lockmode); + UnlockTupleTuplock(relation, &(oldtup.t_self), *lockmode); if (vmbuffer != InvalidBuffer) ReleaseBuffer(vmbuffer); bms_free(hot_attrs); @@ -3948,7 +3944,7 @@ l2: compute_new_xmax_infomask(HeapTupleHeaderGetRawXmax(oldtup.t_data), oldtup.t_data->t_infomask, oldtup.t_data->t_infomask2, - xid, lockmode, true, + xid, *lockmode, true, &xmax_old_tuple, &infomask_old_tuple, &infomask2_old_tuple); @@ -4065,7 +4061,7 @@ l2: compute_new_xmax_infomask(HeapTupleHeaderGetRawXmax(oldtup.t_data), oldtup.t_data->t_infomask, oldtup.t_data->t_infomask2, - xid, lockmode, false, + xid, *lockmode, false, &xmax_lock_old_tuple, &infomask_lock_old_tuple, &infomask2_lock_old_tuple); @@ -4377,7 +4373,7 @@ l2: * Release the lmgr tuple lock, if we had it. */ if (have_tuple_lock) - UnlockTupleTuplock(relation, &(oldtup.t_self), lockmode); + UnlockTupleTuplock(relation, &(oldtup.t_self), *lockmode); pgstat_count_heap_update(relation, use_hot_update); @@ -4601,11 +4597,12 @@ simple_heap_update(Relation relation, ItemPointer otid, HeapTuple tup) { HTSU_Result result; HeapUpdateFailureData hufd; + LockTupleMode lockmode; result = heap_update(relation, otid, tup, GetCurrentCommandId(true), InvalidSnapshot, true /* wait for commit */ , - &hufd); + &hufd, &lockmode); switch (result) { case HeapTupleSelfUpdated: @@ -5191,7 +5188,6 @@ failed: Assert(result == HeapTupleSelfUpdated || result == HeapTupleUpdated || result == HeapTupleWouldBlock); Assert(!(tuple->t_data->t_infomask & HEAP_XMAX_INVALID)); - hufd->result = result; hufd->ctid = tuple->t_data->t_ctid; hufd->xmax = HeapTupleHeaderGetUpdateXid(tuple->t_data); if (result == HeapTupleSelfUpdated) diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt index ca0409b83ea..20d61f37803 100644 --- a/src/backend/catalog/sql_features.txt +++ b/src/backend/catalog/sql_features.txt @@ -229,9 +229,9 @@ F311 Schema definition statement 02 CREATE TABLE for persistent base tables YES F311 Schema definition statement 03 CREATE VIEW YES F311 Schema definition statement 04 CREATE VIEW: WITH CHECK OPTION YES F311 Schema definition statement 05 GRANT statement YES -F312 MERGE statement YES also consider INSERT ... ON CONFLICT DO UPDATE -F313 Enhanced MERGE statement YES -F314 MERGE statement with DELETE branch YES +F312 MERGE statement NO consider INSERT ... ON CONFLICT DO UPDATE +F313 Enhanced MERGE statement NO +F314 MERGE statement with DELETE branch NO F321 User authorization YES F341 Usage tables NO no ROUTINE_*_USAGE tables F361 Subprogram support YES diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 306e6444669..e1a62a1bce9 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -946,9 +946,6 @@ ExplainNode(PlanState *planstate, List *ancestors, case CMD_DELETE: pname = operation = "Delete"; break; - case CMD_MERGE: - pname = operation = "Merge"; - break; default: pname = "???"; break; @@ -3011,10 +3008,6 @@ 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 ???"; @@ -3137,32 +3130,6 @@ 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); diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c index c3610b18741..b945b1556a8 100644 --- a/src/backend/commands/prepare.c +++ b/src/backend/commands/prepare.c @@ -151,7 +151,6 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString, case CMD_INSERT: case CMD_UPDATE: case CMD_DELETE: - case CMD_MERGE: /* OK */ break; default: diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 0d57d467484..cc50691aa00 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -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; - 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; + ts = table->old_tuplestore; + table->old_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; - /* - * Capture the new tuple in the appropriate transition table based on - * the event. - */ - if (newtup != NULL) + 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); + } + 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); } /* diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile index 76d87eea49c..cc09895fa5c 100644 --- a/src/backend/executor/Makefile +++ b/src/backend/executor/Makefile @@ -14,7 +14,7 @@ include $(top_builddir)/src/Makefile.global OBJS = execAmi.o execCurrent.o execExpr.o execExprInterp.o \ execGrouping.o execIndexing.o execJunk.o \ - execMain.o execMerge.o execParallel.o execPartition.o execProcnode.o \ + execMain.o execParallel.o execPartition.o execProcnode.o \ execReplication.o execScan.o execSRF.o execTuples.o \ execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \ nodeBitmapAnd.o nodeBitmapOr.o \ diff --git a/src/backend/executor/README b/src/backend/executor/README index 67736805c26..0d7cd552eb6 100644 --- a/src/backend/executor/README +++ b/src/backend/executor/README @@ -37,17 +37,6 @@ the plan tree returns the computed tuples to be updated, plus a "junk" one. For DELETE, the plan tree need only deliver a CTID column, and the ModifyTable node visits each of those rows and marks the row deleted. -MERGE runs one generic plan that returns candidate target rows. Each row -consists of a super-row that contains all the columns needed by any of the -individual actions, plus CTID and TABLEOID junk columns. The CTID column is -required to know if a matching target row was found or not and the TABLEOID -column is needed to find the underlying target partition, in case when the -target table is a partitioned table. When a matching target tuple is found, -the CTID column identifies the matching tuple and we attempt to activate -WHEN MATCHED actions. If a matching tuple is not found, then CTID column is -NULL and we attempt to activate WHEN NOT MATCHED actions. Once we know which -action is activated we form the final result row and apply only those changes. - XXX a great deal more documentation needs to be written here... diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 13ad92745e0..4fa713bbe5e 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -233,7 +233,6 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags) case CMD_INSERT: case CMD_DELETE: case CMD_UPDATE: - case CMD_MERGE: estate->es_output_cid = GetCurrentCommandId(true); break; @@ -1351,9 +1350,6 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo, resultRelInfo->ri_onConflictArbiterIndexes = NIL; resultRelInfo->ri_onConflict = NULL; - resultRelInfo->ri_mergeTargetRTI = 0; - resultRelInfo->ri_mergeState = (MergeState *) palloc0(sizeof (MergeState)); - /* * Partition constraint, which also includes the partition constraint of * all the ancestors that are partitions. Note that it will be checked @@ -2203,19 +2199,6 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, errmsg("new row violates row-level security policy for table \"%s\"", wco->relname))); break; - case WCO_RLS_MERGE_UPDATE_CHECK: - case WCO_RLS_MERGE_DELETE_CHECK: - if (wco->polname != NULL) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("target row violates row-level security policy \"%s\" (USING expression) for table \"%s\"", - wco->polname, wco->relname))); - else - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("target row violates row-level security policy (USING expression) for table \"%s\"", - wco->relname))); - break; case WCO_RLS_CONFLICT_CHECK: if (wco->polname != NULL) ereport(ERROR, diff --git a/src/backend/executor/execMerge.c b/src/backend/executor/execMerge.c deleted file mode 100644 index d75d7e5ab26..00000000000 --- a/src/backend/executor/execMerge.c +++ /dev/null @@ -1,682 +0,0 @@ -/*------------------------------------------------------------------------- - * - * execMerge.c - * routines to handle Merge nodes relating to the MERGE command - * - * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * src/backend/executor/execMerge.c - * - *------------------------------------------------------------------------- - */ - - -#include "postgres.h" - -#include "access/htup_details.h" -#include "access/xact.h" -#include "commands/trigger.h" -#include "executor/execPartition.h" -#include "executor/executor.h" -#include "executor/nodeModifyTable.h" -#include "executor/execMerge.h" -#include "miscadmin.h" -#include "nodes/nodeFuncs.h" -#include "storage/bufmgr.h" -#include "storage/lmgr.h" -#include "utils/builtins.h" -#include "utils/memutils.h" -#include "utils/rel.h" -#include "utils/tqual.h" - -static void ExecMergeNotMatched(ModifyTableState *mtstate, EState *estate, - TupleTableSlot *slot); -static bool ExecMergeMatched(ModifyTableState *mtstate, EState *estate, - TupleTableSlot *slot, JunkFilter *junkfilter, - ItemPointer tupleid); -/* - * Perform MERGE. - */ -void -ExecMerge(ModifyTableState *mtstate, EState *estate, TupleTableSlot *slot, - JunkFilter *junkfilter, ResultRelInfo *resultRelInfo) -{ - ExprContext *econtext = mtstate->ps.ps_ExprContext; - ItemPointer tupleid; - ItemPointerData tuple_ctid; - bool matched = false; - Datum datum; - bool isNull; - - Assert(resultRelInfo->ri_RelationDesc->rd_rel->relkind == RELKIND_RELATION || - resultRelInfo->ri_RelationDesc->rd_rel->relkind == RELKIND_PARTITIONED_TABLE); - - /* - * Reset per-tuple memory context to free any expression evaluation - * storage allocated in the previous cycle. - */ - ResetExprContext(econtext); - - /* - * We run a JOIN between the target relation and the source relation to - * find a set of candidate source rows that has matching row in the target - * table and a set of candidate source rows that does not have matching - * row in the target table. If the join returns us a tuple with target - * relation's tid set, that implies that the join found a matching row for - * the given source tuple. This case triggers the WHEN MATCHED clause of - * the MERGE. Whereas a NULL in the target relation's ctid column - * indicates a NOT MATCHED case. - */ - datum = ExecGetJunkAttribute(slot, junkfilter->jf_junkAttNo, &isNull); - - if (!isNull) - { - matched = true; - tupleid = (ItemPointer) DatumGetPointer(datum); - tuple_ctid = *tupleid; /* be sure we don't free ctid!! */ - tupleid = &tuple_ctid; - } - else - { - matched = false; - tupleid = NULL; /* we don't need it for INSERT actions */ - } - - /* - * If we are dealing with a WHEN MATCHED case, we execute the first action - * for which the additional WHEN MATCHED AND quals pass. If an action - * without quals is found, that action is executed. - * - * Similarly, if we are dealing with WHEN NOT MATCHED case, we look at the - * given WHEN NOT MATCHED actions in sequence until one passes. - * - * Things get interesting in case of concurrent update/delete of the - * target tuple. Such concurrent update/delete is detected while we are - * executing a WHEN MATCHED action. - * - * A concurrent update can: - * - * 1. modify the target tuple so that it no longer satisfies the - * additional quals attached to the current WHEN MATCHED action OR - * - * In this case, we are still dealing with a WHEN MATCHED case, but - * we should recheck the list of WHEN MATCHED actions and choose the first - * one that satisfies the new target tuple. - * - * 2. modify the target tuple so that the join quals no longer pass and - * hence the source tuple no longer has a match. - * - * In the second case, the source tuple no longer matches the target tuple, - * so we now instead find a qualifying WHEN NOT MATCHED action to execute. - * - * A concurrent delete, changes a WHEN MATCHED case to WHEN NOT MATCHED. - * - * ExecMergeMatched takes care of following the update chain and - * re-finding the qualifying WHEN MATCHED action, as long as the updated - * target tuple still satisfies the join quals i.e. it still remains a - * WHEN MATCHED case. If the tuple gets deleted or the join quals fail, it - * returns and we try ExecMergeNotMatched. Given that ExecMergeMatched - * always make progress by following the update chain and we never switch - * from ExecMergeNotMatched to ExecMergeMatched, there is no risk of a - * livelock. - */ - if (matched) - matched = ExecMergeMatched(mtstate, estate, slot, junkfilter, tupleid); - - /* - * Either we were dealing with a NOT MATCHED tuple or ExecMergeNotMatched() - * returned "false", indicating the previously MATCHED tuple is no longer a - * matching tuple. - */ - if (!matched) - ExecMergeNotMatched(mtstate, estate, slot); -} - -/* - * Check and execute the first qualifying MATCHED action. The current target - * tuple is identified by tupleid. - * - * We start from the first WHEN MATCHED action and check if the WHEN AND quals - * pass, if any. If the WHEN AND quals for the first action do not pass, we - * check the second, then the third and so on. If we reach to the end, no - * action is taken and we return true, indicating that no further action is - * required for this tuple. - * - * If we do find a qualifying action, then we attempt to execute the action. - * - * If the tuple is concurrently updated, EvalPlanQual is run with the updated - * tuple to recheck the join quals. Note that the additional quals associated - * with individual actions are evaluated separately by the MERGE code, while - * EvalPlanQual checks for the join quals. If EvalPlanQual tells us that the - * updated tuple still passes the join quals, then we restart from the first - * action to look for a qualifying action. Otherwise, we return false meaning - * that a NOT MATCHED action must now be executed for the current source tuple. - */ -static bool -ExecMergeMatched(ModifyTableState *mtstate, EState *estate, - TupleTableSlot *slot, JunkFilter *junkfilter, - ItemPointer tupleid) -{ - ExprContext *econtext = mtstate->ps.ps_ExprContext; - bool isNull; - List *mergeMatchedActionStates = NIL; - HeapUpdateFailureData hufd; - bool tuple_updated, - tuple_deleted; - Buffer buffer; - HeapTupleData tuple; - EPQState *epqstate = &mtstate->mt_epqstate; - ResultRelInfo *saved_resultRelInfo; - ResultRelInfo *resultRelInfo = estate->es_result_relation_info; - ListCell *l; - TupleTableSlot *saved_slot = slot; - - if (mtstate->mt_partition_tuple_routing) - { - Datum datum; - Oid tableoid = InvalidOid; - int leaf_part_index; - PartitionTupleRouting *proute = mtstate->mt_partition_tuple_routing; - - /* - * In case of partitioned table, we fetch the tableoid while performing - * MATCHED MERGE action. - */ - datum = ExecGetJunkAttribute(slot, junkfilter->jf_otherJunkAttNo, - &isNull); - Assert(!isNull); - tableoid = DatumGetObjectId(datum); - - /* - * If we're dealing with a MATCHED tuple, then tableoid must have been - * set correctly. In case of partitioned table, we must now fetch the - * correct result relation corresponding to the child table emitting - * the matching target row. For normal table, there is just one result - * relation and it must be the one emitting the matching row. - */ - leaf_part_index = ExecFindPartitionByOid(proute, tableoid); - - resultRelInfo = proute->partitions[leaf_part_index]; - if (resultRelInfo == NULL) - { - resultRelInfo = ExecInitPartitionInfo(mtstate, - mtstate->resultRelInfo, - proute, estate, leaf_part_index); - Assert(resultRelInfo != NULL); - } - } - - /* - * Save the current information and work with the correct result relation. - */ - saved_resultRelInfo = resultRelInfo; - estate->es_result_relation_info = resultRelInfo; - - /* - * And get the correct action lists. - */ - mergeMatchedActionStates = - resultRelInfo->ri_mergeState->matchedActionStates; - - /* - * If there are not WHEN MATCHED actions, we are done. - */ - if (mergeMatchedActionStates == NIL) - return true; - - /* - * Make tuple and any needed join variables available to ExecQual and - * ExecProject. The target's existing tuple is installed in the scantuple. - * Again, this target relation's slot is required only in the case of a - * MATCHED tuple and UPDATE/DELETE actions. - */ - if (mtstate->mt_partition_tuple_routing) - ExecSetSlotDescriptor(mtstate->mt_existing, - resultRelInfo->ri_RelationDesc->rd_att); - econtext->ecxt_scantuple = mtstate->mt_existing; - econtext->ecxt_innertuple = slot; - econtext->ecxt_outertuple = NULL; - -lmerge_matched:; - slot = saved_slot; - - /* - * UPDATE/DELETE is only invoked for matched rows. And we must have found - * the tupleid of the target row in that case. We fetch using SnapshotAny - * because we might get called again after EvalPlanQual returns us a new - * tuple. This tuple may not be visible to our MVCC snapshot. - */ - Assert(tupleid != NULL); - - tuple.t_self = *tupleid; - if (!heap_fetch(resultRelInfo->ri_RelationDesc, SnapshotAny, &tuple, - &buffer, true, NULL)) - elog(ERROR, "Failed to fetch the target tuple"); - - /* Store target's existing tuple in the state's dedicated slot */ - ExecStoreTuple(&tuple, mtstate->mt_existing, buffer, false); - - foreach(l, mergeMatchedActionStates) - { - MergeActionState *action = (MergeActionState *) lfirst(l); - - /* - * Test condition, if any - * - * In the absence of a condition we perform the action unconditionally - * (no need to check separately since ExecQual() will return true if - * there are no conditions to evaluate). - */ - if (!ExecQual(action->whenqual, econtext)) - continue; - - /* - * Check if the existing target tuple meet the USING checks of - * UPDATE/DELETE RLS policies. If those checks fail, we throw an - * error. - * - * The WITH CHECK quals are applied in ExecUpdate() and hence we need - * not do anything special to handle them. - * - * NOTE: We must do this after WHEN quals are evaluated so that we - * check policies only when they matter. - */ - if (resultRelInfo->ri_WithCheckOptions) - { - ExecWithCheckOptions(action->commandType == CMD_UPDATE ? - WCO_RLS_MERGE_UPDATE_CHECK : WCO_RLS_MERGE_DELETE_CHECK, - resultRelInfo, - mtstate->mt_existing, - mtstate->ps.state); - } - - /* Perform stated action */ - switch (action->commandType) - { - case CMD_UPDATE: - - /* - * We set up the projection earlier, so all we do here is - * Project, no need for any other tasks prior to the - * ExecUpdate. - */ - if (mtstate->mt_partition_tuple_routing) - ExecSetSlotDescriptor(mtstate->mt_mergeproj, action->tupDesc); - ExecProject(action->proj); - - /* - * We don't call ExecFilterJunk() because the projected tuple - * using the UPDATE action's targetlist doesn't have a junk - * attribute. - */ - slot = ExecUpdate(mtstate, tupleid, NULL, - mtstate->mt_mergeproj, - slot, epqstate, estate, - &tuple_updated, &hufd, - action, mtstate->canSetTag); - break; - - case CMD_DELETE: - /* Nothing to Project for a DELETE action */ - slot = ExecDelete(mtstate, tupleid, NULL, - slot, epqstate, estate, - &tuple_deleted, false, &hufd, action, - mtstate->canSetTag, - false /* changingPart */); - - break; - - default: - elog(ERROR, "unknown action in MERGE WHEN MATCHED clause"); - - } - - /* - * Check for any concurrent update/delete operation which may have - * prevented our update/delete. We also check for situations where we - * might be trying to update/delete the same tuple twice. - */ - if ((action->commandType == CMD_UPDATE && !tuple_updated) || - (action->commandType == CMD_DELETE && !tuple_deleted)) - - { - switch (hufd.result) - { - case HeapTupleMayBeUpdated: - break; - case HeapTupleInvisible: - - /* - * This state should never be reached since the underlying - * JOIN runs with a MVCC snapshot and EvalPlanQual runs - * with a dirty snapshot. So such a row should have never - * been returned for MERGE. - */ - elog(ERROR, "unexpected invisible tuple"); - break; - - case HeapTupleSelfUpdated: - - /* - * SQLStandard disallows this for MERGE. - */ - if (TransactionIdIsCurrentTransactionId(hufd.xmax)) - ereport(ERROR, - (errcode(ERRCODE_CARDINALITY_VIOLATION), - errmsg("MERGE command cannot affect row a second time"), - errhint("Ensure that not more than one source row matches any one target row"))); - /* This shouldn't happen */ - elog(ERROR, "attempted to update or delete invisible tuple"); - break; - - case HeapTupleUpdated: - - /* - * The target tuple was concurrently updated/deleted by - * some other transaction. - * - * If the current tuple is that last tuple in the update - * chain, then we know that the tuple was concurrently - * deleted. Just return and let the caller try NOT MATCHED - * actions. - * - * If the current tuple was concurrently updated, then we - * must run the EvalPlanQual() with the new version of the - * tuple. If EvalPlanQual() does not return a tuple then - * we switch to the NOT MATCHED list of actions. - * If it does return a tuple and the join qual is - * still satisfied, then we just need to recheck the - * MATCHED actions, starting from the top, and execute the - * first qualifying action. - */ - if (!ItemPointerEquals(tupleid, &hufd.ctid)) - { - TupleTableSlot *epqslot; - - /* - * Since we generate a JOIN query with a target table - * RTE different than the result relation RTE, we must - * pass in the RTI of the relation used in the join - * query and not the one from result relation. - */ - Assert(resultRelInfo->ri_mergeTargetRTI > 0); - epqslot = EvalPlanQual(estate, - epqstate, - resultRelInfo->ri_RelationDesc, - GetEPQRangeTableIndex(resultRelInfo), - LockTupleExclusive, - &hufd.ctid, - hufd.xmax); - - if (!TupIsNull(epqslot)) - { - (void) ExecGetJunkAttribute(epqslot, - resultRelInfo->ri_junkFilter->jf_junkAttNo, - &isNull); - - /* - * A non-NULL ctid means that we are still dealing - * with MATCHED case. But we must retry from the - * start with the updated tuple to ensure that the - * first qualifying WHEN MATCHED action is - * executed. - * - * We don't use the new slot returned by - * EvalPlanQual because we anyways re-install the - * new target tuple in econtext->ecxt_scantuple - * before re-evaluating WHEN AND conditions and - * re-projecting the update targetlists. The - * source side tuple does not change and hence we - * can safely continue to use the old slot. - */ - if (!isNull) - { - /* - * Must update *tupleid to the TID of the - * newer tuple found in the update chain. - */ - *tupleid = hufd.ctid; - ReleaseBuffer(buffer); - goto lmerge_matched; - } - } - } - - /* - * Tell the caller about the updated TID, restore the - * state back and return. - */ - *tupleid = hufd.ctid; - estate->es_result_relation_info = saved_resultRelInfo; - ReleaseBuffer(buffer); - return false; - - default: - break; - - } - } - - if (action->commandType == CMD_UPDATE && tuple_updated) - InstrCountFiltered2(&mtstate->ps, 1); - if (action->commandType == CMD_DELETE && tuple_deleted) - InstrCountFiltered3(&mtstate->ps, 1); - - /* - * We've activated one of the WHEN clauses, so we don't search - * further. This is required behaviour, not an optimization. - */ - estate->es_result_relation_info = saved_resultRelInfo; - break; - } - - ReleaseBuffer(buffer); - - /* - * Successfully executed an action or no qualifying action was found. - */ - return true; -} - -/* - * Execute the first qualifying NOT MATCHED action. - */ -static void -ExecMergeNotMatched(ModifyTableState *mtstate, EState *estate, - TupleTableSlot *slot) -{ - PartitionTupleRouting *proute = mtstate->mt_partition_tuple_routing; - ExprContext *econtext = mtstate->ps.ps_ExprContext; - List *mergeNotMatchedActionStates = NIL; - ResultRelInfo *resultRelInfo; - ListCell *l; - TupleTableSlot *myslot; - - /* - * We are dealing with NOT MATCHED tuple. Since for MERGE, the partition - * tree is not expanded for the result relation, we continue to work with - * the currently active result relation, which corresponds to the root - * of the partition tree. - */ - resultRelInfo = mtstate->resultRelInfo; - - /* - * For INSERT actions, root relation's merge action is OK since the - * INSERT's targetlist and the WHEN conditions can only refer to the - * source relation and hence it does not matter which result relation we - * work with. - */ - mergeNotMatchedActionStates = - resultRelInfo->ri_mergeState->notMatchedActionStates; - - /* - * Make source tuple available to ExecQual and ExecProject. We don't need - * the target tuple since the WHEN quals and the targetlist can't refer to - * the target columns. - */ - econtext->ecxt_scantuple = NULL; - econtext->ecxt_innertuple = slot; - econtext->ecxt_outertuple = NULL; - - foreach(l, mergeNotMatchedActionStates) - { - MergeActionState *action = (MergeActionState *) lfirst(l); - - /* - * Test condition, if any - * - * In the absence of a condition we perform the action unconditionally - * (no need to check separately since ExecQual() will return true if - * there are no conditions to evaluate). - */ - if (!ExecQual(action->whenqual, econtext)) - continue; - - /* Perform stated action */ - switch (action->commandType) - { - case CMD_INSERT: - - /* - * We set up the projection earlier, so all we do here is - * Project, no need for any other tasks prior to the - * ExecInsert. - */ - if (mtstate->mt_partition_tuple_routing) - ExecSetSlotDescriptor(mtstate->mt_mergeproj, action->tupDesc); - ExecProject(action->proj); - - /* - * ExecPrepareTupleRouting may modify the passed-in slot. Hence - * pass a local reference so that action->slot is not modified. - */ - myslot = mtstate->mt_mergeproj; - - /* Prepare for tuple routing if needed. */ - if (proute) - myslot = ExecPrepareTupleRouting(mtstate, estate, proute, - resultRelInfo, myslot); - slot = ExecInsert(mtstate, myslot, slot, - estate, action, - mtstate->canSetTag); - /* Revert ExecPrepareTupleRouting's state change. */ - if (proute) - estate->es_result_relation_info = resultRelInfo; - InstrCountFiltered1(&mtstate->ps, 1); - break; - case CMD_NOTHING: - /* Do Nothing */ - break; - default: - elog(ERROR, "unknown action in MERGE WHEN NOT MATCHED clause"); - } - - break; - } -} - -void -ExecInitMerge(ModifyTableState *mtstate, EState *estate, - ResultRelInfo *resultRelInfo) -{ - ListCell *l; - ExprContext *econtext; - List *mergeMatchedActionStates = NIL; - List *mergeNotMatchedActionStates = NIL; - TupleDesc relationDesc = resultRelInfo->ri_RelationDesc->rd_att; - ModifyTable *node = (ModifyTable *) mtstate->ps.plan; - - if (node->mergeActionList == NIL) - return; - - mtstate->mt_merge_subcommands = 0; - - if (mtstate->ps.ps_ExprContext == NULL) - ExecAssignExprContext(estate, &mtstate->ps); - - econtext = mtstate->ps.ps_ExprContext; - - /* initialize slot for the existing tuple */ - Assert(mtstate->mt_existing == NULL); - mtstate->mt_existing = - ExecInitExtraTupleSlot(mtstate->ps.state, - mtstate->mt_partition_tuple_routing ? - NULL : relationDesc); - - /* initialize slot for merge actions */ - Assert(mtstate->mt_mergeproj == NULL); - mtstate->mt_mergeproj = - ExecInitExtraTupleSlot(mtstate->ps.state, - mtstate->mt_partition_tuple_routing ? - NULL : relationDesc); - - /* - * Create a MergeActionState for each action on the mergeActionList - * and add it to either a list of matched actions or not-matched - * actions. - */ - foreach(l, node->mergeActionList) - { - MergeAction *action = (MergeAction *) lfirst(l); - MergeActionState *action_state = makeNode(MergeActionState); - TupleDesc tupDesc; - - action_state->matched = action->matched; - action_state->commandType = action->commandType; - action_state->whenqual = ExecInitQual((List *) action->qual, - &mtstate->ps); - - /* create target slot for this action's projection */ - tupDesc = ExecTypeFromTL((List *) action->targetList, - resultRelInfo->ri_RelationDesc->rd_rel->relhasoids); - action_state->tupDesc = tupDesc; - - /* build action projection state */ - action_state->proj = - ExecBuildProjectionInfo(action->targetList, econtext, - mtstate->mt_mergeproj, &mtstate->ps, - resultRelInfo->ri_RelationDesc->rd_att); - - /* - * We create two lists - one for WHEN MATCHED actions and one - * for WHEN NOT MATCHED actions - and stick the - * MergeActionState into the appropriate list. - */ - if (action_state->matched) - mergeMatchedActionStates = - lappend(mergeMatchedActionStates, action_state); - else - mergeNotMatchedActionStates = - lappend(mergeNotMatchedActionStates, action_state); - - switch (action->commandType) - { - case CMD_INSERT: - ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc, - action->targetList); - mtstate->mt_merge_subcommands |= MERGE_INSERT; - break; - case CMD_UPDATE: - ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc, - action->targetList); - mtstate->mt_merge_subcommands |= MERGE_UPDATE; - break; - case CMD_DELETE: - mtstate->mt_merge_subcommands |= MERGE_DELETE; - break; - case CMD_NOTHING: - break; - default: - elog(ERROR, "unknown operation"); - break; - } - - resultRelInfo->ri_mergeState->matchedActionStates = - mergeMatchedActionStates; - resultRelInfo->ri_mergeState->notMatchedActionStates = - mergeNotMatchedActionStates; - } -} diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index d4d54e927a5..11139f743d1 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -73,8 +73,6 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate, Relation rel) ResultRelInfo *update_rri = NULL; int num_update_rri = 0, update_rri_index = 0; - bool is_update = false; - bool is_merge = false; PartitionTupleRouting *proute; int nparts; ModifyTable *node = mtstate ? (ModifyTable *) mtstate->ps.plan : NULL; @@ -97,22 +95,13 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate, Relation rel) /* Set up details specific to the type of tuple routing we are doing. */ if (node && node->operation == CMD_UPDATE) - is_update = true; - else if (node && node->operation == CMD_MERGE) - is_merge = true; - - if (is_update) { update_rri = mtstate->resultRelInfo; num_update_rri = list_length(node->plans); proute->subplan_partition_offsets = palloc(num_update_rri * sizeof(int)); proute->num_subplan_partition_offsets = num_update_rri; - } - - if (is_update || is_merge) - { /* * We need an additional tuple slot for storing transient tuples that * are converted to the root table descriptor. @@ -296,30 +285,6 @@ ExecFindPartition(ResultRelInfo *resultRelInfo, PartitionDispatch *pd, } /* - * Given OID of the partition leaf, return the index of the leaf in the - * partition hierarchy. - * - * XXX This is an O(N) operation and further optimization would be beneficial - */ -int -ExecFindPartitionByOid(PartitionTupleRouting *proute, Oid partoid) -{ - int i; - - for (i = 0; i < proute->num_partitions; i++) - { - if (proute->partition_oids[i] == partoid) - break; - } - - if (i >= proute->num_partitions) - ereport(ERROR, - (errcode(ERRCODE_INTERNAL_ERROR), - errmsg("no partition found for OID %u", partoid))); - return i; -} - -/* * ExecInitPartitionInfo * Initialize ResultRelInfo and other information for a partition if not * already done @@ -357,8 +322,6 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, rootrel, estate->es_instrument); - leaf_part_rri->ri_PartitionLeafIndex = partidx; - /* * Since we've just initialized this ResultRelInfo, it's not in any list * attached to the estate as yet. Add it, so that it can be found later. @@ -635,90 +598,6 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, Assert(proute->partitions[partidx] == NULL); proute->partitions[partidx] = leaf_part_rri; - /* - * Initialize information about this partition that's needed to handle - * MERGE. - */ - if (node && node->operation == CMD_MERGE) - { - TupleDesc partrelDesc = RelationGetDescr(partrel); - TupleConversionMap *map = proute->parent_child_tupconv_maps[partidx]; - int firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex; - Relation firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc; - - /* - * If the root parent and partition have the same tuple - * descriptor, just reuse the original MERGE state for partition. - */ - if (map == NULL) - { - leaf_part_rri->ri_mergeState = resultRelInfo->ri_mergeState; - } - else - { - /* Convert expressions contain partition's attnos. */ - List *conv_tl, *conv_qual; - ListCell *l; - List *matchedActionStates = NIL; - List *notMatchedActionStates = NIL; - - foreach (l, node->mergeActionList) - { - MergeAction *action = lfirst_node(MergeAction, l); - MergeActionState *action_state = makeNode(MergeActionState); - TupleDesc tupDesc; - ExprContext *econtext; - - action_state->matched = action->matched; - action_state->commandType = action->commandType; - - conv_qual = (List *) action->qual; - conv_qual = map_partition_varattnos(conv_qual, - firstVarno, partrel, - firstResultRel, NULL); - - action_state->whenqual = ExecInitQual(conv_qual, &mtstate->ps); - - conv_tl = (List *) action->targetList; - conv_tl = map_partition_varattnos(conv_tl, - firstVarno, partrel, - firstResultRel, NULL); - - conv_tl = adjust_partition_tlist( conv_tl, map); - - tupDesc = ExecTypeFromTL(conv_tl, partrelDesc->tdhasoid); - action_state->tupDesc = tupDesc; - - /* build action projection state */ - econtext = mtstate->ps.ps_ExprContext; - action_state->proj = - ExecBuildProjectionInfo(conv_tl, econtext, - mtstate->mt_mergeproj, - &mtstate->ps, - partrelDesc); - - if (action_state->matched) - matchedActionStates = - lappend(matchedActionStates, action_state); - else - notMatchedActionStates = - lappend(notMatchedActionStates, action_state); - } - leaf_part_rri->ri_mergeState->matchedActionStates = - matchedActionStates; - leaf_part_rri->ri_mergeState->notMatchedActionStates = - notMatchedActionStates; - } - - /* - * get_partition_dispatch_recurse() and expand_partitioned_rtentry() - * fetch the leaf OIDs in the same order. So we can safely derive the - * index of the merge target relation corresponding to this partition - * by simply adding partidx + 1 to the root's merge target relation. - */ - leaf_part_rri->ri_mergeTargetRTI = node->mergeTargetRelation + - partidx + 1; - } MemoryContextSwitchTo(oldContext); return leaf_part_rri; diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c index b66346702dc..0333ccd0fed 100644 --- a/src/backend/executor/execReplication.c +++ b/src/backend/executor/execReplication.c @@ -464,7 +464,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, { slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo, &searchslot->tts_tuple->t_self, - NULL, slot, NULL); + NULL, slot); if (slot == NULL) /* "do nothing" */ skip_tuple = true; @@ -525,7 +525,7 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate, { skip_tuple = !ExecBRDeleteTriggers(estate, epqstate, resultRelInfo, &searchslot->tts_tuple->t_self, - NULL, NULL); + NULL); } if (!skip_tuple) diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 543a735be2b..7ec2c6bcaa8 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -42,7 +42,6 @@ #include "commands/trigger.h" #include "executor/execPartition.h" #include "executor/executor.h" -#include "executor/execMerge.h" #include "executor/nodeModifyTable.h" #include "foreign/fdwapi.h" #include "miscadmin.h" @@ -63,6 +62,11 @@ static bool ExecOnConflictUpdate(ModifyTableState *mtstate, EState *estate, bool canSetTag, TupleTableSlot **returning); +static TupleTableSlot *ExecPrepareTupleRouting(ModifyTableState *mtstate, + EState *estate, + PartitionTupleRouting *proute, + ResultRelInfo *targetRelInfo, + TupleTableSlot *slot); static ResultRelInfo *getTargetResultRelInfo(ModifyTableState *node); static void ExecSetupChildParentMapForTcs(ModifyTableState *mtstate); static void ExecSetupChildParentMapForSubplan(ModifyTableState *mtstate); @@ -81,7 +85,7 @@ static TupleConversionMap *tupconv_map_for_subplan(ModifyTableState *node, * The plan output is represented by its targetlist, because that makes * handling the dropped-column case easier. */ -void +static void ExecCheckPlanOutput(Relation resultRel, List *targetList) { TupleDesc resultDesc = RelationGetDescr(resultRel); @@ -255,12 +259,11 @@ ExecCheckTIDVisible(EState *estate, * Returns RETURNING result if any, otherwise NULL. * ---------------------------------------------------------------- */ -extern TupleTableSlot * +static TupleTableSlot * ExecInsert(ModifyTableState *mtstate, TupleTableSlot *slot, TupleTableSlot *planSlot, EState *estate, - MergeActionState *actionState, bool canSetTag) { HeapTuple tuple; @@ -387,17 +390,9 @@ ExecInsert(ModifyTableState *mtstate, * partition, we should instead check UPDATE policies, because we are * executing policies defined on the target table, and not those * defined on the child partitions. - * - * If we're running MERGE, we refer to the action that we're executing - * to know if we're doing an INSERT or UPDATE to a partition table. */ - if (mtstate->operation == CMD_UPDATE) - wco_kind = WCO_RLS_UPDATE_CHECK; - else if (mtstate->operation == CMD_MERGE) - wco_kind = (actionState->commandType == CMD_UPDATE) ? - WCO_RLS_UPDATE_CHECK : WCO_RLS_INSERT_CHECK; - else - wco_kind = WCO_RLS_INSERT_CHECK; + wco_kind = (mtstate->operation == CMD_UPDATE) ? + WCO_RLS_UPDATE_CHECK : WCO_RLS_INSERT_CHECK; /* * ExecWithCheckOptions() will skip any WCOs which are not of the kind @@ -622,19 +617,10 @@ ExecInsert(ModifyTableState *mtstate, * passed to foreign table triggers; it is NULL when the foreign * table has no relevant triggers. * - * MERGE passes actionState of the action it's currently executing; - * regular DELETE passes NULL. This is used by ExecDelete to know if it's - * being called from MERGE or regular DELETE operation. - * - * If the DELETE fails because the tuple is concurrently updated/deleted - * by this or some other transaction, hufdp is filled with the reason as - * well as other important information. Currently only MERGE needs this - * information. - * * Returns RETURNING result if any, otherwise NULL. * ---------------------------------------------------------------- */ -TupleTableSlot * +static TupleTableSlot * ExecDelete(ModifyTableState *mtstate, ItemPointer tupleid, HeapTuple oldtuple, @@ -643,8 +629,6 @@ ExecDelete(ModifyTableState *mtstate, EState *estate, bool *tupleDeleted, bool processReturning, - HeapUpdateFailureData *hufdp, - MergeActionState *actionState, bool canSetTag, bool changingPart) { @@ -659,14 +643,6 @@ ExecDelete(ModifyTableState *mtstate, *tupleDeleted = false; /* - * Initialize hufdp. Since the caller is only interested in the failure - * status, initialize with the state that is used to indicate successful - * operation. - */ - if (hufdp) - hufdp->result = HeapTupleMayBeUpdated; - - /* * get information on the (current) result relation */ resultRelInfo = estate->es_result_relation_info; @@ -679,7 +655,7 @@ ExecDelete(ModifyTableState *mtstate, bool dodelete; dodelete = ExecBRDeleteTriggers(estate, epqstate, resultRelInfo, - tupleid, oldtuple, hufdp); + tupleid, oldtuple); if (!dodelete) /* "do nothing" */ return NULL; @@ -747,15 +723,6 @@ ldelete:; true /* wait for commit */ , &hufd, changingPart); - - /* - * Copy the necessary information, if the caller has asked for it. We - * must do this irrespective of whether the tuple was updated or - * deleted. - */ - if (hufdp) - *hufdp = hufd; - switch (result) { case HeapTupleSelfUpdated: @@ -790,11 +757,7 @@ ldelete:; errmsg("tuple to be updated was already modified by an operation triggered by the current command"), errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows."))); - /* - * Else, already deleted by self; nothing to do but inform - * MERGE about it anyways so that it can take necessary - * action. - */ + /* Else, already deleted by self; nothing to do */ return NULL; case HeapTupleMayBeUpdated: @@ -814,19 +777,10 @@ ldelete:; { TupleTableSlot *epqslot; - /* - * If we're executing MERGE, then the onus of running - * EvalPlanQual() and handling its outcome lies with the - * caller. - */ - if (actionState != NULL) - return NULL; - - /* Normal DELETE path. */ epqslot = EvalPlanQual(estate, epqstate, resultRelationDesc, - GetEPQRangeTableIndex(resultRelInfo), + resultRelInfo->ri_RangeTableIndex, LockTupleExclusive, &hufd.ctid, hufd.xmax); @@ -836,12 +790,7 @@ ldelete:; goto ldelete; } } - - /* - * tuple already deleted; nothing to do. But MERGE might want - * to handle it differently. We've already filled-in hufdp - * with sufficient information for MERGE to look at. - */ + /* tuple already deleted; nothing to do */ return NULL; default: @@ -969,21 +918,10 @@ ldelete:; * foreign table triggers; it is NULL when the foreign table has * no relevant triggers. * - * MERGE passes actionState of the action it's currently executing; - * regular UPDATE passes NULL. This is used by ExecUpdate to know if it's - * being called from MERGE or regular UPDATE operation. ExecUpdate may - * pass this information to ExecInsert if it ends up running DELETE+INSERT - * for partition key updates. - * - * If the UPDATE fails because the tuple is concurrently updated/deleted - * by this or some other transaction, hufdp is filled with the reason as - * well as other important information. Currently only MERGE needs this - * information. - * * Returns RETURNING result if any, otherwise NULL. * ---------------------------------------------------------------- */ -extern TupleTableSlot * +static TupleTableSlot * ExecUpdate(ModifyTableState *mtstate, ItemPointer tupleid, HeapTuple oldtuple, @@ -991,9 +929,6 @@ ExecUpdate(ModifyTableState *mtstate, TupleTableSlot *planSlot, EPQState *epqstate, EState *estate, - bool *tuple_updated, - HeapUpdateFailureData *hufdp, - MergeActionState *actionState, bool canSetTag) { HeapTuple tuple; @@ -1010,17 +945,6 @@ ExecUpdate(ModifyTableState *mtstate, if (IsBootstrapProcessingMode()) elog(ERROR, "cannot UPDATE during bootstrap"); - if (tuple_updated) - *tuple_updated = false; - - /* - * Initialize hufdp. Since the caller is only interested in the failure - * status, initialize with the state that is used to indicate successful - * operation. - */ - if (hufdp) - hufdp->result = HeapTupleMayBeUpdated; - /* * get the heap tuple out of the tuple table slot, making sure we have a * writable copy @@ -1038,7 +962,7 @@ ExecUpdate(ModifyTableState *mtstate, resultRelInfo->ri_TrigDesc->trig_update_before_row) { slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo, - tupleid, oldtuple, slot, hufdp); + tupleid, oldtuple, slot); if (slot == NULL) /* "do nothing" */ return NULL; @@ -1084,6 +1008,7 @@ ExecUpdate(ModifyTableState *mtstate, } else { + LockTupleMode lockmode; bool partition_constraint_failed; /* @@ -1162,7 +1087,7 @@ lreplace:; * processing. We want to return rows from INSERT. */ ExecDelete(mtstate, tupleid, oldtuple, planSlot, epqstate, - estate, &tuple_deleted, false, hufdp, NULL, + estate, &tuple_deleted, false, false /* canSetTag */, true /* changingPart */); /* @@ -1199,36 +1124,16 @@ lreplace:; saved_tcs_map = mtstate->mt_transition_capture->tcs_map; /* - * We should convert the tuple into root's tuple descriptor, since - * ExecInsert() starts the search from root. To do that, we need to - * retrieve the tuple conversion map for this resultRelInfo. - * - * If we're running MERGE then resultRelInfo is per-partition - * resultRelInfo as initialized in ExecInitPartitionInfo(). Note - * that we don't expand inheritance for the resultRelation in case - * of MERGE and hence there is just one subplan. Whereas for - * regular UPDATE, resultRelInfo is one of the per-subplan - * resultRelInfos. In either case the position of this partition in - * tracked in ri_PartitionLeafIndex; - * - * Retrieve the map either by looking at the resultRelInfo's - * position in mtstate->resultRelInfo[] (for UPDATE) or by simply - * using the ri_PartitionLeafIndex value (for MERGE). + * resultRelInfo is one of the per-subplan resultRelInfos. So we + * should convert the tuple into root's tuple descriptor, since + * ExecInsert() starts the search from root. The tuple conversion + * map list is in the order of mtstate->resultRelInfo[], so to + * retrieve the one for this resultRel, we need to know the + * position of the resultRel in mtstate->resultRelInfo[]. */ - if (mtstate->operation == CMD_MERGE) - { - map_index = resultRelInfo->ri_PartitionLeafIndex; - Assert(mtstate->rootResultRelInfo == NULL); - tupconv_map = TupConvMapForLeaf(proute, - mtstate->resultRelInfo, - map_index); - } - else - { - map_index = resultRelInfo - mtstate->resultRelInfo; - Assert(map_index >= 0 && map_index < mtstate->mt_nplans); - tupconv_map = tupconv_map_for_subplan(mtstate, map_index); - } + map_index = resultRelInfo - mtstate->resultRelInfo; + Assert(map_index >= 0 && map_index < mtstate->mt_nplans); + tupconv_map = tupconv_map_for_subplan(mtstate, map_index); tuple = ConvertPartitionTupleSlot(tupconv_map, tuple, proute->root_tuple_slot, @@ -1238,16 +1143,12 @@ lreplace:; * Prepare for tuple routing, making it look like we're inserting * into the root. */ + Assert(mtstate->rootResultRelInfo != NULL); slot = ExecPrepareTupleRouting(mtstate, estate, proute, - getTargetResultRelInfo(mtstate), - slot); + mtstate->rootResultRelInfo, slot); ret_slot = ExecInsert(mtstate, slot, planSlot, - estate, actionState, canSetTag); - - /* Update is successful. */ - if (tuple_updated) - *tuple_updated = true; + estate, canSetTag); /* Revert ExecPrepareTupleRouting's node change. */ estate->es_result_relation_info = resultRelInfo; @@ -1285,16 +1186,7 @@ lreplace:; estate->es_output_cid, estate->es_crosscheck_snapshot, true /* wait for commit */ , - &hufd); - - /* - * Copy the necessary information, if the caller has asked for it. We - * must do this irrespective of whether the tuple was updated or - * deleted. - */ - if (hufdp) - *hufdp = hufd; - + &hufd, &lockmode); switch (result) { case HeapTupleSelfUpdated: @@ -1348,37 +1240,22 @@ lreplace:; { TupleTableSlot *epqslot; - /* - * If we're executing MERGE, then the onus of running - * EvalPlanQual() and handling its outcome lies with the - * caller. - */ - if (actionState != NULL) - return NULL; - - /* Regular UPDATE path. */ epqslot = EvalPlanQual(estate, epqstate, resultRelationDesc, - GetEPQRangeTableIndex(resultRelInfo), - hufd.lockmode, + resultRelInfo->ri_RangeTableIndex, + lockmode, &hufd.ctid, hufd.xmax); if (!TupIsNull(epqslot)) { *tupleid = hufd.ctid; - /* Normal UPDATE path */ slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot); tuple = ExecMaterializeSlot(slot); goto lreplace; } } - - /* - * tuple already deleted; nothing to do. But MERGE might want - * to handle it differently. We've already filled-in hufdp - * with sufficient information for MERGE to look at. - */ + /* tuple already deleted; nothing to do */ return NULL; default: @@ -1407,9 +1284,6 @@ lreplace:; estate, false, NULL, NIL); } - if (tuple_updated) - *tuple_updated = true; - if (canSetTag) (estate->es_processed)++; @@ -1504,9 +1378,9 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, * there's no historical behavior to break. * * It is the user's responsibility to prevent this situation from - * occurring. These problems are why SQL Standard similarly - * specifies that for SQL MERGE, an exception must be raised in - * the event of an attempt to update the same row twice. + * occurring. These problems are why SQL-2003 similarly specifies + * that for SQL MERGE, an exception must be raised in the event of + * an attempt to update the same row twice. */ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple.t_data))) ereport(ERROR, @@ -1636,7 +1510,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, *returning = ExecUpdate(mtstate, &tuple.t_self, NULL, mtstate->mt_conflproj, planSlot, &mtstate->mt_epqstate, mtstate->ps.state, - NULL, NULL, NULL, canSetTag); + canSetTag); ReleaseBuffer(buffer); return true; @@ -1674,14 +1548,6 @@ fireBSTriggers(ModifyTableState *node) case CMD_DELETE: ExecBSDeleteTriggers(node->ps.state, resultRelInfo); break; - case CMD_MERGE: - if (node->mt_merge_subcommands & MERGE_INSERT) - ExecBSInsertTriggers(node->ps.state, resultRelInfo); - if (node->mt_merge_subcommands & MERGE_UPDATE) - ExecBSUpdateTriggers(node->ps.state, resultRelInfo); - if (node->mt_merge_subcommands & MERGE_DELETE) - ExecBSDeleteTriggers(node->ps.state, resultRelInfo); - break; default: elog(ERROR, "unknown operation"); break; @@ -1737,17 +1603,6 @@ fireASTriggers(ModifyTableState *node) ExecASDeleteTriggers(node->ps.state, resultRelInfo, node->mt_transition_capture); break; - case CMD_MERGE: - if (node->mt_merge_subcommands & MERGE_DELETE) - ExecASDeleteTriggers(node->ps.state, resultRelInfo, - node->mt_transition_capture); - if (node->mt_merge_subcommands & MERGE_UPDATE) - ExecASUpdateTriggers(node->ps.state, resultRelInfo, - node->mt_transition_capture); - if (node->mt_merge_subcommands & MERGE_INSERT) - ExecASInsertTriggers(node->ps.state, resultRelInfo, - node->mt_transition_capture); - break; default: elog(ERROR, "unknown operation"); break; @@ -1810,7 +1665,7 @@ ExecSetupTransitionCaptureState(ModifyTableState *mtstate, EState *estate) * * Returns a slot holding the tuple of the partition rowtype. */ -TupleTableSlot * +static TupleTableSlot * ExecPrepareTupleRouting(ModifyTableState *mtstate, EState *estate, PartitionTupleRouting *proute, @@ -2143,7 +1998,6 @@ ExecModifyTable(PlanState *pstate) { /* advance to next subplan if any */ node->mt_whichplan++; - if (node->mt_whichplan < node->mt_nplans) { resultRelInfo++; @@ -2192,12 +2046,6 @@ ExecModifyTable(PlanState *pstate) EvalPlanQualSetSlot(&node->mt_epqstate, planSlot); slot = planSlot; - if (operation == CMD_MERGE) - { - ExecMerge(node, estate, slot, junkfilter, resultRelInfo); - continue; - } - tupleid = NULL; oldtuple = NULL; if (junkfilter != NULL) @@ -2279,20 +2127,19 @@ ExecModifyTable(PlanState *pstate) slot = ExecPrepareTupleRouting(node, estate, proute, resultRelInfo, slot); slot = ExecInsert(node, slot, planSlot, - estate, NULL, node->canSetTag); + estate, node->canSetTag); /* Revert ExecPrepareTupleRouting's state change. */ if (proute) estate->es_result_relation_info = resultRelInfo; break; case CMD_UPDATE: slot = ExecUpdate(node, tupleid, oldtuple, slot, planSlot, - &node->mt_epqstate, estate, - NULL, NULL, NULL, node->canSetTag); + &node->mt_epqstate, estate, node->canSetTag); break; case CMD_DELETE: slot = ExecDelete(node, tupleid, oldtuple, planSlot, &node->mt_epqstate, estate, - NULL, true, NULL, NULL, node->canSetTag, + NULL, true, node->canSetTag, false /* changingPart */); break; default: @@ -2383,16 +2230,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) saved_resultRelInfo = estate->es_result_relation_info; resultRelInfo = mtstate->resultRelInfo; - - /* - * mergeTargetRelation must be set if we're running MERGE and mustn't be - * set if we're not. - */ - Assert(operation != CMD_MERGE || node->mergeTargetRelation > 0); - Assert(operation == CMD_MERGE || node->mergeTargetRelation == 0); - - resultRelInfo->ri_mergeTargetRTI = node->mergeTargetRelation; - i = 0; foreach(l, node->plans) { @@ -2471,8 +2308,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) * partition key. */ if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && - (operation == CMD_INSERT || operation == CMD_MERGE || - update_tuple_routing_needed)) + (operation == CMD_INSERT || update_tuple_routing_needed)) mtstate->mt_partition_tuple_routing = ExecSetupPartitionTupleRouting(mtstate, rel); @@ -2484,15 +2320,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) ExecSetupTransitionCaptureState(mtstate, estate); /* - * If we are doing MERGE then setup child-parent mapping. This will be - * required in case we end up doing a partition-key update, triggering a - * tuple routing. - */ - if (mtstate->operation == CMD_MERGE && - mtstate->mt_partition_tuple_routing != NULL) - ExecSetupChildParentMapForLeaf(mtstate->mt_partition_tuple_routing); - - /* * Construct mapping from each of the per-subplan partition attnos to the * root attno. This is required when during update row movement the tuple * descriptor of a source partition does not match the root partitioned @@ -2684,10 +2511,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) } } - resultRelInfo = mtstate->resultRelInfo; - if (mtstate->operation == CMD_MERGE) - ExecInitMerge(mtstate, estate, resultRelInfo); - /* select first subplan */ mtstate->mt_whichplan = 0; subplan = (Plan *) linitial(node->plans); @@ -2701,7 +2524,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) * --- no need to look first. Typically, this will be a 'ctid' or * 'wholerow' attribute, but in the case of a foreign data wrapper it * might be a set of junk attributes sufficient to identify the remote - * row. We follow this logic for MERGE, so it always has a junk attributes. + * row. * * If there are multiple result relations, each one needs its own junk * filter. Note multiple rels are only possible for UPDATE/DELETE, so we @@ -2729,7 +2552,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) break; case CMD_UPDATE: case CMD_DELETE: - case CMD_MERGE: junk_filter_needed = true; break; default: @@ -2745,7 +2567,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) JunkFilter *j; subplan = mtstate->mt_plans[i]->plan; - if (operation == CMD_INSERT || operation == CMD_UPDATE) ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc, subplan->targetlist); @@ -2754,9 +2575,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) resultRelInfo->ri_RelationDesc->rd_att->tdhasoid, ExecInitExtraTupleSlot(estate, NULL)); - if (operation == CMD_UPDATE || - operation == CMD_DELETE || - operation == CMD_MERGE) + if (operation == CMD_UPDATE || operation == CMD_DELETE) { /* For UPDATE/DELETE, find the appropriate junk attr now */ char relkind; @@ -2769,15 +2588,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) j->jf_junkAttNo = ExecFindJunkAttribute(j, "ctid"); if (!AttributeNumberIsValid(j->jf_junkAttNo)) elog(ERROR, "could not find junk ctid column"); - - if (operation == CMD_MERGE && - relkind == RELKIND_PARTITIONED_TABLE) - { - j->jf_otherJunkAttNo = ExecFindJunkAttribute(j, "tableoid"); - if (!AttributeNumberIsValid(j->jf_otherJunkAttNo)) - elog(ERROR, "could not find junk tableoid column"); - - } } else if (relkind == RELKIND_FOREIGN_TABLE) { diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index a49015e7cbc..08f6f67a15c 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -2420,9 +2420,6 @@ _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, uint64 tcount) else res = SPI_OK_UPDATE; break; - case CMD_MERGE: - res = SPI_OK_MERGE; - break; default: return SPI_ERROR_OPUNKNOWN; } diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index b856fe29b59..7c045a7afef 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -207,7 +207,6 @@ _copyModifyTable(const ModifyTable *from) COPY_NODE_FIELD(partitioned_rels); COPY_SCALAR_FIELD(partColsUpdated); COPY_NODE_FIELD(resultRelations); - COPY_SCALAR_FIELD(mergeTargetRelation); COPY_SCALAR_FIELD(resultRelIndex); COPY_SCALAR_FIELD(rootResultRelIndex); COPY_NODE_FIELD(plans); @@ -223,8 +222,6 @@ _copyModifyTable(const ModifyTable *from) COPY_NODE_FIELD(onConflictWhere); COPY_SCALAR_FIELD(exclRelRTI); COPY_NODE_FIELD(exclRelTlist); - COPY_NODE_FIELD(mergeSourceTargetList); - COPY_NODE_FIELD(mergeActionList); return newnode; } @@ -2137,20 +2134,6 @@ _copyOnConflictExpr(const OnConflictExpr *from) return newnode; } -static MergeAction * -_copyMergeAction(const MergeAction *from) -{ - MergeAction *newnode = makeNode(MergeAction); - - COPY_SCALAR_FIELD(matched); - COPY_SCALAR_FIELD(commandType); - COPY_SCALAR_FIELD(override); - COPY_NODE_FIELD(qual); - COPY_NODE_FIELD(targetList); - - return newnode; -} - /* * _copyPartitionPruneStepOp */ @@ -3030,9 +3013,6 @@ _copyQuery(const Query *from) COPY_NODE_FIELD(setOperations); COPY_NODE_FIELD(constraintDeps); COPY_NODE_FIELD(withCheckOptions); - COPY_SCALAR_FIELD(mergeTarget_relation); - COPY_NODE_FIELD(mergeSourceTargetList); - COPY_NODE_FIELD(mergeActionList); COPY_LOCATION_FIELD(stmt_location); COPY_LOCATION_FIELD(stmt_len); @@ -3096,35 +3076,6 @@ _copyUpdateStmt(const UpdateStmt *from) return newnode; } -static MergeStmt * -_copyMergeStmt(const MergeStmt *from) -{ - MergeStmt *newnode = makeNode(MergeStmt); - - COPY_NODE_FIELD(relation); - COPY_NODE_FIELD(source_relation); - COPY_NODE_FIELD(join_condition); - COPY_NODE_FIELD(mergeWhenClauses); - COPY_NODE_FIELD(withClause); - - return newnode; -} - -static MergeWhenClause * -_copyMergeWhenClause(const MergeWhenClause *from) -{ - MergeWhenClause *newnode = makeNode(MergeWhenClause); - - COPY_SCALAR_FIELD(matched); - COPY_SCALAR_FIELD(commandType); - COPY_NODE_FIELD(condition); - COPY_NODE_FIELD(targetList); - COPY_NODE_FIELD(cols); - COPY_NODE_FIELD(values); - COPY_SCALAR_FIELD(override); - return newnode; -} - static SelectStmt * _copySelectStmt(const SelectStmt *from) { @@ -5110,9 +5061,6 @@ copyObjectImpl(const void *from) case T_OnConflictExpr: retval = _copyOnConflictExpr(from); break; - case T_MergeAction: - retval = _copyMergeAction(from); - break; case T_PartitionPruneStepOp: retval = _copyPartitionPruneStepOp(from); break; @@ -5197,12 +5145,6 @@ copyObjectImpl(const void *from) case T_UpdateStmt: retval = _copyUpdateStmt(from); break; - case T_MergeStmt: - retval = _copyMergeStmt(from); - break; - case T_MergeWhenClause: - retval = _copyMergeWhenClause(from); - break; case T_SelectStmt: retval = _copySelectStmt(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 39946959afd..6a971d0141a 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -812,18 +812,6 @@ _equalOnConflictExpr(const OnConflictExpr *a, const OnConflictExpr *b) return true; } - -static bool -_equalMergeAction(const MergeAction *a, const MergeAction *b) -{ - COMPARE_SCALAR_FIELD(matched); - COMPARE_SCALAR_FIELD(commandType); - COMPARE_SCALAR_FIELD(override); - COMPARE_NODE_FIELD(qual); - COMPARE_NODE_FIELD(targetList); - - return true; -} /* * Stuff from relation.h */ @@ -989,8 +977,6 @@ _equalQuery(const Query *a, const Query *b) COMPARE_NODE_FIELD(setOperations); COMPARE_NODE_FIELD(constraintDeps); COMPARE_NODE_FIELD(withCheckOptions); - COMPARE_NODE_FIELD(mergeSourceTargetList); - COMPARE_NODE_FIELD(mergeActionList); COMPARE_LOCATION_FIELD(stmt_location); COMPARE_LOCATION_FIELD(stmt_len); @@ -1047,32 +1033,6 @@ _equalUpdateStmt(const UpdateStmt *a, const UpdateStmt *b) } static bool -_equalMergeStmt(const MergeStmt *a, const MergeStmt *b) -{ - COMPARE_NODE_FIELD(relation); - COMPARE_NODE_FIELD(source_relation); - COMPARE_NODE_FIELD(join_condition); - COMPARE_NODE_FIELD(mergeWhenClauses); - COMPARE_NODE_FIELD(withClause); - - return true; -} - -static bool -_equalMergeWhenClause(const MergeWhenClause *a, const MergeWhenClause *b) -{ - COMPARE_SCALAR_FIELD(matched); - COMPARE_SCALAR_FIELD(commandType); - COMPARE_NODE_FIELD(condition); - COMPARE_NODE_FIELD(targetList); - COMPARE_NODE_FIELD(cols); - COMPARE_NODE_FIELD(values); - COMPARE_SCALAR_FIELD(override); - - return true; -} - -static bool _equalSelectStmt(const SelectStmt *a, const SelectStmt *b) { COMPARE_NODE_FIELD(distinctClause); @@ -3197,9 +3157,6 @@ equal(const void *a, const void *b) case T_OnConflictExpr: retval = _equalOnConflictExpr(a, b); break; - case T_MergeAction: - retval = _equalMergeAction(a, b); - break; case T_JoinExpr: retval = _equalJoinExpr(a, b); break; @@ -3265,12 +3222,6 @@ equal(const void *a, const void *b) case T_UpdateStmt: retval = _equalUpdateStmt(a, b); break; - case T_MergeStmt: - retval = _equalMergeStmt(a, b); - break; - case T_MergeWhenClause: - retval = _equalMergeWhenClause(a, b); - break; case T_SelectStmt: retval = _equalSelectStmt(a, b); break; diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index 4c309d236a3..a10014f755b 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -2146,16 +2146,6 @@ expression_tree_walker(Node *node, return true; } break; - case T_MergeAction: - { - MergeAction *action = (MergeAction *) node; - - if (walker(action->targetList, context)) - return true; - if (walker(action->qual, context)) - return true; - } - break; case T_PartitionPruneStepOp: { PartitionPruneStepOp *opstep = (PartitionPruneStepOp *) node; @@ -2276,10 +2266,6 @@ query_tree_walker(Query *query, return true; if (walker((Node *) query->onConflict, context)) return true; - if (walker((Node *) query->mergeSourceTargetList, context)) - return true; - if (walker((Node *) query->mergeActionList, context)) - return true; if (walker((Node *) query->returningList, context)) return true; if (walker((Node *) query->jointree, context)) @@ -2957,18 +2943,6 @@ expression_tree_mutator(Node *node, return (Node *) newnode; } break; - case T_MergeAction: - { - MergeAction *action = (MergeAction *) node; - MergeAction *newnode; - - FLATCOPY(newnode, action, MergeAction); - MUTATE(newnode->qual, action->qual, Node *); - MUTATE(newnode->targetList, action->targetList, List *); - - return (Node *) newnode; - } - break; case T_PartitionPruneStepOp: { PartitionPruneStepOp *opstep = (PartitionPruneStepOp *) node; @@ -3134,8 +3108,6 @@ query_tree_mutator(Query *query, MUTATE(query->targetList, query->targetList, List *); MUTATE(query->withCheckOptions, query->withCheckOptions, List *); MUTATE(query->onConflict, query->onConflict, OnConflictExpr *); - MUTATE(query->mergeSourceTargetList, query->mergeSourceTargetList, List *); - MUTATE(query->mergeActionList, query->mergeActionList, List *); MUTATE(query->returningList, query->returningList, List *); MUTATE(query->jointree, query->jointree, FromExpr *); MUTATE(query->setOperations, query->setOperations, Node *); @@ -3277,9 +3249,9 @@ query_or_expression_tree_mutator(Node *node, * boundaries: we descend to everything that's possibly interesting. * * Currently, the node type coverage here extends only to DML statements - * (SELECT/INSERT/UPDATE/DELETE/MERGE) and nodes that can appear in them, - * because this is used mainly during analysis of CTEs, and only DML - * statements can appear in CTEs. + * (SELECT/INSERT/UPDATE/DELETE) and nodes that can appear in them, because + * this is used mainly during analysis of CTEs, and only DML statements can + * appear in CTEs. */ bool raw_expression_tree_walker(Node *node, @@ -3459,36 +3431,6 @@ raw_expression_tree_walker(Node *node, return true; } break; - case T_MergeStmt: - { - MergeStmt *stmt = (MergeStmt *) node; - - if (walker(stmt->relation, context)) - return true; - if (walker(stmt->source_relation, context)) - return true; - if (walker(stmt->join_condition, context)) - return true; - if (walker(stmt->mergeWhenClauses, context)) - return true; - if (walker(stmt->withClause, context)) - return true; - } - break; - case T_MergeWhenClause: - { - MergeWhenClause *mergeWhenClause = (MergeWhenClause *) node; - - if (walker(mergeWhenClause->condition, context)) - return true; - if (walker(mergeWhenClause->targetList, context)) - return true; - if (walker(mergeWhenClause->cols, context)) - return true; - if (walker(mergeWhenClause->values, context)) - return true; - } - break; case T_SelectStmt: { SelectStmt *stmt = (SelectStmt *) node; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index ef626b34b3d..3991a0ce83a 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -375,7 +375,6 @@ _outModifyTable(StringInfo str, const ModifyTable *node) WRITE_NODE_FIELD(partitioned_rels); WRITE_BOOL_FIELD(partColsUpdated); WRITE_NODE_FIELD(resultRelations); - WRITE_INT_FIELD(mergeTargetRelation); WRITE_INT_FIELD(resultRelIndex); WRITE_INT_FIELD(rootResultRelIndex); WRITE_NODE_FIELD(plans); @@ -391,22 +390,6 @@ _outModifyTable(StringInfo str, const ModifyTable *node) WRITE_NODE_FIELD(onConflictWhere); WRITE_UINT_FIELD(exclRelRTI); WRITE_NODE_FIELD(exclRelTlist); - WRITE_NODE_FIELD(mergeSourceTargetList); - WRITE_NODE_FIELD(mergeActionList); -} - -static void -_outMergeWhenClause(StringInfo str, const MergeWhenClause *node) -{ - WRITE_NODE_TYPE("MERGEWHENCLAUSE"); - - WRITE_BOOL_FIELD(matched); - WRITE_ENUM_FIELD(commandType, CmdType); - WRITE_NODE_FIELD(condition); - WRITE_NODE_FIELD(targetList); - WRITE_NODE_FIELD(cols); - WRITE_NODE_FIELD(values); - WRITE_ENUM_FIELD(override, OverridingKind); } static void @@ -1749,17 +1732,6 @@ _outOnConflictExpr(StringInfo str, const OnConflictExpr *node) } static void -_outMergeAction(StringInfo str, const MergeAction *node) -{ - WRITE_NODE_TYPE("MERGEACTION"); - - WRITE_BOOL_FIELD(matched); - WRITE_ENUM_FIELD(commandType, CmdType); - WRITE_NODE_FIELD(qual); - WRITE_NODE_FIELD(targetList); -} - -static void _outPartitionPruneInfo(StringInfo str, const PartitionPruneInfo *node) { int i; @@ -2189,7 +2161,6 @@ _outModifyTablePath(StringInfo str, const ModifyTablePath *node) WRITE_NODE_FIELD(partitioned_rels); WRITE_BOOL_FIELD(partColsUpdated); WRITE_NODE_FIELD(resultRelations); - WRITE_INT_FIELD(mergeTargetRelation); WRITE_NODE_FIELD(subpaths); WRITE_NODE_FIELD(subroots); WRITE_NODE_FIELD(withCheckOptionLists); @@ -2197,8 +2168,6 @@ _outModifyTablePath(StringInfo str, const ModifyTablePath *node) WRITE_NODE_FIELD(rowMarks); WRITE_NODE_FIELD(onconflict); WRITE_INT_FIELD(epqParam); - WRITE_NODE_FIELD(mergeSourceTargetList); - WRITE_NODE_FIELD(mergeActionList); } static void @@ -3012,9 +2981,6 @@ _outQuery(StringInfo str, const Query *node) WRITE_NODE_FIELD(setOperations); WRITE_NODE_FIELD(constraintDeps); /* withCheckOptions intentionally omitted, see comment in parsenodes.h */ - WRITE_INT_FIELD(mergeTarget_relation); - WRITE_NODE_FIELD(mergeSourceTargetList); - WRITE_NODE_FIELD(mergeActionList); WRITE_LOCATION_FIELD(stmt_location); WRITE_LOCATION_FIELD(stmt_len); } @@ -3733,9 +3699,6 @@ outNode(StringInfo str, const void *obj) case T_ModifyTable: _outModifyTable(str, obj); break; - case T_MergeWhenClause: - _outMergeWhenClause(str, obj); - break; case T_Append: _outAppend(str, obj); break; @@ -4012,9 +3975,6 @@ outNode(StringInfo str, const void *obj) case T_OnConflictExpr: _outOnConflictExpr(str, obj); break; - case T_MergeAction: - _outMergeAction(str, obj); - break; case T_PartitionPruneStepOp: _outPartitionPruneStepOp(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 93785e3bdfd..c466b98102e 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -270,9 +270,6 @@ _readQuery(void) READ_NODE_FIELD(setOperations); READ_NODE_FIELD(constraintDeps); /* withCheckOptions intentionally omitted, see comment in parsenodes.h */ - READ_INT_FIELD(mergeTarget_relation); - READ_NODE_FIELD(mergeSourceTargetList); - READ_NODE_FIELD(mergeActionList); READ_LOCATION_FIELD(stmt_location); READ_LOCATION_FIELD(stmt_len); @@ -1357,22 +1354,6 @@ _readPartitionPruneStepCombine(void) READ_DONE(); } -/* - * _readMergeAction - */ -static MergeAction * -_readMergeAction(void) -{ - READ_LOCALS(MergeAction); - - READ_BOOL_FIELD(matched); - READ_ENUM_FIELD(commandType, CmdType); - READ_NODE_FIELD(qual); - READ_NODE_FIELD(targetList); - - READ_DONE(); -} - static PartitionPruneInfo * _readPartitionPruneInfo(void) { @@ -1638,7 +1619,6 @@ _readModifyTable(void) READ_NODE_FIELD(partitioned_rels); READ_BOOL_FIELD(partColsUpdated); READ_NODE_FIELD(resultRelations); - READ_INT_FIELD(mergeTargetRelation); READ_INT_FIELD(resultRelIndex); READ_INT_FIELD(rootResultRelIndex); READ_NODE_FIELD(plans); @@ -1654,27 +1634,6 @@ _readModifyTable(void) READ_NODE_FIELD(onConflictWhere); READ_UINT_FIELD(exclRelRTI); READ_NODE_FIELD(exclRelTlist); - READ_NODE_FIELD(mergeSourceTargetList); - READ_NODE_FIELD(mergeActionList); - - READ_DONE(); -} - -/* - * _readMergeWhenClause - */ -static MergeWhenClause * -_readMergeWhenClause(void) -{ - READ_LOCALS(MergeWhenClause); - - READ_BOOL_FIELD(matched); - READ_ENUM_FIELD(commandType, CmdType); - READ_NODE_FIELD(condition); - READ_NODE_FIELD(targetList); - READ_NODE_FIELD(cols); - READ_NODE_FIELD(values); - READ_ENUM_FIELD(override, OverridingKind); READ_DONE(); } @@ -2657,8 +2616,6 @@ parseNodeString(void) return_value = _readFromExpr(); else if (MATCH("ONCONFLICTEXPR", 14)) return_value = _readOnConflictExpr(); - else if (MATCH("MERGEACTION", 11)) - return_value = _readMergeAction(); else if (MATCH("PARTITIONPRUNESTEPOP", 20)) return_value = _readPartitionPruneStepOp(); else if (MATCH("PARTITIONPRUNESTEPCOMBINE", 25)) @@ -2687,8 +2644,6 @@ parseNodeString(void) return_value = _readProjectSet(); else if (MATCH("MODIFYTABLE", 11)) return_value = _readModifyTable(); - else if (MATCH("MERGEWHENCLAUSE", 15)) - return_value = _readMergeWhenClause(); else if (MATCH("APPEND", 6)) return_value = _readAppend(); else if (MATCH("MERGEAPPEND", 11)) diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 048bdf4ad1e..b6ad01be6bb 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -289,13 +289,9 @@ static ModifyTable *make_modifytable(PlannerInfo *root, CmdType operation, bool canSetTag, Index nominalRelation, List *partitioned_rels, bool partColsUpdated, - List *resultRelations, - Index mergeTargetRelation, - List *subplans, + List *resultRelations, List *subplans, List *withCheckOptionLists, List *returningLists, - List *rowMarks, OnConflictExpr *onconflict, - List *mergeSourceTargetList, - List *mergeActionList, int epqParam); + List *rowMarks, OnConflictExpr *onconflict, int epqParam); static GatherMerge *create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path); @@ -2486,14 +2482,11 @@ create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path) best_path->partitioned_rels, best_path->partColsUpdated, best_path->resultRelations, - best_path->mergeTargetRelation, subplans, best_path->withCheckOptionLists, best_path->returningLists, best_path->rowMarks, best_path->onconflict, - best_path->mergeSourceTargetList, - best_path->mergeActionList, best_path->epqParam); copy_generic_path_info(&plan->plan, &best_path->path); @@ -6561,13 +6554,9 @@ make_modifytable(PlannerInfo *root, CmdType operation, bool canSetTag, Index nominalRelation, List *partitioned_rels, bool partColsUpdated, - List *resultRelations, - Index mergeTargetRelation, - List *subplans, + List *resultRelations, List *subplans, List *withCheckOptionLists, List *returningLists, - List *rowMarks, OnConflictExpr *onconflict, - List *mergeSourceTargetList, - List *mergeActionList, int epqParam) + List *rowMarks, OnConflictExpr *onconflict, int epqParam) { ModifyTable *node = makeNode(ModifyTable); List *fdw_private_list; @@ -6593,7 +6582,6 @@ make_modifytable(PlannerInfo *root, node->partitioned_rels = partitioned_rels; node->partColsUpdated = partColsUpdated; node->resultRelations = resultRelations; - node->mergeTargetRelation = mergeTargetRelation; node->resultRelIndex = -1; /* will be set correctly in setrefs.c */ node->rootResultRelIndex = -1; /* will be set correctly in setrefs.c */ node->plans = subplans; @@ -6626,8 +6614,6 @@ make_modifytable(PlannerInfo *root, node->withCheckOptionLists = withCheckOptionLists; node->returningLists = returningLists; node->rowMarks = rowMarks; - node->mergeSourceTargetList = mergeSourceTargetList; - node->mergeActionList = mergeActionList; node->epqParam = epqParam; /* diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 20f49e5d432..2e298f83573 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -794,24 +794,6 @@ subquery_planner(PlannerGlobal *glob, Query *parse, /* exclRelTlist contains only Vars, so no preprocessing needed */ } - foreach(l, parse->mergeActionList) - { - MergeAction *action = (MergeAction *) lfirst(l); - - action->targetList = (List *) - preprocess_expression(root, - (Node *) action->targetList, - EXPRKIND_TARGET); - action->qual = - preprocess_expression(root, - (Node *) action->qual, - EXPRKIND_QUAL); - } - - parse->mergeSourceTargetList = (List *) - preprocess_expression(root, (Node *) parse->mergeSourceTargetList, - EXPRKIND_TARGET); - root->append_rel_list = (List *) preprocess_expression(root, (Node *) root->append_rel_list, EXPRKIND_APPINFO); @@ -1564,7 +1546,6 @@ inheritance_planner(PlannerInfo *root) subroot->parse->returningList); Assert(!parse->onConflict); - Assert(parse->mergeActionList == NIL); } /* Result path must go into outer query's FINAL upperrel */ @@ -1638,15 +1619,12 @@ inheritance_planner(PlannerInfo *root) partitioned_rels, root->partColsUpdated, resultRelations, - 0, subpaths, subroots, withCheckOptionLists, returningLists, rowMarks, NULL, - NULL, - NULL, SS_assign_special_param(root))); } @@ -2177,8 +2155,8 @@ grouping_planner(PlannerInfo *root, bool inheritance_update, } /* - * If this is an INSERT/UPDATE/DELETE/MERGE, and we're not being - * called from inheritance_planner, add the ModifyTable node. + * If this is an INSERT/UPDATE/DELETE, and we're not being called from + * inheritance_planner, add the ModifyTable node. */ if (parse->commandType != CMD_SELECT && !inheritance_update) { @@ -2218,15 +2196,12 @@ grouping_planner(PlannerInfo *root, bool inheritance_update, NIL, false, list_make1_int(parse->resultRelation), - parse->mergeTarget_relation, list_make1(path), list_make1(root), withCheckOptionLists, returningLists, rowMarks, parse->onConflict, - parse->mergeSourceTargetList, - parse->mergeActionList, SS_assign_special_param(root)); } diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 833a92f5387..69dd327f0c9 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -851,60 +851,6 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset) fix_scan_list(root, splan->exclRelTlist, rtoffset); } - /* - * The MERGE statement produces the target rows by performing a - * right join between the target relation and the source - * relation (which could be a plain relation or a subquery). - * The INSERT and UPDATE actions of the MERGE statement - * requires access to the columns from the source relation. We - * arrange things so that the source relation attributes are - * available as INNER_VAR and the target relation attributes - * are available from the scan tuple. - */ - if (splan->mergeActionList != NIL) - { - /* - * mergeSourceTargetList is already setup correctly to - * include all Vars coming from the source relation. So we - * fix the targetList of individual action nodes by - * ensuring that the source relation Vars are referenced - * as INNER_VAR. Note that for this to work correctly, - * during execution, the ecxt_innertuple must be set to - * the tuple obtained from the source relation. - * - * We leave the Vars from the result relation (i.e. the - * target relation) unchanged i.e. those Vars would be - * picked from the scan slot. So during execution, we must - * ensure that ecxt_scantuple is setup correctly to refer - * to the tuple from the target relation. - */ - - indexed_tlist *itlist; - - itlist = build_tlist_index(splan->mergeSourceTargetList); - - splan->mergeTargetRelation += rtoffset; - - foreach(l, splan->mergeActionList) - { - MergeAction *action = (MergeAction *) lfirst(l); - - /* Fix targetList of each action. */ - action->targetList = fix_join_expr(root, - action->targetList, - NULL, itlist, - linitial_int(splan->resultRelations), - rtoffset); - - /* Fix quals too. */ - action->qual = (Node *) fix_join_expr(root, - (List *) action->qual, - NULL, itlist, - linitial_int(splan->resultRelations), - rtoffset); - } - } - splan->nominalRelation += rtoffset; splan->exclRelRTI += rtoffset; diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c index 8a87cfd14ae..8603feef2b8 100644 --- a/src/backend/optimizer/prep/preptlist.c +++ b/src/backend/optimizer/prep/preptlist.c @@ -118,46 +118,6 @@ preprocess_targetlist(PlannerInfo *root) tlist = expand_targetlist(tlist, command_type, result_relation, target_relation); - if (command_type == CMD_MERGE) - { - ListCell *l; - - /* - * For MERGE, add any junk column(s) needed to allow the executor to - * identify the rows to be updated or deleted, with different - * handling for partitioned tables. - */ - rewriteTargetListMerge(parse, target_relation); - - /* - * For MERGE command, handle targetlist of each MergeAction separately. - * Give the same treatment to MergeAction->targetList as we would have - * given to a regular INSERT/UPDATE/DELETE. - */ - foreach(l, parse->mergeActionList) - { - MergeAction *action = (MergeAction *) lfirst(l); - - switch (action->commandType) - { - case CMD_INSERT: - case CMD_UPDATE: - action->targetList = expand_targetlist(action->targetList, - action->commandType, - result_relation, - target_relation); - break; - case CMD_DELETE: - break; - case CMD_NOTHING: - break; - default: - elog(ERROR, "unknown action in MERGE WHEN clause"); - - } - } - } - /* * Add necessary junk columns for rowmarked rels. These values are needed * for locking of rels selected FOR UPDATE/SHARE, and to do EvalPlanQual @@ -388,7 +348,6 @@ expand_targetlist(List *tlist, int command_type, true /* byval */ ); } break; - case CMD_MERGE: case CMD_UPDATE: if (!att_tup->attisdropped) { diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index bd9442c22d6..776c592dbe7 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -3302,21 +3302,17 @@ create_lockrows_path(PlannerInfo *root, RelOptInfo *rel, * 'rowMarks' is a list of PlanRowMarks (non-locking only) * 'onconflict' is the ON CONFLICT clause, or NULL * 'epqParam' is the ID of Param for EvalPlanQual re-eval - * 'mergeActionList' is a list of MERGE actions */ ModifyTablePath * create_modifytable_path(PlannerInfo *root, RelOptInfo *rel, CmdType operation, bool canSetTag, Index nominalRelation, List *partitioned_rels, bool partColsUpdated, - List *resultRelations, - Index mergeTargetRelation, - List *subpaths, + List *resultRelations, List *subpaths, List *subroots, List *withCheckOptionLists, List *returningLists, List *rowMarks, OnConflictExpr *onconflict, - List *mergeSourceTargetList, - List *mergeActionList, int epqParam) + int epqParam) { ModifyTablePath *pathnode = makeNode(ModifyTablePath); double total_size; @@ -3381,7 +3377,6 @@ create_modifytable_path(PlannerInfo *root, RelOptInfo *rel, pathnode->partitioned_rels = list_copy(partitioned_rels); pathnode->partColsUpdated = partColsUpdated; pathnode->resultRelations = resultRelations; - pathnode->mergeTargetRelation = mergeTargetRelation; pathnode->subpaths = subpaths; pathnode->subroots = subroots; pathnode->withCheckOptionLists = withCheckOptionLists; @@ -3389,8 +3384,6 @@ create_modifytable_path(PlannerInfo *root, RelOptInfo *rel, pathnode->rowMarks = rowMarks; pathnode->onconflict = onconflict; pathnode->epqParam = epqParam; - pathnode->mergeSourceTargetList = mergeSourceTargetList; - pathnode->mergeActionList = mergeActionList; return pathnode; } diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 90bb0c28041..04ee5f20153 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -1851,10 +1851,6 @@ has_row_triggers(PlannerInfo *root, Index rti, CmdType event) trigDesc->trig_delete_before_row)) result = true; break; - /* There is no separate event for MERGE, only INSERT/UPDATE/DELETE */ - case CMD_MERGE: - result = false; - break; default: elog(ERROR, "unrecognized CmdType: %d", (int) event); break; diff --git a/src/backend/parser/Makefile b/src/backend/parser/Makefile index 95fdf0b9732..f14febdbda0 100644 --- a/src/backend/parser/Makefile +++ b/src/backend/parser/Makefile @@ -14,7 +14,7 @@ override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS) OBJS= analyze.o gram.o scan.o parser.o \ parse_agg.o parse_clause.o parse_coerce.o parse_collate.o parse_cte.o \ - parse_enr.o parse_expr.o parse_func.o parse_merge.o parse_node.o parse_oper.o \ + parse_enr.o parse_expr.o parse_func.o parse_node.o parse_oper.o \ parse_param.o parse_relation.o parse_target.o parse_type.o \ parse_utilcmd.o scansup.o diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 606021bc94f..0c66ea1dfce 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -38,7 +38,6 @@ #include "parser/parse_cte.h" #include "parser/parse_expr.h" #include "parser/parse_func.h" -#include "parser/parse_merge.h" #include "parser/parse_oper.h" #include "parser/parse_param.h" #include "parser/parse_relation.h" @@ -54,6 +53,9 @@ post_parse_analyze_hook_type post_parse_analyze_hook = NULL; static Query *transformOptionalSelectInto(ParseState *pstate, Node *parseTree); static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt); static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt); +static List *transformInsertRow(ParseState *pstate, List *exprlist, + List *stmtcols, List *icolumns, List *attrnos, + bool strip_indirection); static OnConflictExpr *transformOnConflictClause(ParseState *pstate, OnConflictClause *onConflictClause); static int count_rowexpr_columns(ParseState *pstate, Node *expr); @@ -66,6 +68,8 @@ static void determineRecursiveColTypes(ParseState *pstate, Node *larg, List *nrtargetlist); static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt); static List *transformReturningList(ParseState *pstate, List *returningList); +static List *transformUpdateTargetList(ParseState *pstate, + List *targetList); static Query *transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt); static Query *transformExplainStmt(ParseState *pstate, @@ -263,7 +267,6 @@ transformStmt(ParseState *pstate, Node *parseTree) case T_InsertStmt: case T_UpdateStmt: case T_DeleteStmt: - case T_MergeStmt: (void) test_raw_expression_coverage(parseTree, NULL); break; default: @@ -288,10 +291,6 @@ transformStmt(ParseState *pstate, Node *parseTree) result = transformUpdateStmt(pstate, (UpdateStmt *) parseTree); break; - case T_MergeStmt: - result = transformMergeStmt(pstate, (MergeStmt *) parseTree); - break; - case T_SelectStmt: { SelectStmt *n = (SelectStmt *) parseTree; @@ -367,7 +366,6 @@ analyze_requires_snapshot(RawStmt *parseTree) case T_InsertStmt: case T_DeleteStmt: case T_UpdateStmt: - case T_MergeStmt: case T_SelectStmt: result = true; break; @@ -898,7 +896,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * attrnos: integer column numbers (must be same length as icolumns) * strip_indirection: if true, remove any field/array assignment nodes */ -List * +static List * transformInsertRow(ParseState *pstate, List *exprlist, List *stmtcols, List *icolumns, List *attrnos, bool strip_indirection) @@ -2262,9 +2260,9 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) /* * transformUpdateTargetList - - * handle SET clause in UPDATE/MERGE/INSERT ... ON CONFLICT UPDATE + * handle SET clause in UPDATE/INSERT ... ON CONFLICT UPDATE */ -List * +static List * transformUpdateTargetList(ParseState *pstate, List *origTlist) { List *tlist = NIL; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd0c26c11b8..e5484766234 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -241,7 +241,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); PartitionSpec *partspec; PartitionBoundSpec *partboundspec; RoleSpec *rolespec; - MergeWhenClause *mergewhen; } %type <node> stmt schema_stmt @@ -283,7 +282,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); CreateMatViewStmt RefreshMatViewStmt CreateAmStmt CreatePublicationStmt AlterPublicationStmt CreateSubscriptionStmt AlterSubscriptionStmt DropSubscriptionStmt - MergeStmt %type <node> select_no_parens select_with_parens select_clause simple_select values_clause @@ -402,7 +400,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); TriggerTransitions TriggerReferencing publication_name_list vacuum_relation_list opt_vacuum_relation_list - merge_values_clause %type <list> group_by_list %type <node> group_by_item empty_grouping_set rollup_clause cube_clause @@ -463,7 +460,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type <istmt> insert_rest %type <infer> opt_conf_expr %type <onconflict> opt_on_conflict -%type <mergewhen> merge_insert merge_update merge_delete %type <vsetstmt> generic_set set_rest set_rest_more generic_reset reset_rest SetResetClause FunctionSetResetClause @@ -589,9 +585,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type <list> hash_partbound partbound_datum_list range_datum_list %type <defelt> hash_partbound_elem -%type <node> merge_when_clause opt_merge_when_and_condition -%type <list> merge_when_list - /* * Non-keyword token types. These are hard-wired into the "flex" lexer. * They must be listed first so that their numeric codes do not depend on @@ -659,8 +652,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED - MAPPING MATCH MATCHED MATERIALIZED MAXVALUE MERGE METHOD - MINUTE_P MINVALUE MODE MONTH_P MOVE + MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE NAME_P NAMES NATIONAL NATURAL NCHAR NEW NEXT NO NONE NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF @@ -929,7 +921,6 @@ stmt : | RefreshMatViewStmt | LoadStmt | LockStmt - | MergeStmt | NotifyStmt | PrepareStmt | ReassignOwnedStmt @@ -10689,7 +10680,6 @@ ExplainableStmt: | InsertStmt | UpdateStmt | DeleteStmt - | MergeStmt | DeclareCursorStmt | CreateAsStmt | CreateMatViewStmt @@ -10752,7 +10742,6 @@ PreparableStmt: | InsertStmt | UpdateStmt | DeleteStmt /* by default all are $$=$1 */ - | MergeStmt ; /***************************************************************************** @@ -11122,142 +11111,6 @@ set_target_list: /***************************************************************************** * * QUERY: - * MERGE STATEMENTS - * - *****************************************************************************/ - -MergeStmt: - opt_with_clause MERGE INTO relation_expr_opt_alias - USING table_ref - ON a_expr - merge_when_list - { - MergeStmt *m = makeNode(MergeStmt); - - m->withClause = $1; - m->relation = $4; - m->source_relation = $6; - m->join_condition = $8; - m->mergeWhenClauses = $9; - - $$ = (Node *)m; - } - ; - - -merge_when_list: - merge_when_clause { $$ = list_make1($1); } - | merge_when_list merge_when_clause { $$ = lappend($1,$2); } - ; - -merge_when_clause: - WHEN MATCHED opt_merge_when_and_condition THEN merge_update - { - $5->matched = true; - $5->commandType = CMD_UPDATE; - $5->condition = $3; - - $$ = (Node *) $5; - } - | WHEN MATCHED opt_merge_when_and_condition THEN merge_delete - { - MergeWhenClause *m = makeNode(MergeWhenClause); - - m->matched = true; - m->commandType = CMD_DELETE; - m->condition = $3; - - $$ = (Node *)m; - } - | WHEN NOT MATCHED opt_merge_when_and_condition THEN merge_insert - { - $6->matched = false; - $6->commandType = CMD_INSERT; - $6->condition = $4; - - $$ = (Node *) $6; - } - | WHEN NOT MATCHED opt_merge_when_and_condition THEN DO NOTHING - { - MergeWhenClause *m = makeNode(MergeWhenClause); - - m->matched = false; - m->commandType = CMD_NOTHING; - m->condition = $4; - - $$ = (Node *)m; - } - ; - -opt_merge_when_and_condition: - AND a_expr { $$ = $2; } - | { $$ = NULL; } - ; - -merge_delete: - DELETE_P { $$ = NULL; } - ; - -merge_update: - UPDATE SET set_clause_list - { - MergeWhenClause *n = makeNode(MergeWhenClause); - n->targetList = $3; - - $$ = n; - } - ; - -merge_insert: - INSERT merge_values_clause - { - MergeWhenClause *n = makeNode(MergeWhenClause); - n->cols = NIL; - n->values = $2; - $$ = n; - } - | INSERT OVERRIDING override_kind VALUE_P merge_values_clause - { - MergeWhenClause *n = makeNode(MergeWhenClause); - n->cols = NIL; - n->override = $3; - n->values = $5; - $$ = n; - } - | INSERT '(' insert_column_list ')' merge_values_clause - { - MergeWhenClause *n = makeNode(MergeWhenClause); - n->cols = $3; - n->values = $5; - $$ = n; - } - | INSERT '(' insert_column_list ')' OVERRIDING override_kind VALUE_P merge_values_clause - { - MergeWhenClause *n = makeNode(MergeWhenClause); - n->cols = $3; - n->override = $6; - n->values = $8; - $$ = n; - } - | INSERT DEFAULT VALUES - { - MergeWhenClause *n = makeNode(MergeWhenClause); - n->cols = NIL; - n->values = NIL; - $$ = n; - } - ; - -merge_values_clause: - VALUES '(' expr_list ')' - { - $$ = $3; - } - ; - -/***************************************************************************** - * - * QUERY: * CURSOR STATEMENTS * *****************************************************************************/ @@ -15256,10 +15109,8 @@ unreserved_keyword: | LOGGED | MAPPING | MATCH - | MATCHED | MATERIALIZED | MAXVALUE - | MERGE | METHOD | MINUTE_P | MINVALUE diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c index 0307738946a..61727e1d71a 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -455,13 +455,6 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr) case EXPR_KIND_VALUES_SINGLE: errkind = true; break; - case EXPR_KIND_MERGE_WHEN_AND: - if (isAgg) - err = _("aggregate functions are not allowed in WHEN AND conditions"); - else - err = _("grouping operations are not allowed in WHEN AND conditions"); - - break; case EXPR_KIND_CHECK_CONSTRAINT: case EXPR_KIND_DOMAIN_CHECK: if (isAgg) @@ -880,9 +873,6 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc, case EXPR_KIND_VALUES_SINGLE: errkind = true; break; - case EXPR_KIND_MERGE_WHEN_AND: - err = _("window functions are not allowed in WHEN AND conditions"); - break; case EXPR_KIND_CHECK_CONSTRAINT: case EXPR_KIND_DOMAIN_CHECK: err = _("window functions are not allowed in check constraints"); diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index c73d06b3917..e1478805c26 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -76,6 +76,9 @@ static RangeTblEntry *transformRangeTableFunc(ParseState *pstate, RangeTableFunc *t); static TableSampleClause *transformRangeTableSample(ParseState *pstate, RangeTableSample *rts); +static Node *transformFromClauseItem(ParseState *pstate, Node *n, + RangeTblEntry **top_rte, int *top_rti, + List **namespace); static Node *buildMergedJoinVar(ParseState *pstate, JoinType jointype, Var *l_colvar, Var *r_colvar); static ParseNamespaceItem *makeNamespaceItem(RangeTblEntry *rte, @@ -136,7 +139,6 @@ transformFromClause(ParseState *pstate, List *frmList) n = transformFromClauseItem(pstate, n, &rte, &rtindex, - NULL, NULL, &namespace); checkNameSpaceConflicts(pstate, pstate->p_namespace, namespace); @@ -1094,21 +1096,14 @@ getRTEForSpecialRelationTypes(ParseState *pstate, RangeVar *rv) * * *top_rti: receives the rangetable index of top_rte. (Ditto.) * - * *right_rte: receives the RTE corresponding to the right side of the - * jointree. Only MERGE really needs to know about this and only MERGE passes a - * non-NULL pointer. - * - * *right_rti: receives the rangetable index of the right_rte. - * * *namespace: receives a List of ParseNamespaceItems for the RTEs exposed * as table/column names by this item. (The lateral_only flags in these items * are indeterminate and should be explicitly set by the caller before use.) */ -Node * +static Node * transformFromClauseItem(ParseState *pstate, Node *n, RangeTblEntry **top_rte, int *top_rti, - RangeTblEntry **right_rte, int *right_rti, - List **fnamespace) + List **namespace) { if (IsA(n, RangeVar)) { @@ -1130,7 +1125,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); *top_rte = rte; *top_rti = rtindex; - *fnamespace = list_make1(makeDefaultNSItem(rte)); + *namespace = list_make1(makeDefaultNSItem(rte)); rtr = makeNode(RangeTblRef); rtr->rtindex = rtindex; return (Node *) rtr; @@ -1148,7 +1143,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); *top_rte = rte; *top_rti = rtindex; - *fnamespace = list_make1(makeDefaultNSItem(rte)); + *namespace = list_make1(makeDefaultNSItem(rte)); rtr = makeNode(RangeTblRef); rtr->rtindex = rtindex; return (Node *) rtr; @@ -1166,7 +1161,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); *top_rte = rte; *top_rti = rtindex; - *fnamespace = list_make1(makeDefaultNSItem(rte)); + *namespace = list_make1(makeDefaultNSItem(rte)); rtr = makeNode(RangeTblRef); rtr->rtindex = rtindex; return (Node *) rtr; @@ -1184,7 +1179,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); *top_rte = rte; *top_rti = rtindex; - *fnamespace = list_make1(makeDefaultNSItem(rte)); + *namespace = list_make1(makeDefaultNSItem(rte)); rtr = makeNode(RangeTblRef); rtr->rtindex = rtindex; return (Node *) rtr; @@ -1199,7 +1194,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, /* Recursively transform the contained relation */ rel = transformFromClauseItem(pstate, rts->relation, - top_rte, top_rti, NULL, NULL, fnamespace); + top_rte, top_rti, namespace); /* Currently, grammar could only return a RangeVar as contained rel */ rtr = castNode(RangeTblRef, rel); rte = rt_fetch(rtr->rtindex, pstate->p_rtable); @@ -1227,7 +1222,6 @@ transformFromClauseItem(ParseState *pstate, Node *n, List *l_namespace, *r_namespace, *my_namespace, - *save_namespace, *l_colnames, *r_colnames, *res_colnames, @@ -1246,7 +1240,6 @@ transformFromClauseItem(ParseState *pstate, Node *n, j->larg = transformFromClauseItem(pstate, j->larg, &l_rte, &l_rtindex, - NULL, NULL, &l_namespace); /* @@ -1270,34 +1263,12 @@ transformFromClauseItem(ParseState *pstate, Node *n, sv_namespace_length = list_length(pstate->p_namespace); pstate->p_namespace = list_concat(pstate->p_namespace, l_namespace); - /* - * If we are running MERGE, don't make the other RTEs visible while - * parsing the source relation. It mustn't see them. - * - * Currently, only MERGE passes non-NULL value for right_rte, so we - * can safely deduce if we're running MERGE or not by just looking at - * the right_rte. If that ever changes, we should look at other means - * to find that. - */ - if (right_rte) - { - save_namespace = pstate->p_namespace; - pstate->p_namespace = NIL; - } - /* And now we can process the RHS */ j->rarg = transformFromClauseItem(pstate, j->rarg, &r_rte, &r_rtindex, - NULL, NULL, &r_namespace); - /* - * And now restore the namespace again so that join-quals can see it. - */ - if (right_rte) - pstate->p_namespace = save_namespace; - /* Remove the left-side RTEs from the namespace list again */ pstate->p_namespace = list_truncate(pstate->p_namespace, sv_namespace_length); @@ -1324,12 +1295,6 @@ transformFromClauseItem(ParseState *pstate, Node *n, expandRTE(r_rte, r_rtindex, 0, -1, false, &r_colnames, &r_colvars); - if (right_rte) - *right_rte = r_rte; - - if (right_rti) - *right_rti = r_rtindex; - /* * Natural join does not explicitly specify columns; must generate * columns to join. Need to run through the list of columns from each @@ -1558,7 +1523,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, * The join RTE itself is always made visible for unqualified column * names. It's visible as a relation name only if it has an alias. */ - *fnamespace = lappend(my_namespace, + *namespace = lappend(my_namespace, makeNamespaceItem(rte, (j->alias != NULL), true, diff --git a/src/backend/parser/parse_collate.c b/src/backend/parser/parse_collate.c index 51c73c4018a..6d34245083e 100644 --- a/src/backend/parser/parse_collate.c +++ b/src/backend/parser/parse_collate.c @@ -485,7 +485,6 @@ assign_collations_walker(Node *node, assign_collations_context *context) case T_FromExpr: case T_OnConflictExpr: case T_SortGroupClause: - case T_MergeAction: (void) expression_tree_walker(node, assign_collations_walker, (void *) &loccontext); diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 38fbe3366fc..385e54a9b69 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -1818,7 +1818,6 @@ transformSubLink(ParseState *pstate, SubLink *sublink) case EXPR_KIND_RETURNING: case EXPR_KIND_VALUES: case EXPR_KIND_VALUES_SINGLE: - case EXPR_KIND_MERGE_WHEN_AND: /* okay */ break; case EXPR_KIND_CHECK_CONSTRAINT: @@ -3476,8 +3475,6 @@ ParseExprKindName(ParseExprKind exprKind) return "PARTITION BY"; case EXPR_KIND_CALL_ARGUMENT: return "CALL"; - case EXPR_KIND_MERGE_WHEN_AND: - return "MERGE WHEN AND"; /* * There is intentionally no default: case here, so that the diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 615aee6d15f..ea5d5212b4c 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -2277,9 +2277,6 @@ check_srf_call_placement(ParseState *pstate, Node *last_srf, int location) /* okay, since we process this like a SELECT tlist */ pstate->p_hasTargetSRFs = true; break; - case EXPR_KIND_MERGE_WHEN_AND: - err = _("set-returning functions are not allowed in WHEN AND conditions"); - break; case EXPR_KIND_CHECK_CONSTRAINT: case EXPR_KIND_DOMAIN_CHECK: err = _("set-returning functions are not allowed in check constraints"); diff --git a/src/backend/parser/parse_merge.c b/src/backend/parser/parse_merge.c deleted file mode 100644 index 722cb23b86c..00000000000 --- a/src/backend/parser/parse_merge.c +++ /dev/null @@ -1,654 +0,0 @@ -/*------------------------------------------------------------------------- - * - * parse_merge.c - * handle merge-statement in parser - * - * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * src/backend/parser/parse_merge.c - * - *------------------------------------------------------------------------- - */ - -#include "postgres.h" - -#include "miscadmin.h" - -#include "access/sysattr.h" -#include "nodes/makefuncs.h" -#include "parser/analyze.h" -#include "parser/parse_collate.h" -#include "parser/parsetree.h" -#include "parser/parser.h" -#include "parser/parse_clause.h" -#include "parser/parse_cte.h" -#include "parser/parse_merge.h" -#include "parser/parse_relation.h" -#include "parser/parse_target.h" -#include "utils/rel.h" -#include "utils/relcache.h" - -static int transformMergeJoinClause(ParseState *pstate, Node *merge, - List **mergeSourceTargetList); -static void setNamespaceForMergeWhen(ParseState *pstate, - MergeWhenClause *mergeWhenClause); -static void setNamespaceVisibilityForRTE(List *namespace, RangeTblEntry *rte, - bool rel_visible, - bool cols_visible); -static List *expandSourceTL(ParseState *pstate, RangeTblEntry *rte, - int rtindex); - -/* - * Special handling for MERGE statement is required because we assemble - * the query manually. This is similar to setTargetTable() followed - * by transformFromClause() but with a few less steps. - * - * Process the FROM clause and add items to the query's range table, - * joinlist, and namespace. - * - * A special targetlist comprising of the columns from the right-subtree of - * the join is populated and returned. Note that when the JoinExpr is - * setup by transformMergeStmt, the left subtree has the target result - * relation and the right subtree has the source relation. - * - * Returns the rangetable index of the target relation. - */ -static int -transformMergeJoinClause(ParseState *pstate, Node *merge, - List **mergeSourceTargetList) -{ - RangeTblEntry *rte, - *rt_rte; - List *namespace; - int rtindex, - rt_rtindex; - Node *n; - int mergeTarget_relation = list_length(pstate->p_rtable) + 1; - Var *var; - TargetEntry *te; - - n = transformFromClauseItem(pstate, merge, - &rte, - &rtindex, - &rt_rte, - &rt_rtindex, - &namespace); - - pstate->p_joinlist = list_make1(n); - - /* - * We created an internal join between the target and the source relation - * to carry out the MERGE actions. Normally such an unaliased join hides - * the joining relations, unless the column references are qualified. - * Also, any unqualified column references are resolved to the Join RTE, if - * there is a matching entry in the targetlist. But the way MERGE - * execution is later setup, we expect all column references to resolve to - * either the source or the target relation. Hence we must not add the - * Join RTE to the namespace. - * - * The last entry must be for the top-level Join RTE. We don't want to - * resolve any references to the Join RTE. So discard that. - * - * We also do not want to resolve any references from the leftside of the - * Join since that corresponds to the target relation. References to the - * columns of the target relation must be resolved from the result - * relation and not the one that is used in the join. So the - * mergeTarget_relation is marked invisible to both qualified as well as - * unqualified references. - */ - Assert(list_length(namespace) > 1); - namespace = list_truncate(namespace, list_length(namespace) - 1); - pstate->p_namespace = list_concat(pstate->p_namespace, namespace); - - setNamespaceVisibilityForRTE(pstate->p_namespace, - rt_fetch(mergeTarget_relation, pstate->p_rtable), false, false); - - /* - * Expand the right relation and add its columns to the - * mergeSourceTargetList. Note that the right relation can either be a - * plain relation or a subquery or anything that can have a - * RangeTableEntry. - */ - *mergeSourceTargetList = expandSourceTL(pstate, rt_rte, rt_rtindex); - - /* - * Add a whole-row-Var entry to support references to "source.*". - */ - var = makeWholeRowVar(rt_rte, rt_rtindex, 0, false); - te = makeTargetEntry((Expr *) var, list_length(*mergeSourceTargetList) + 1, - NULL, true); - *mergeSourceTargetList = lappend(*mergeSourceTargetList, te); - - return mergeTarget_relation; -} - -/* - * Make appropriate changes to the namespace visibility while transforming - * individual action's quals and targetlist expressions. In particular, for - * INSERT actions we must only see the source relation (since INSERT action is - * invoked for NOT MATCHED tuples and hence there is no target tuple to deal - * with). On the other hand, UPDATE and DELETE actions can see both source and - * target relations. - * - * Also, since the internal Join node can hide the source and target - * relations, we must explicitly make the respective relation as visible so - * that columns can be referenced unqualified from these relations. - */ -static void -setNamespaceForMergeWhen(ParseState *pstate, MergeWhenClause *mergeWhenClause) -{ - RangeTblEntry *targetRelRTE, - *sourceRelRTE; - - /* Assume target relation is at index 1 */ - targetRelRTE = rt_fetch(1, pstate->p_rtable); - - /* - * Assume that the top-level join RTE is at the end. The source relation - * is just before that. - */ - sourceRelRTE = rt_fetch(list_length(pstate->p_rtable) - 1, pstate->p_rtable); - - switch (mergeWhenClause->commandType) - { - case CMD_INSERT: - - /* - * Inserts can't see target relation, but they can see source - * relation. - */ - setNamespaceVisibilityForRTE(pstate->p_namespace, - targetRelRTE, false, false); - setNamespaceVisibilityForRTE(pstate->p_namespace, - sourceRelRTE, true, true); - break; - - case CMD_UPDATE: - case CMD_DELETE: - - /* - * Updates and deletes can see both target and source relations. - */ - setNamespaceVisibilityForRTE(pstate->p_namespace, - targetRelRTE, true, true); - setNamespaceVisibilityForRTE(pstate->p_namespace, - sourceRelRTE, true, true); - break; - - case CMD_NOTHING: - break; - default: - elog(ERROR, "unknown action in MERGE WHEN clause"); - } -} - -/* - * transformMergeStmt - - * transforms a MERGE statement - */ -Query * -transformMergeStmt(ParseState *pstate, MergeStmt *stmt) -{ - Query *qry = makeNode(Query); - ListCell *l; - AclMode targetPerms = ACL_NO_RIGHTS; - bool is_terminal[2]; - JoinExpr *joinexpr; - RangeTblEntry *resultRelRTE, *mergeRelRTE; - List *mergeActionList; - - /* There can't be any outer WITH to worry about */ - Assert(pstate->p_ctenamespace == NIL); - - qry->commandType = CMD_MERGE; - qry->hasRecursive = false; - - /* process the WITH clause independently of all else */ - if (stmt->withClause) - { - if (stmt->withClause->recursive) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("WITH RECURSIVE is not supported for MERGE statement"))); - - qry->cteList = transformWithClause(pstate, stmt->withClause); - qry->hasModifyingCTE = pstate->p_hasModifyingCTE; - } - - /* - * Check WHEN clauses for permissions and sanity - */ - is_terminal[0] = false; - is_terminal[1] = false; - foreach(l, stmt->mergeWhenClauses) - { - MergeWhenClause *mergeWhenClause = (MergeWhenClause *) lfirst(l); - int when_type = (mergeWhenClause->matched ? 0 : 1); - - /* - * Collect action types so we can check Target permissions - */ - switch (mergeWhenClause->commandType) - { - case CMD_INSERT: - targetPerms |= ACL_INSERT; - break; - case CMD_UPDATE: - targetPerms |= ACL_UPDATE; - break; - case CMD_DELETE: - targetPerms |= ACL_DELETE; - break; - case CMD_NOTHING: - break; - default: - elog(ERROR, "unknown action in MERGE WHEN clause"); - } - - /* - * Check for unreachable WHEN clauses - */ - if (mergeWhenClause->condition == NULL) - is_terminal[when_type] = true; - else if (is_terminal[when_type]) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("unreachable WHEN clause specified after unconditional WHEN clause"))); - } - - /* - * Construct a query of the form - * SELECT relation.ctid --junk attribute - * ,relation.tableoid --junk attribute - * ,source_relation.<somecols> - * ,relation.<somecols> - * FROM relation RIGHT JOIN source_relation - * ON join_condition; -- no WHERE clause - all conditions are applied in - * executor - * - * stmt->relation is the target relation, given as a RangeVar - * stmt->source_relation is a RangeVar or subquery - * - * We specify the join as a RIGHT JOIN as a simple way of forcing the - * first (larg) RTE to refer to the target table. - * - * The MERGE query's join can be tuned in some cases, see below for these - * special case tweaks. - * - * We set QSRC_PARSER to show query constructed in parse analysis - * - * Note that we have only one Query for a MERGE statement and the planner - * is called only once. That query is executed once to produce our stream - * of candidate change rows, so the query must contain all of the columns - * required by each of the targetlist or conditions for each action. - * - * As top-level statements INSERT, UPDATE and DELETE have a Query, whereas - * with MERGE the individual actions do not require separate planning, - * only different handling in the executor. See nodeModifyTable handling - * of commandType CMD_MERGE. - * - * A sub-query can include the Target, but otherwise the sub-query cannot - * reference the outermost Target table at all. - */ - qry->querySource = QSRC_PARSER; - - /* - * Setup the target table. Unlike regular UPDATE/DELETE, we don't expand - * inheritance for the target relation in case of MERGE. - * - * This special arrangement is required for handling partitioned tables - * because we perform an JOIN between the target and the source relation to - * identify the matching and not-matching rows. If we take the usual path - * of expanding the target table's inheritance and create one subplan per - * partition, then we we won't be able to correctly identify the matching - * and not-matching rows since for a given source row, there may not be a - * matching row in one partition, but it may exists in some other - * partition. So we must first append all the qualifying rows from all the - * partitions and then do the matching. - * - * Once a target row is returned by the underlying join, we find the - * correct partition and setup required state to carry out UPDATE/DELETE. - * All of this happens during execution. - */ - qry->resultRelation = setTargetTable(pstate, stmt->relation, - false, /* do not expand inheritance */ - true, targetPerms); - - /* - * Create a JOIN between the target and the source relation. - */ - joinexpr = makeNode(JoinExpr); - joinexpr->isNatural = false; - joinexpr->alias = NULL; - joinexpr->usingClause = NIL; - joinexpr->quals = stmt->join_condition; - joinexpr->larg = (Node *) stmt->relation; - joinexpr->rarg = (Node *) stmt->source_relation; - - /* - * Simplify the MERGE query as much as possible - * - * These seem like things that could go into Optimizer, but they are - * semantic simplifications rather than optimizations, per se. - * - * If there are no INSERT actions we won't be using the non-matching - * candidate rows for anything, so no need for an outer join. We do still - * need an inner join for UPDATE and DELETE actions. - */ - if (targetPerms & ACL_INSERT) - joinexpr->jointype = JOIN_RIGHT; - else - joinexpr->jointype = JOIN_INNER; - - /* - * We use a special purpose transformation here because the normal - * routines don't quite work right for the MERGE case. - * - * A special mergeSourceTargetList is setup by transformMergeJoinClause(). - * It refers to all the attributes provided by the source relation. This - * is later used by set_plan_refs() to fix the UPDATE/INSERT target lists - * to so that they can correctly fetch the attributes from the source - * relation. - * - * The target relation when used in the underlying join, gets a new RTE - * with rte->inh set to true. We remember this RTE (and later pass on to - * the planner and executor) for two main reasons: - * - * 1. If we ever need to run EvalPlanQual while performing MERGE, we must - * make the modified tuple available to the underlying join query, which is - * using a different RTE from the resultRelation RTE. - * - * 2. rewriteTargetListMerge() requires the RTE of the underlying join in - * order to add junk CTID and TABLEOID attributes. - */ - qry->mergeTarget_relation = transformMergeJoinClause(pstate, (Node *) joinexpr, - &qry->mergeSourceTargetList); - - /* - * The target table referenced in the MERGE is looked up twice; once while - * setting it up as the result relation and again when it's used in the - * underlying the join query. In some rare situations, it may happen that - * these lookups return different results, for example, if a new relation - * with the same name gets created in a schema which is ahead in the - * search_path, in between the two lookups. - * - * It's a very narrow case, but nevertheless we guard against it by simply - * checking if the OIDs returned by the two lookups is the same. If not, we - * just throw an error. - */ - Assert(qry->resultRelation > 0); - Assert(qry->mergeTarget_relation > 0); - - /* Fetch both the RTEs */ - resultRelRTE = rt_fetch(qry->resultRelation, pstate->p_rtable); - mergeRelRTE = rt_fetch(qry->mergeTarget_relation, pstate->p_rtable); - - if (resultRelRTE->relid != mergeRelRTE->relid) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("relation referenced by MERGE statement has changed"))); - - /* - * This query should just provide the source relation columns. Later, in - * preprocess_targetlist(), we shall also add "ctid" attribute of the - * target relation to ensure that the target tuple can be fetched - * correctly. - */ - qry->targetList = qry->mergeSourceTargetList; - - /* qry has no WHERE clause so absent quals are shown as NULL */ - qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); - qry->rtable = pstate->p_rtable; - - /* - * XXX MERGE is unsupported in various cases - */ - if (!(pstate->p_target_relation->rd_rel->relkind == RELKIND_RELATION || - pstate->p_target_relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("MERGE is not supported for this relation type"))); - - if (pstate->p_target_relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE && - pstate->p_target_relation->rd_rel->relhassubclass) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("MERGE is not supported for relations with inheritance"))); - - if (pstate->p_target_relation->rd_rel->relhasrules) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("MERGE is not supported for relations with rules"))); - - /* - * We now have a good query shape, so now look at the when conditions and - * action targetlists. - * - * Overall, the MERGE Query's targetlist is NIL. - * - * Each individual action has its own targetlist that needs separate - * transformation. These transforms don't do anything to the overall - * targetlist, since that is only used for resjunk columns. - * - * We can reference any column in Target or Source, which is OK because - * both of those already have RTEs. There is nothing like the EXCLUDED - * pseudo-relation for INSERT ON CONFLICT. - */ - mergeActionList = NIL; - foreach(l, stmt->mergeWhenClauses) - { - MergeWhenClause *mergeWhenClause = (MergeWhenClause *) lfirst(l); - MergeAction *action = makeNode(MergeAction); - - action->commandType = mergeWhenClause->commandType; - action->matched = mergeWhenClause->matched; - - /* - * Set namespace for the specific action. This must be done before - * analyzing the WHEN quals and the action targetlisst. - */ - setNamespaceForMergeWhen(pstate, mergeWhenClause); - - /* - * Transform the when condition. - * - * Note that these quals are NOT added to the join quals; instead they - * are evaluated separately during execution to decide which of the - * WHEN MATCHED or WHEN NOT MATCHED actions to execute. - */ - action->qual = transformWhereClause(pstate, mergeWhenClause->condition, - EXPR_KIND_MERGE_WHEN_AND, "WHEN"); - - /* - * Transform target lists for each INSERT and UPDATE action stmt - */ - switch (action->commandType) - { - case CMD_INSERT: - { - List *exprList = NIL; - ListCell *lc; - RangeTblEntry *rte; - ListCell *icols; - ListCell *attnos; - List *icolumns; - List *attrnos; - - pstate->p_is_insert = true; - - icolumns = checkInsertTargets(pstate, - mergeWhenClause->cols, - &attrnos); - Assert(list_length(icolumns) == list_length(attrnos)); - - action->override = mergeWhenClause->override; - - /* - * Handle INSERT much like in transformInsertStmt - */ - if (mergeWhenClause->values == NIL) - { - /* - * We have INSERT ... DEFAULT VALUES. We can handle - * this case by emitting an empty targetlist --- all - * columns will be defaulted when the planner expands - * the targetlist. - */ - exprList = NIL; - } - else - { - /* - * Process INSERT ... VALUES with a single VALUES - * sublist. We treat this case separately for - * efficiency. The sublist is just computed directly - * as the Query's targetlist, with no VALUES RTE. So - * it works just like a SELECT without any FROM. - */ - - /* - * Do basic expression transformation (same as a ROW() - * expr, but allow SetToDefault at top level) - */ - exprList = transformExpressionList(pstate, - mergeWhenClause->values, - EXPR_KIND_VALUES_SINGLE, - true); - - /* Prepare row for assignment to target table */ - exprList = transformInsertRow(pstate, exprList, - mergeWhenClause->cols, - icolumns, attrnos, - false); - } - - /* - * Generate action's target list using the computed list - * of expressions. Also, mark all the target columns as - * needing insert permissions. - */ - rte = pstate->p_target_rangetblentry; - icols = list_head(icolumns); - attnos = list_head(attrnos); - foreach(lc, exprList) - { - Expr *expr = (Expr *) lfirst(lc); - ResTarget *col; - AttrNumber attr_num; - TargetEntry *tle; - - col = lfirst_node(ResTarget, icols); - attr_num = (AttrNumber) lfirst_int(attnos); - - tle = makeTargetEntry(expr, - attr_num, - col->name, - false); - action->targetList = lappend(action->targetList, tle); - - rte->insertedCols = bms_add_member(rte->insertedCols, - attr_num - FirstLowInvalidHeapAttributeNumber); - - icols = lnext(icols); - attnos = lnext(attnos); - } - } - break; - case CMD_UPDATE: - { - pstate->p_is_insert = false; - action->targetList = transformUpdateTargetList(pstate, - mergeWhenClause->targetList); - } - break; - case CMD_DELETE: - break; - - case CMD_NOTHING: - action->targetList = NIL; - break; - default: - elog(ERROR, "unknown action in MERGE WHEN clause"); - } - - mergeActionList = lappend(mergeActionList, action); - } - - qry->mergeActionList = mergeActionList; - - /* XXX maybe later */ - qry->returningList = NULL; - - qry->hasTargetSRFs = false; - qry->hasSubLinks = pstate->p_hasSubLinks; - - assign_query_collations(pstate, qry); - - return qry; -} - -static void -setNamespaceVisibilityForRTE(List *namespace, RangeTblEntry *rte, - bool rel_visible, - bool cols_visible) -{ - ListCell *lc; - - foreach(lc, namespace) - { - ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(lc); - - if (nsitem->p_rte == rte) - { - nsitem->p_rel_visible = rel_visible; - nsitem->p_cols_visible = cols_visible; - break; - } - } - -} - -/* - * Expand the source relation to include all attributes of this RTE. - * - * This function is very similar to expandRelAttrs except that we don't mark - * columns for SELECT privileges. That will be decided later when we transform - * the action targetlists and the WHEN quals for actual references to the - * source relation. - */ -static List * -expandSourceTL(ParseState *pstate, RangeTblEntry *rte, int rtindex) -{ - List *names, - *vars; - ListCell *name, - *var; - List *te_list = NIL; - - expandRTE(rte, rtindex, 0, -1, false, &names, &vars); - - /* - * Require read access to the table. - */ - rte->requiredPerms |= ACL_SELECT; - - forboth(name, names, var, vars) - { - char *label = strVal(lfirst(name)); - Var *varnode = (Var *) lfirst(var); - TargetEntry *te; - - te = makeTargetEntry((Expr *) varnode, - (AttrNumber) pstate->p_next_resno++, - label, - false); - te_list = lappend(te_list, te); - } - - Assert(name == NULL && var == NULL); /* lists not the same length? */ - - return te_list; -} diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 8b912eeea31..bf5df26009a 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -728,16 +728,6 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, const char *colname, colname), parser_errposition(pstate, location))); - /* In MERGE WHEN AND condition, no system column is allowed except tableOid or OID */ - if (pstate->p_expr_kind == EXPR_KIND_MERGE_WHEN_AND && - attnum < InvalidAttrNumber && - !(attnum == TableOidAttributeNumber || attnum == ObjectIdAttributeNumber)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("system column \"%s\" reference in WHEN AND condition is invalid", - colname), - parser_errposition(pstate, location))); - if (attnum != InvalidAttrNumber) { /* now check to see if column actually is defined */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index cb4bcd58d16..88140bc6877 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1377,57 +1377,6 @@ rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte, } } -void -rewriteTargetListMerge(Query *parsetree, Relation target_relation) -{ - Var *var = NULL; - const char *attrname; - TargetEntry *tle; - - Assert(target_relation->rd_rel->relkind == RELKIND_RELATION || - target_relation->rd_rel->relkind == RELKIND_MATVIEW || - target_relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE); - - /* - * Emit CTID so that executor can find the row to update or delete. - */ - var = makeVar(parsetree->mergeTarget_relation, - SelfItemPointerAttributeNumber, - TIDOID, - -1, - InvalidOid, - 0); - - attrname = "ctid"; - tle = makeTargetEntry((Expr *) var, - list_length(parsetree->targetList) + 1, - pstrdup(attrname), - true); - - parsetree->targetList = lappend(parsetree->targetList, tle); - - /* - * If we are dealing with partitioned table, then emit TABLEOID so that - * executor can find the partition the row belongs to. - */ - if (target_relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - { - var = makeVar(parsetree->mergeTarget_relation, - TableOidAttributeNumber, - OIDOID, - -1, - InvalidOid, - 0); - - attrname = "tableoid"; - tle = makeTargetEntry((Expr *) var, - list_length(parsetree->targetList) + 1, - pstrdup(attrname), - true); - - parsetree->targetList = lappend(parsetree->targetList, tle); - } -} /* * matchLocks - @@ -3382,7 +3331,6 @@ RewriteQuery(Query *parsetree, List *rewrite_events) } else if (event == CMD_UPDATE) { - Assert(parsetree->override == OVERRIDING_NOT_SET); parsetree->targetList = rewriteTargetListIU(parsetree->targetList, parsetree->commandType, @@ -3390,48 +3338,6 @@ RewriteQuery(Query *parsetree, List *rewrite_events) rt_entry_relation, parsetree->resultRelation, NULL); } - else if (event == CMD_MERGE) - { - Assert(parsetree->override == OVERRIDING_NOT_SET); - - /* - * Rewrite each action targetlist separately - */ - foreach(lc1, parsetree->mergeActionList) - { - MergeAction *action = (MergeAction *) lfirst(lc1); - - switch (action->commandType) - { - case CMD_NOTHING: - case CMD_DELETE: /* Nothing to do here */ - break; - case CMD_UPDATE: - action->targetList = - rewriteTargetListIU(action->targetList, - action->commandType, - parsetree->override, - rt_entry_relation, - parsetree->resultRelation, - NULL); - break; - case CMD_INSERT: - { - action->targetList = - rewriteTargetListIU(action->targetList, - action->commandType, - action->override, - rt_entry_relation, - parsetree->resultRelation, - NULL); - } - break; - default: - elog(ERROR, "unrecognized commandType: %d", action->commandType); - break; - } - } - } else if (event == CMD_DELETE) { /* Nothing to do here */ @@ -3445,20 +3351,13 @@ RewriteQuery(Query *parsetree, List *rewrite_events) locks = matchLocks(event, rt_entry_relation->rd_rules, result_relation, parsetree, &hasUpdate); - /* - * XXX MERGE doesn't support write rules because they would violate - * the SQL Standard spec and would be unclear how they should work. - */ - if (event == CMD_MERGE) - product_queries = NIL; - else - product_queries = fireRules(parsetree, - result_relation, - event, - locks, - &instead, - &returning, - &qual_product); + product_queries = fireRules(parsetree, + result_relation, + event, + locks, + &instead, + &returning, + &qual_product); /* * If there were no INSTEAD rules, and the target relation is a view diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index 57e52b4f988..61ef396d8af 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -379,95 +379,6 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, } } - /* - * FOR MERGE, we fetch policies for UPDATE, DELETE and INSERT (and ALL) - * and set them up so that we can enforce the appropriate policy depending - * on the final action we take. - * - * We don't fetch the SELECT policies since they are correctly applied to - * the root->mergeTarget_relation. The target rows are selected after - * joining the mergeTarget_relation and the source relation and hence it's - * enough to apply SELECT policies to the mergeTarget_relation. - * - * We don't push the UPDATE/DELETE USING quals to the RTE because we don't - * really want to apply them while scanning the relation since we don't - * know whether we will be doing a UPDATE or a DELETE at the end. We apply - * the respective policy once we decide the final action on the target - * tuple. - * - * XXX We are setting up USING quals as WITH CHECK. If RLS prohibits - * UPDATE/DELETE on the target row, we shall throw an error instead of - * silently ignoring the row. This is different than how normal - * UPDATE/DELETE works and more in line with INSERT ON CONFLICT DO UPDATE - * handling. - */ - if (commandType == CMD_MERGE) - { - List *merge_permissive_policies; - List *merge_restrictive_policies; - - /* - * Fetch the UPDATE policies and set them up to execute on the - * existing target row before doing UPDATE. - */ - get_policies_for_relation(rel, CMD_UPDATE, user_id, - &merge_permissive_policies, - &merge_restrictive_policies); - - /* - * WCO_RLS_MERGE_UPDATE_CHECK is used to check UPDATE USING quals on - * the existing target row. - */ - add_with_check_options(rel, rt_index, - WCO_RLS_MERGE_UPDATE_CHECK, - merge_permissive_policies, - merge_restrictive_policies, - withCheckOptions, - hasSubLinks, - true); - - /* - * Same with DELETE policies. - */ - get_policies_for_relation(rel, CMD_DELETE, user_id, - &merge_permissive_policies, - &merge_restrictive_policies); - - add_with_check_options(rel, rt_index, - WCO_RLS_MERGE_DELETE_CHECK, - merge_permissive_policies, - merge_restrictive_policies, - withCheckOptions, - hasSubLinks, - true); - - /* - * No special handling is required for INSERT policies. They will be - * checked and enforced during ExecInsert(). But we must add them to - * withCheckOptions. - */ - get_policies_for_relation(rel, CMD_INSERT, user_id, - &merge_permissive_policies, - &merge_restrictive_policies); - - add_with_check_options(rel, rt_index, - WCO_RLS_INSERT_CHECK, - merge_permissive_policies, - merge_restrictive_policies, - withCheckOptions, - hasSubLinks, - false); - - /* Enforce the WITH CHECK clauses of the UPDATE policies */ - add_with_check_options(rel, rt_index, - WCO_RLS_UPDATE_CHECK, - merge_permissive_policies, - merge_restrictive_policies, - withCheckOptions, - hasSubLinks, - false); - } - heap_close(rel, NoLock); /* @@ -527,14 +438,6 @@ get_policies_for_relation(Relation relation, CmdType cmd, Oid user_id, if (policy->polcmd == ACL_DELETE_CHR) cmd_matches = true; break; - case CMD_MERGE: - - /* - * We do not support a separate policy for MERGE command. - * Instead it derives from the policies defined for other - * commands. - */ - break; default: elog(ERROR, "unrecognized policy command type %d", (int) cmd); diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c index 50f852a4aa7..66cc5c35c68 100644 --- a/src/backend/tcop/pquery.c +++ b/src/backend/tcop/pquery.c @@ -193,11 +193,6 @@ ProcessQuery(PlannedStmt *plan, "DELETE " UINT64_FORMAT, queryDesc->estate->es_processed); break; - case CMD_MERGE: - snprintf(completionTag, COMPLETION_TAG_BUFSIZE, - "MERGE " UINT64_FORMAT, - queryDesc->estate->es_processed); - break; default: strcpy(completionTag, "???"); break; diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 4effadcb300..f8bcd5e2e2f 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -110,7 +110,6 @@ CommandIsReadOnly(PlannedStmt *pstmt) case CMD_UPDATE: case CMD_INSERT: case CMD_DELETE: - case CMD_MERGE: return false; case CMD_UTILITY: /* For now, treat all utility commands as read/write */ @@ -1833,8 +1832,6 @@ QueryReturnsTuples(Query *parsetree) case CMD_SELECT: /* returns tuples */ return true; - case CMD_MERGE: - return false; case CMD_INSERT: case CMD_UPDATE: case CMD_DELETE: @@ -2079,10 +2076,6 @@ CreateCommandTag(Node *parsetree) tag = "UPDATE"; break; - case T_MergeStmt: - tag = "MERGE"; - break; - case T_SelectStmt: tag = "SELECT"; break; @@ -2826,9 +2819,6 @@ CreateCommandTag(Node *parsetree) case CMD_DELETE: tag = "DELETE"; break; - case CMD_MERGE: - tag = "MERGE"; - break; case CMD_UTILITY: tag = CreateCommandTag(stmt->utilityStmt); break; @@ -2889,9 +2879,6 @@ CreateCommandTag(Node *parsetree) case CMD_DELETE: tag = "DELETE"; break; - case CMD_MERGE: - tag = "MERGE"; - break; case CMD_UTILITY: tag = CreateCommandTag(stmt->utilityStmt); break; @@ -2940,7 +2927,6 @@ GetCommandLogLevel(Node *parsetree) case T_InsertStmt: case T_DeleteStmt: case T_UpdateStmt: - case T_MergeStmt: lev = LOGSTMT_MOD; break; @@ -3380,7 +3366,6 @@ GetCommandLogLevel(Node *parsetree) case CMD_UPDATE: case CMD_INSERT: case CMD_DELETE: - case CMD_MERGE: lev = LOGSTMT_MOD; break; @@ -3411,7 +3396,6 @@ GetCommandLogLevel(Node *parsetree) case CMD_UPDATE: case CMD_INSERT: case CMD_DELETE: - case CMD_MERGE: lev = LOGSTMT_MOD; break; |