aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/access/heap/heapam.c28
-rw-r--r--src/backend/catalog/sql_features.txt6
-rw-r--r--src/backend/commands/explain.c33
-rw-r--r--src/backend/commands/prepare.c1
-rw-r--r--src/backend/commands/trigger.c156
-rw-r--r--src/backend/executor/Makefile2
-rw-r--r--src/backend/executor/README10
-rw-r--r--src/backend/executor/execMain.c17
-rw-r--r--src/backend/executor/execPartition.c116
-rw-r--r--src/backend/executor/execReplication.c4
-rw-r--r--src/backend/executor/nodeModifyTable.c384
-rw-r--r--src/backend/executor/spi.c3
-rw-r--r--src/backend/nodes/copyfuncs.c40
-rw-r--r--src/backend/nodes/equalfuncs.c32
-rw-r--r--src/backend/nodes/nodeFuncs.c48
-rw-r--r--src/backend/nodes/outfuncs.c25
-rw-r--r--src/backend/nodes/readfuncs.c6
-rw-r--r--src/backend/optimizer/plan/createplan.c22
-rw-r--r--src/backend/optimizer/plan/planner.c29
-rw-r--r--src/backend/optimizer/plan/setrefs.c54
-rw-r--r--src/backend/optimizer/prep/preptlist.c41
-rw-r--r--src/backend/optimizer/util/pathnode.c11
-rw-r--r--src/backend/optimizer/util/plancat.c4
-rw-r--r--src/backend/parser/Makefile2
-rw-r--r--src/backend/parser/analyze.c18
-rw-r--r--src/backend/parser/gram.y158
-rw-r--r--src/backend/parser/parse_agg.c10
-rw-r--r--src/backend/parser/parse_clause.c45
-rw-r--r--src/backend/parser/parse_collate.c1
-rw-r--r--src/backend/parser/parse_expr.c3
-rw-r--r--src/backend/parser/parse_func.c3
-rw-r--r--src/backend/parser/parse_relation.c10
-rw-r--r--src/backend/rewrite/rewriteHandler.c117
-rw-r--r--src/backend/rewrite/rowsecurity.c97
-rw-r--r--src/backend/tcop/pquery.c5
-rw-r--r--src/backend/tcop/utility.c16
-rw-r--r--src/include/access/heapam.h13
-rw-r--r--src/include/commands/trigger.h6
-rw-r--r--src/include/executor/execPartition.h1
-rw-r--r--src/include/executor/instrument.h7
-rw-r--r--src/include/executor/nodeModifyTable.h21
-rw-r--r--src/include/executor/spi.h1
-rw-r--r--src/include/nodes/execnodes.h65
-rw-r--r--src/include/nodes/nodes.h6
-rw-r--r--src/include/nodes/parsenodes.h37
-rw-r--r--src/include/nodes/plannodes.h8
-rw-r--r--src/include/nodes/relation.h7
-rw-r--r--src/include/optimizer/pathnode.h7
-rw-r--r--src/include/parser/analyze.h5
-rw-r--r--src/include/parser/kwlist.h2
-rw-r--r--src/include/parser/parse_clause.h5
-rw-r--r--src/include/parser/parse_node.h5
-rw-r--r--src/include/rewrite/rewriteHandler.h1
-rw-r--r--src/interfaces/libpq/fe-exec.c9
-rw-r--r--src/pl/plpgsql/src/pl_exec.c5
-rw-r--r--src/pl/plpgsql/src/pl_gram.y8
-rw-r--r--src/pl/plpgsql/src/pl_scanner.c1
-rw-r--r--src/pl/plpgsql/src/plpgsql.h4
-rw-r--r--src/test/isolation/isolation_schedule4
-rw-r--r--src/test/regress/expected/identity.out55
-rw-r--r--src/test/regress/expected/privileges.out98
-rw-r--r--src/test/regress/expected/rowsecurity.out182
-rw-r--r--src/test/regress/expected/rules.out31
-rw-r--r--src/test/regress/expected/triggers.out48
-rw-r--r--src/test/regress/parallel_schedule2
-rw-r--r--src/test/regress/serial_schedule1
-rw-r--r--src/test/regress/sql/identity.sql45
-rw-r--r--src/test/regress/sql/privileges.sql108
-rw-r--r--src/test/regress/sql/rowsecurity.sql156
-rw-r--r--src/test/regress/sql/rules.sql33
-rw-r--r--src/test/regress/sql/triggers.sql47
-rw-r--r--src/tools/pgindent/typedefs.list3
72 files changed, 159 insertions, 2435 deletions
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index f96567f5d51..d7279248e70 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -3245,7 +3245,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)
@@ -3508,7 +3507,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();
@@ -3548,10 +3547,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.
@@ -3667,7 +3664,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;
@@ -3684,7 +3681,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
}
else
{
- lockmode = hufd->lockmode = LockTupleExclusive;
+ *lockmode = LockTupleExclusive;
mxact_status = MultiXactStatusUpdate;
key_intact = false;
}
@@ -3762,12 +3759,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 */
@@ -3851,7 +3848,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);
@@ -3890,7 +3887,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)
@@ -3899,7 +3895,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);
@@ -3937,7 +3933,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);
@@ -4054,7 +4050,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);
@@ -4366,7 +4362,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);
@@ -4590,11 +4586,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:
@@ -5180,7 +5177,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 79f639d5e27..8a58672a94e 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;
@@ -3010,10 +3007,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 ???";
@@ -3136,32 +3129,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 e71f921fda1..a6593f939ca 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -85,8 +85,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,
@@ -2730,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;
@@ -2745,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;
}
@@ -2816,7 +2814,6 @@ ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
relinfo,
tupleid,
LockTupleExclusive,
- NULL,
NULL);
else
trigtuple = fdw_trigtuple;
@@ -2954,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);
@@ -2976,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 */
}
@@ -3096,7 +3092,6 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
relinfo,
tupleid,
LockTupleExclusive,
- NULL,
NULL);
else
trigtuple = fdw_trigtuple;
@@ -3245,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;
@@ -3272,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:
@@ -3313,17 +3302,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);
@@ -3846,14 +3828,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 */
- /* "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;
@@ -4320,19 +4296,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;
}
}
@@ -4667,10 +4637,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;
@@ -4682,31 +4650,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. */
@@ -4736,14 +4696,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);
@@ -4932,20 +4888,12 @@ AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
{
AfterTriggersTableData *table = (AfterTriggersTableData *) lfirst(lc);
- ts = table->old_upd_tuplestore;
- table->old_upd_tuplestore = NULL;
+ ts = table->old_tuplestore;
+ table->old_tuplestore = NULL;
if (ts)
tuplestore_end(ts);
- ts = table->new_upd_tuplestore;
- table->new_upd_tuplestore = NULL;
- if (ts)
- tuplestore_end(ts);
- ts = table->old_del_tuplestore;
- table->old_del_tuplestore = NULL;
- if (ts)
- tuplestore_end(ts);
- ts = table->new_ins_tuplestore;
- table->new_ins_tuplestore = NULL;
+ ts = table->new_tuplestore;
+ table->new_tuplestore = NULL;
if (ts)
tuplestore_end(ts);
}
@@ -5796,11 +5744,12 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
newtup == NULL));
if (oldtup != NULL &&
- (event == TRIGGER_EVENT_DELETE && delete_old_table))
+ ((event == TRIGGER_EVENT_DELETE && delete_old_table) ||
+ (event == TRIGGER_EVENT_UPDATE && update_old_table)))
{
Tuplestorestate *old_tuplestore;
- old_tuplestore = transition_capture->tcs_private->old_del_tuplestore;
+ old_tuplestore = transition_capture->tcs_private->old_tuplestore;
if (map != NULL)
{
@@ -5812,48 +5761,13 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
else
tuplestore_puttuple(old_tuplestore, oldtup);
}
- if (oldtup != NULL &&
- (event == TRIGGER_EVENT_UPDATE && update_old_table))
- {
- Tuplestorestate *old_tuplestore;
-
- old_tuplestore = transition_capture->tcs_private->old_upd_tuplestore;
-
- if (map != NULL)
- {
- 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))
- {
- Tuplestorestate *new_tuplestore;
-
- new_tuplestore = transition_capture->tcs_private->new_ins_tuplestore;
-
- if (original_insert_tuple != NULL)
- tuplestore_puttuple(new_tuplestore, original_insert_tuple);
- else if (map != NULL)
- {
- HeapTuple converted = do_convert_tuple(newtup, map);
-
- tuplestore_puttuple(new_tuplestore, converted);
- pfree(converted);
- }
- else
- tuplestore_puttuple(new_tuplestore, newtup);
- }
if (newtup != NULL &&
- (event == TRIGGER_EVENT_UPDATE && update_new_table))
+ ((event == TRIGGER_EVENT_INSERT && insert_new_table) ||
+ (event == TRIGGER_EVENT_UPDATE && update_new_table)))
{
Tuplestorestate *new_tuplestore;
- new_tuplestore = transition_capture->tcs_private->new_upd_tuplestore;
+ new_tuplestore = transition_capture->tcs_private->new_tuplestore;
if (original_insert_tuple != NULL)
tuplestore_puttuple(new_tuplestore, original_insert_tuple);
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 68675f97966..cc09895fa5c 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -22,7 +22,7 @@ OBJS = execAmi.o execCurrent.o execExpr.o execExprInterp.o \
nodeCustom.o nodeFunctionscan.o nodeGather.o \
nodeHash.o nodeHashjoin.o nodeIndexscan.o nodeIndexonlyscan.o \
nodeLimit.o nodeLockRows.o nodeGatherMerge.o \
- nodeMaterial.o nodeMergeAppend.o nodeMergejoin.o nodeMerge.o nodeModifyTable.o \
+ nodeMaterial.o nodeMergeAppend.o nodeMergejoin.o nodeModifyTable.o \
nodeNestloop.o nodeProjectSet.o nodeRecursiveunion.o nodeResult.o \
nodeSamplescan.o nodeSeqscan.o nodeSetOp.o nodeSort.o nodeUnique.o \
nodeValuesscan.o \
diff --git a/src/backend/executor/README b/src/backend/executor/README
index 05769772b77..0d7cd552eb6 100644
--- a/src/backend/executor/README
+++ b/src/backend/executor/README
@@ -37,16 +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 a CTID and a 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 partition table. If the CTID column is set we attempt to
-activate WHEN MATCHED actions, or if it is NULL then we will 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 e4d9b0b3f88..9a107aba561 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;
@@ -1358,9 +1357,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
@@ -2209,19 +2205,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/execPartition.c b/src/backend/executor/execPartition.c
index a6a7885abd1..9a131886491 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -67,8 +67,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;
@@ -91,22 +89,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.
@@ -311,25 +300,6 @@ ExecFindPartition(ResultRelInfo *resultRelInfo, PartitionDispatch *pd,
}
/*
- * Given OID of the partition leaf, return the index of the leaf in the
- * partition hierarchy.
- */
-int
-ExecFindPartitionByOid(PartitionTupleRouting *proute, Oid partoid)
-{
- int i;
-
- for (i = 0; i < proute->num_partitions; i++)
- {
- if (proute->partition_oids[i] == partoid)
- break;
- }
-
- Assert(i < proute->num_partitions);
- return i;
-}
-
-/*
* ExecInitPartitionInfo
* Initialize ResultRelInfo and other information for a partition if not
* already done
@@ -367,8 +337,6 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
rootrel,
estate->es_instrument);
- leaf_part_rri->ri_PartitionLeafIndex = partidx;
-
/*
* Verify result relation is a valid target for an INSERT. An UPDATE of a
* partition-key becomes a DELETE+INSERT operation, so this check is still
@@ -657,90 +625,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 971f92a938a..32891abbdf5 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -454,7 +454,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;
@@ -515,7 +515,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 b03db64e8e1..1b09868ff8e 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/nodeMerge.h"
#include "executor/nodeModifyTable.h"
#include "foreign/fdwapi.h"
#include "miscadmin.h"
@@ -63,17 +62,17 @@ 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);
static TupleConversionMap *tupconv_map_for_subplan(ModifyTableState *node,
int whichplan);
-/* flags for mt_merge_subcommands */
-#define MERGE_INSERT 0x01
-#define MERGE_UPDATE 0x02
-#define MERGE_DELETE 0x04
-
/*
* Verify that the tuples to be produced by INSERT or UPDATE match the
* target relation's rowtype
@@ -260,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;
@@ -392,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
@@ -627,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,
@@ -648,8 +629,6 @@ ExecDelete(ModifyTableState *mtstate,
EState *estate,
bool *tupleDeleted,
bool processReturning,
- HeapUpdateFailureData *hufdp,
- MergeActionState *actionState,
bool canSetTag)
{
ResultRelInfo *resultRelInfo;
@@ -663,14 +642,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;
@@ -683,7 +654,7 @@ ExecDelete(ModifyTableState *mtstate,
bool dodelete;
dodelete = ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
- tupleid, oldtuple, hufdp);
+ tupleid, oldtuple);
if (!dodelete) /* "do nothing" */
return NULL;
@@ -750,15 +721,6 @@ ldelete:;
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;
-
switch (result)
{
case HeapTupleSelfUpdated:
@@ -793,11 +755,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:
@@ -808,24 +766,14 @@ ldelete:;
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
-
if (!ItemPointerEquals(tupleid, &hufd.ctid))
{
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);
@@ -835,12 +783,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:
@@ -968,21 +911,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,
@@ -990,9 +922,6 @@ ExecUpdate(ModifyTableState *mtstate,
TupleTableSlot *planSlot,
EPQState *epqstate,
EState *estate,
- bool *tuple_updated,
- HeapUpdateFailureData *hufdp,
- MergeActionState *actionState,
bool canSetTag)
{
HeapTuple tuple;
@@ -1009,17 +938,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
@@ -1037,7 +955,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;
@@ -1083,6 +1001,7 @@ ExecUpdate(ModifyTableState *mtstate,
}
else
{
+ LockTupleMode lockmode;
bool partition_constraint_failed;
/*
@@ -1160,9 +1079,8 @@ lreplace:;
* Row movement, part 1. Delete the tuple, but skip RETURNING
* processing. We want to return rows from INSERT.
*/
- ExecDelete(mtstate, tupleid, oldtuple, planSlot, epqstate,
- estate, &tuple_deleted, false, hufdp, NULL,
- false);
+ ExecDelete(mtstate, tupleid, oldtuple, planSlot, epqstate, estate,
+ &tuple_deleted, false, false);
/*
* For some reason if DELETE didn't happen (e.g. trigger prevented
@@ -1198,36 +1116,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,
@@ -1237,16 +1135,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;
@@ -1284,16 +1178,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:
@@ -1338,42 +1223,26 @@ lreplace:;
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
-
if (!ItemPointerEquals(tupleid, &hufd.ctid))
{
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:
@@ -1402,9 +1271,6 @@ lreplace:;
estate, false, NULL, NIL);
}
- if (tuple_updated)
- *tuple_updated = true;
-
if (canSetTag)
(estate->es_processed)++;
@@ -1499,9 +1365,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,
@@ -1623,7 +1489,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;
@@ -1661,14 +1527,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;
@@ -1724,17 +1582,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;
@@ -1797,7 +1644,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,
@@ -2120,7 +1967,6 @@ ExecModifyTable(PlanState *pstate)
{
/* advance to next subplan if any */
node->mt_whichplan++;
-
if (node->mt_whichplan < node->mt_nplans)
{
resultRelInfo++;
@@ -2169,12 +2015,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)
@@ -2256,20 +2096,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);
break;
default:
elog(ERROR, "unknown operation");
@@ -2359,16 +2198,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)
{
@@ -2447,8 +2276,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);
@@ -2460,15 +2288,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
@@ -2659,106 +2478,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
}
}
- resultRelInfo = mtstate->resultRelInfo;
-
- if (node->mergeActionList)
- {
- ListCell *l;
- ExprContext *econtext;
- List *mergeMatchedActionStates = NIL;
- List *mergeNotMatchedActionStates = NIL;
- TupleDesc relationDesc = resultRelInfo->ri_RelationDesc->rd_att;
-
- 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;
-
- }
- }
-
/* select first subplan */
mtstate->mt_whichplan = 0;
subplan = (Plan *) linitial(node->plans);
@@ -2772,7 +2491,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
@@ -2800,7 +2519,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
break;
case CMD_UPDATE:
case CMD_DELETE:
- case CMD_MERGE:
junk_filter_needed = true;
break;
default:
@@ -2816,7 +2534,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);
@@ -2825,9 +2542,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;
@@ -2840,15 +2555,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 770ed3b1a88..c7293a60d78 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;
}
@@ -2980,9 +2977,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);
@@ -3046,34 +3040,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(mergeActionList);
-
- return newnode;
-}
-
-static MergeAction *
-_copyMergeAction(const MergeAction *from)
-{
- MergeAction *newnode = makeNode(MergeAction);
-
- COPY_SCALAR_FIELD(matched);
- COPY_SCALAR_FIELD(commandType);
- COPY_NODE_FIELD(condition);
- COPY_NODE_FIELD(qual);
- COPY_NODE_FIELD(stmt);
- COPY_NODE_FIELD(targetList);
-
- return newnode;
-}
-
static SelectStmt *
_copySelectStmt(const SelectStmt *from)
{
@@ -5136,12 +5102,6 @@ copyObjectImpl(const void *from)
case T_UpdateStmt:
retval = _copyUpdateStmt(from);
break;
- case T_MergeStmt:
- retval = _copyMergeStmt(from);
- break;
- case T_MergeAction:
- retval = _copyMergeAction(from);
- break;
case T_SelectStmt:
retval = _copySelectStmt(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 5a0151eece5..765b1be74b3 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -987,8 +987,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);
@@ -1045,30 +1043,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(mergeActionList);
-
- return true;
-}
-
-static bool
-_equalMergeAction(const MergeAction *a, const MergeAction *b)
-{
- COMPARE_SCALAR_FIELD(matched);
- COMPARE_SCALAR_FIELD(commandType);
- COMPARE_NODE_FIELD(condition);
- COMPARE_NODE_FIELD(qual);
- COMPARE_NODE_FIELD(stmt);
- COMPARE_NODE_FIELD(targetList);
-
- return true;
-}
-
-static bool
_equalSelectStmt(const SelectStmt *a, const SelectStmt *b)
{
COMPARE_NODE_FIELD(distinctClause);
@@ -3259,12 +3233,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_MergeAction:
- retval = _equalMergeAction(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 68e2cec66e4..6c76c41ebea 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_JoinExpr:
{
JoinExpr *join = (JoinExpr *) node;
@@ -2265,10 +2255,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))
@@ -2946,18 +2932,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_JoinExpr:
{
JoinExpr *join = (JoinExpr *) node;
@@ -3109,8 +3083,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 *);
@@ -3252,9 +3224,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,
@@ -3434,20 +3406,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->mergeActionList, 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 9ebea550485..f61ae03ac50 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,21 +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
-_outMergeAction(StringInfo str, const MergeAction *node)
-{
- WRITE_NODE_TYPE("MERGEACTION");
-
- WRITE_BOOL_FIELD(matched);
- WRITE_ENUM_FIELD(commandType, CmdType);
- WRITE_NODE_FIELD(condition);
- WRITE_NODE_FIELD(qual);
- /* We don't dump the stmt node */
- WRITE_NODE_FIELD(targetList);
}
static void
@@ -2130,7 +2114,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);
@@ -2138,8 +2121,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
@@ -2961,9 +2942,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);
}
@@ -3679,9 +3657,6 @@ outNode(StringInfo str, const void *obj)
case T_ModifyTable:
_outModifyTable(str, obj);
break;
- case T_MergeAction:
- _outMergeAction(str, obj);
- break;
case T_Append:
_outAppend(str, obj);
break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 694bb3fc404..d02d4ec5b7c 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);
@@ -1579,7 +1576,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);
@@ -1595,8 +1591,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();
}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 99d07360293..ccdd5cdaba2 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -288,13 +288,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);
@@ -2450,14 +2446,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);
@@ -6524,13 +6517,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;
@@ -6556,7 +6545,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;
@@ -6589,8 +6577,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 15c8d34c704..53ed6f8a17f 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);
@@ -1553,7 +1535,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 */
@@ -1612,15 +1593,12 @@ inheritance_planner(PlannerInfo *root)
partitioned_rels,
partColsUpdated,
resultRelations,
- 0,
subpaths,
subroots,
withCheckOptionLists,
returningLists,
rowMarks,
NULL,
- NULL,
- NULL,
SS_assign_special_param(root)));
}
@@ -2151,8 +2129,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)
{
@@ -2192,15 +2170,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 cd540a0df5b..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 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 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 416b3f95786..22133fcf120 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -3284,21 +3284,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;
@@ -3363,7 +3359,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;
@@ -3371,8 +3366,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 8a6baa7beae..0231f8bf7c6 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -1835,10 +1835,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 7eb9544efee..a4b5aaef44f 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 583ee321e1d..cd5ba2d4d8d 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -282,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
@@ -585,10 +584,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_and_condition
-%type <list> merge_when_list
-%type <node> merge_update merge_delete merge_insert
-
/*
* 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
@@ -656,8 +651,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
@@ -926,7 +920,6 @@ stmt :
| RefreshMatViewStmt
| LoadStmt
| LockStmt
- | MergeStmt
| NotifyStmt
| PrepareStmt
| ReassignOwnedStmt
@@ -10667,7 +10660,6 @@ ExplainableStmt:
| InsertStmt
| UpdateStmt
| DeleteStmt
- | MergeStmt
| DeclareCursorStmt
| CreateAsStmt
| CreateMatViewStmt
@@ -10730,7 +10722,6 @@ PreparableStmt:
| InsertStmt
| UpdateStmt
| DeleteStmt /* by default all are $$=$1 */
- | MergeStmt
;
/*****************************************************************************
@@ -11100,151 +11091,6 @@ set_target_list:
/*****************************************************************************
*
* QUERY:
- * MERGE STATEMENTS
- *
- *****************************************************************************/
-
-MergeStmt:
- MERGE INTO relation_expr_opt_alias
- USING table_ref
- ON a_expr
- merge_when_list
- {
- MergeStmt *m = makeNode(MergeStmt);
-
- m->relation = $3;
- m->source_relation = $5;
- m->join_condition = $7;
- m->mergeActionList = $8;
-
- $$ = (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_and_condition THEN merge_update
- {
- MergeAction *m = makeNode(MergeAction);
-
- m->matched = true;
- m->commandType = CMD_UPDATE;
- m->condition = $3;
- m->stmt = $5;
-
- $$ = (Node *)m;
- }
- | WHEN MATCHED opt_and_condition THEN merge_delete
- {
- MergeAction *m = makeNode(MergeAction);
-
- m->matched = true;
- m->commandType = CMD_DELETE;
- m->condition = $3;
- m->stmt = $5;
-
- $$ = (Node *)m;
- }
- | WHEN NOT MATCHED opt_and_condition THEN merge_insert
- {
- MergeAction *m = makeNode(MergeAction);
-
- m->matched = false;
- m->commandType = CMD_INSERT;
- m->condition = $4;
- m->stmt = $6;
-
- $$ = (Node *)m;
- }
- | WHEN NOT MATCHED opt_and_condition THEN DO NOTHING
- {
- MergeAction *m = makeNode(MergeAction);
-
- m->matched = false;
- m->commandType = CMD_NOTHING;
- m->condition = $4;
- m->stmt = NULL;
-
- $$ = (Node *)m;
- }
- ;
-
-opt_and_condition:
- AND a_expr { $$ = $2; }
- | { $$ = NULL; }
- ;
-
-merge_delete:
- DELETE_P
- {
- DeleteStmt *n = makeNode(DeleteStmt);
- $$ = (Node *)n;
- }
- ;
-
-merge_update:
- UPDATE SET set_clause_list
- {
- UpdateStmt *n = makeNode(UpdateStmt);
- n->targetList = $3;
-
- $$ = (Node *)n;
- }
- ;
-
-merge_insert:
- INSERT values_clause
- {
- InsertStmt *n = makeNode(InsertStmt);
- n->cols = NIL;
- n->selectStmt = $2;
-
- $$ = (Node *)n;
- }
- | INSERT OVERRIDING override_kind VALUE_P values_clause
- {
- InsertStmt *n = makeNode(InsertStmt);
- n->cols = NIL;
- n->override = $3;
- n->selectStmt = $5;
-
- $$ = (Node *)n;
- }
- | INSERT '(' insert_column_list ')' values_clause
- {
- InsertStmt *n = makeNode(InsertStmt);
- n->cols = $3;
- n->selectStmt = $5;
-
- $$ = (Node *)n;
- }
- | INSERT '(' insert_column_list ')' OVERRIDING override_kind VALUE_P values_clause
- {
- InsertStmt *n = makeNode(InsertStmt);
- n->cols = $3;
- n->override = $6;
- n->selectStmt = $8;
-
- $$ = (Node *)n;
- }
- | INSERT DEFAULT VALUES
- {
- InsertStmt *n = makeNode(InsertStmt);
- n->cols = NIL;
- n->selectStmt = NULL;
-
- $$ = (Node *)n;
- }
- ;
-
-/*****************************************************************************
- *
- * QUERY:
* CURSOR STATEMENTS
*
*****************************************************************************/
@@ -15242,10 +15088,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 544e7300b89..377a7ed6d0a 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 3cb761b4ed0..3a02307bd99 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,20 +1096,13 @@ 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 **namespace)
{
if (IsA(n, RangeVar))
@@ -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, namespace);
+ 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
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_relation.c b/src/backend/parser/parse_relation.c
index f7e11f969c0..053ae02c9fe 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 98239f569ae..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,50 +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:
- {
- InsertStmt *istmt = (InsertStmt *) action->stmt;
-
- action->targetList =
- rewriteTargetListIU(action->targetList,
- action->commandType,
- istmt->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 */
@@ -3447,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 6e85886e646..ce77a18bc96 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 84f25917368..b2dc9d18eac 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;
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 608f50b0616..4c0256b18a4 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -53,34 +53,23 @@ typedef enum LockTupleMode
* When heap_update, heap_delete, or heap_lock_tuple fail because the target
* tuple is already outdated, they fill in this struct to provide information
* to the caller about what happened.
- *
- * result is the result of HeapTupleSatisfiesUpdate, leading to the failure.
- * It's set to HeapTupleMayBeUpdated when there is no failure.
- *
* ctid is the target's ctid link: it is the same as the target's TID if the
* target was deleted, or the location of the replacement tuple if the target
* was updated.
- *
* xmax is the outdating transaction's XID. If the caller wants to visit the
* replacement tuple, it must check that this matches before believing the
* replacement is really a match.
- *
* cmax is the outdating command's CID, but only when the failure code is
* HeapTupleSelfUpdated (i.e., something in the current transaction outdated
* the tuple); otherwise cmax is zero. (We make this restriction because
* HeapTupleHeaderGetCmax doesn't work for tuples outdated in other
* transactions.)
- *
- * lockmode is only relevant for callers of heap_update() and is the mode which
- * the caller should use in case it needs to lock the updated tuple.
*/
typedef struct HeapUpdateFailureData
{
- HTSU_Result result;
ItemPointerData ctid;
TransactionId xmax;
CommandId cmax;
- LockTupleMode lockmode;
} HeapUpdateFailureData;
@@ -173,7 +162,7 @@ extern void heap_abort_speculative(Relation relation, HeapTuple tuple);
extern HTSU_Result heap_update(Relation relation, ItemPointer otid,
HeapTuple newtup,
CommandId cid, Snapshot crosscheck, bool wait,
- HeapUpdateFailureData *hufd);
+ HeapUpdateFailureData *hufd, LockTupleMode *lockmode);
extern HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple,
CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
bool follow_update,
diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h
index 1b79a803103..a5b8610fa22 100644
--- a/src/include/commands/trigger.h
+++ b/src/include/commands/trigger.h
@@ -206,8 +206,7 @@ extern bool ExecBRDeleteTriggers(EState *estate,
EPQState *epqstate,
ResultRelInfo *relinfo,
ItemPointer tupleid,
- HeapTuple fdw_trigtuple,
- HeapUpdateFailureData *hufdp);
+ HeapTuple fdw_trigtuple);
extern void ExecARDeleteTriggers(EState *estate,
ResultRelInfo *relinfo,
ItemPointer tupleid,
@@ -226,8 +225,7 @@ extern TupleTableSlot *ExecBRUpdateTriggers(EState *estate,
ResultRelInfo *relinfo,
ItemPointer tupleid,
HeapTuple fdw_trigtuple,
- TupleTableSlot *slot,
- HeapUpdateFailureData *hufdp);
+ TupleTableSlot *slot);
extern void ExecARUpdateTriggers(EState *estate,
ResultRelInfo *relinfo,
ItemPointer tupleid,
diff --git a/src/include/executor/execPartition.h b/src/include/executor/execPartition.h
index 9f55f6409ef..03a599ad57a 100644
--- a/src/include/executor/execPartition.h
+++ b/src/include/executor/execPartition.h
@@ -114,7 +114,6 @@ extern int ExecFindPartition(ResultRelInfo *resultRelInfo,
PartitionDispatch *pd,
TupleTableSlot *slot,
EState *estate);
-extern int ExecFindPartitionByOid(PartitionTupleRouting *proute, Oid partoid);
extern ResultRelInfo *ExecInitPartitionInfo(ModifyTableState *mtstate,
ResultRelInfo *resultRelInfo,
PartitionTupleRouting *proute,
diff --git a/src/include/executor/instrument.h b/src/include/executor/instrument.h
index 28eb0093d47..b72f91898ad 100644
--- a/src/include/executor/instrument.h
+++ b/src/include/executor/instrument.h
@@ -58,11 +58,8 @@ typedef struct Instrumentation
double total; /* Total total time (in seconds) */
double ntuples; /* Total tuples produced */
double nloops; /* # of run cycles for this node */
- double nfiltered1; /* # tuples removed by scanqual or joinqual OR
- * # tuples inserted by MERGE */
- double nfiltered2; /* # tuples removed by "other" quals OR
- * # tuples updated by MERGE */
- double nfiltered3; /* # tuples deleted by MERGE */
+ double nfiltered1; /* # tuples removed by scanqual or joinqual */
+ double nfiltered2; /* # tuples removed by "other" quals */
BufferUsage bufusage; /* Total buffer usage */
} Instrumentation;
diff --git a/src/include/executor/nodeModifyTable.h b/src/include/executor/nodeModifyTable.h
index 686cfa61710..0d7e579e1cb 100644
--- a/src/include/executor/nodeModifyTable.h
+++ b/src/include/executor/nodeModifyTable.h
@@ -18,26 +18,5 @@
extern ModifyTableState *ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags);
extern void ExecEndModifyTable(ModifyTableState *node);
extern void ExecReScanModifyTable(ModifyTableState *node);
-extern TupleTableSlot *ExecPrepareTupleRouting(ModifyTableState *mtstate,
- EState *estate,
- struct PartitionTupleRouting *proute,
- ResultRelInfo *targetRelInfo,
- TupleTableSlot *slot);
-extern TupleTableSlot *ExecDelete(ModifyTableState *mtstate,
- ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *planSlot,
- EPQState *epqstate, EState *estate, bool *tupleDeleted,
- bool processReturning, HeapUpdateFailureData *hufdp,
- MergeActionState *actionState, bool canSetTag);
-extern TupleTableSlot *ExecUpdate(ModifyTableState *mtstate,
- ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot,
- TupleTableSlot *planSlot, EPQState *epqstate, EState *estate,
- bool *tuple_updated, HeapUpdateFailureData *hufdp,
- MergeActionState *actionState, bool canSetTag);
-extern TupleTableSlot *ExecInsert(ModifyTableState *mtstate,
- TupleTableSlot *slot,
- TupleTableSlot *planSlot,
- EState *estate,
- MergeActionState *actionState,
- bool canSetTag);
#endif /* NODEMODIFYTABLE_H */
diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h
index 78410b9f772..e5bdaecc4e3 100644
--- a/src/include/executor/spi.h
+++ b/src/include/executor/spi.h
@@ -64,7 +64,6 @@ typedef struct _SPI_plan *SPIPlanPtr;
#define SPI_OK_REL_REGISTER 15
#define SPI_OK_REL_UNREGISTER 16
#define SPI_OK_TD_REGISTER 17
-#define SPI_OK_MERGE 18
#define SPI_OPT_NONATOMIC (1 << 0)
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index ff63d179b2a..6070a42b6fe 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -360,17 +360,8 @@ typedef struct JunkFilter
AttrNumber *jf_cleanMap;
TupleTableSlot *jf_resultSlot;
AttrNumber jf_junkAttNo;
- AttrNumber jf_otherJunkAttNo;
} JunkFilter;
-typedef struct MergeState
-{
- /* List of MERGE MATCHED action states */
- List *matchedActionStates;
- /* List of MERGE NOT MATCHED action states */
- List *notMatchedActionStates;
-} MergeState;
-
/*
* OnConflictSetState
*
@@ -461,38 +452,8 @@ typedef struct ResultRelInfo
/* relation descriptor for root partitioned table */
Relation ri_PartitionRoot;
-
- int ri_PartitionLeafIndex;
- /* for running MERGE on this result relation */
- MergeState *ri_mergeState;
-
- /*
- * While executing MERGE, the target relation is processed twice; once
- * as a target relation and once to run a join between the target and the
- * source. We generate two different RTEs for these two purposes, one with
- * rte->inh set to false and other with rte->inh set to true.
- *
- * Since the plan re-evaluated by EvalPlanQual uses the join RTE, we must
- * install the updated tuple in the scan corresponding to that RTE. The
- * following member tracks the index of the second RTE for EvalPlanQual
- * purposes. ri_mergeTargetRTI is non-zero only when MERGE is in-progress.
- * We use ri_mergeTargetRTI to run EvalPlanQual for MERGE and
- * ri_RangeTableIndex elsewhere.
- */
- Index ri_mergeTargetRTI;
} ResultRelInfo;
-/*
- * Get the Range table index for EvalPlanQual.
- *
- * We use the ri_mergeTargetRTI if set, otherwise use ri_RangeTableIndex.
- * ri_mergeTargetRTI should really be ever set iff we're running MERGE.
- */
-#define GetEPQRangeTableIndex(r) \
- (((r)->ri_mergeTargetRTI > 0) \
- ? (r)->ri_mergeTargetRTI \
- : (r)->ri_RangeTableIndex)
-
/* ----------------
* EState information
*
@@ -1005,11 +966,6 @@ typedef struct PlanState
if (((PlanState *)(node))->instrument) \
((PlanState *)(node))->instrument->nfiltered2 += (delta); \
} while(0)
-#define InstrCountFiltered3(node, delta) \
- do { \
- if (((PlanState *)(node))->instrument) \
- ((PlanState *)(node))->instrument->nfiltered3 += (delta); \
- } while(0)
/*
* EPQState is state for executing an EvalPlanQual recheck on a candidate
@@ -1057,27 +1013,13 @@ typedef struct ProjectSetState
} ProjectSetState;
/* ----------------
- * MergeActionState information
- * ----------------
- */
-typedef struct MergeActionState
-{
- NodeTag type;
- bool matched; /* true=MATCHED, false=NOT MATCHED */
- ExprState *whenqual; /* WHEN AND conditions */
- CmdType commandType; /* INSERT/UPDATE/DELETE/DO NOTHING */
- ProjectionInfo *proj; /* tuple projection info */
- TupleDesc tupDesc; /* tuple descriptor for projection */
-} MergeActionState;
-
-/* ----------------
* ModifyTableState information
* ----------------
*/
typedef struct ModifyTableState
{
PlanState ps; /* its first field is NodeTag */
- CmdType operation; /* INSERT, UPDATE, DELETE or MERGE */
+ CmdType operation; /* INSERT, UPDATE, or DELETE */
bool canSetTag; /* do we set the command tag/es_processed? */
bool mt_done; /* are we done? */
PlanState **mt_plans; /* subplans (one per target rel) */
@@ -1093,8 +1035,6 @@ typedef struct ModifyTableState
List *mt_excludedtlist; /* the excluded pseudo relation's tlist */
TupleTableSlot *mt_conflproj; /* CONFLICT ... SET ... projection target */
- TupleTableSlot *mt_mergeproj; /* MERGE action projection target */
-
/* Tuple-routing support info */
struct PartitionTupleRouting *mt_partition_tuple_routing;
@@ -1106,9 +1046,6 @@ typedef struct ModifyTableState
/* Per plan map for tuple conversion from child to root */
TupleConversionMap **mt_per_subplan_tupconv_maps;
-
- /* Flags showing which subcommands are present INS/UPD/DEL/DO NOTHING */
- int mt_merge_subcommands;
} ModifyTableState;
/* ----------------
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index fce48026b6d..443de227041 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -97,7 +97,6 @@ typedef enum NodeTag
T_PlanState,
T_ResultState,
T_ProjectSetState,
- T_MergeActionState,
T_ModifyTableState,
T_AppendState,
T_MergeAppendState,
@@ -309,8 +308,6 @@ typedef enum NodeTag
T_InsertStmt,
T_DeleteStmt,
T_UpdateStmt,
- T_MergeStmt,
- T_MergeAction,
T_SelectStmt,
T_AlterTableStmt,
T_AlterTableCmd,
@@ -660,8 +657,7 @@ typedef enum CmdType
CMD_SELECT, /* select stmt */
CMD_UPDATE, /* update stmt */
CMD_INSERT, /* insert stmt */
- CMD_DELETE, /* delete stmt */
- CMD_MERGE, /* merge stmt */
+ CMD_DELETE,
CMD_UTILITY, /* cmds like create, destroy, copy, vacuum,
* etc. */
CMD_NOTHING /* dummy command for instead nothing rules
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index d005beeba82..92082b3a7a2 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -38,7 +38,7 @@ typedef enum OverridingKind
typedef enum QuerySource
{
QSRC_ORIGINAL, /* original parsetree (explicit query) */
- QSRC_PARSER, /* added by parse analysis in MERGE */
+ QSRC_PARSER, /* added by parse analysis (now unused) */
QSRC_INSTEAD_RULE, /* added by unconditional INSTEAD rule */
QSRC_QUAL_INSTEAD_RULE, /* added by conditional INSTEAD rule */
QSRC_NON_INSTEAD_RULE /* added by non-INSTEAD rule */
@@ -107,7 +107,7 @@ typedef struct Query
{
NodeTag type;
- CmdType commandType; /* select|insert|update|delete|merge|utility */
+ CmdType commandType; /* select|insert|update|delete|utility */
QuerySource querySource; /* where did I come from? */
@@ -118,7 +118,7 @@ typedef struct Query
Node *utilityStmt; /* non-null if commandType == CMD_UTILITY */
int resultRelation; /* rtable index of target relation for
- * INSERT/UPDATE/DELETE/MERGE; 0 for SELECT */
+ * INSERT/UPDATE/DELETE; 0 for SELECT */
bool hasAggs; /* has aggregates in tlist or havingQual */
bool hasWindowFuncs; /* has window functions in tlist */
@@ -169,9 +169,6 @@ typedef struct Query
List *withCheckOptions; /* a list of WithCheckOption's, which are
* only added during rewrite and therefore
* are not written out as part of Query. */
- int mergeTarget_relation;
- List *mergeSourceTargetList;
- List *mergeActionList; /* list of actions for MERGE (only) */
/*
* The following two fields identify the portion of the source text string
@@ -1131,9 +1128,7 @@ typedef enum WCOKind
WCO_VIEW_CHECK, /* WCO on an auto-updatable view */
WCO_RLS_INSERT_CHECK, /* RLS INSERT WITH CHECK policy */
WCO_RLS_UPDATE_CHECK, /* RLS UPDATE WITH CHECK policy */
- WCO_RLS_CONFLICT_CHECK, /* RLS ON CONFLICT DO UPDATE USING policy */
- WCO_RLS_MERGE_UPDATE_CHECK, /* RLS MERGE UPDATE USING policy */
- WCO_RLS_MERGE_DELETE_CHECK /* RLS MERGE DELETE USING policy */
+ WCO_RLS_CONFLICT_CHECK /* RLS ON CONFLICT DO UPDATE USING policy */
} WCOKind;
typedef struct WithCheckOption
@@ -1509,30 +1504,6 @@ typedef struct UpdateStmt
} UpdateStmt;
/* ----------------------
- * Merge Statement
- * ----------------------
- */
-typedef struct MergeStmt
-{
- NodeTag type;
- RangeVar *relation; /* target relation to merge into */
- Node *source_relation; /* source relation */
- Node *join_condition; /* join condition between source and target */
- List *mergeActionList; /* list of MergeAction(s) */
-} MergeStmt;
-
-typedef struct MergeAction
-{
- NodeTag type;
- bool matched; /* true=MATCHED, false=NOT MATCHED */
- Node *condition; /* WHEN AND conditions (raw parser) */
- Node *qual; /* transformed WHEN AND conditions */
- CmdType commandType; /* INSERT/UPDATE/DELETE/DO NOTHING */
- Node *stmt; /* T_UpdateStmt etc */
- List *targetList; /* the target list (of ResTarget) */
-} MergeAction;
-
-/* ----------------------
* Select Statement
*
* A "simple" SELECT is represented in the output of gram.y by a single
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 0a797f0a052..c922216b7d1 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -18,7 +18,6 @@
#include "lib/stringinfo.h"
#include "nodes/bitmapset.h"
#include "nodes/lockoptions.h"
-#include "nodes/parsenodes.h"
#include "nodes/primnodes.h"
@@ -43,7 +42,7 @@ typedef struct PlannedStmt
{
NodeTag type;
- CmdType commandType; /* select|insert|update|delete|merge|utility */
+ CmdType commandType; /* select|insert|update|delete|utility */
uint64 queryId; /* query identifier (copied from Query) */
@@ -217,14 +216,13 @@ typedef struct ProjectSet
typedef struct ModifyTable
{
Plan plan;
- CmdType operation; /* INSERT, UPDATE, DELETE or MERGE */
+ CmdType operation; /* INSERT, UPDATE, or DELETE */
bool canSetTag; /* do we set the command tag/es_processed? */
Index nominalRelation; /* Parent RT index for use of EXPLAIN */
/* RT indexes of non-leaf tables in a partition tree */
List *partitioned_rels;
bool partColsUpdated; /* some part key in hierarchy updated */
List *resultRelations; /* integer list of RT indexes */
- Index mergeTargetRelation; /* RT index of the merge target */
int resultRelIndex; /* index of first resultRel in plan's list */
int rootResultRelIndex; /* index of the partitioned table root */
List *plans; /* plan(s) producing source data */
@@ -240,8 +238,6 @@ typedef struct ModifyTable
Node *onConflictWhere; /* WHERE for ON CONFLICT UPDATE */
Index exclRelRTI; /* RTI of the EXCLUDED pseudo relation */
List *exclRelTlist; /* tlist of the EXCLUDED pseudo relation */
- List *mergeSourceTargetList;
- List *mergeActionList; /* actions for MERGE */
} ModifyTable;
/* ----------------
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index a2dde70de58..ea5251c6be2 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -1670,7 +1670,7 @@ typedef struct LockRowsPath
} LockRowsPath;
/*
- * ModifyTablePath represents performing INSERT/UPDATE/DELETE/MERGE
+ * ModifyTablePath represents performing INSERT/UPDATE/DELETE modifications
*
* We represent most things that will be in the ModifyTable plan node
* literally, except we have child Path(s) not Plan(s). But analysis of the
@@ -1679,14 +1679,13 @@ typedef struct LockRowsPath
typedef struct ModifyTablePath
{
Path path;
- CmdType operation; /* INSERT, UPDATE, DELETE or MERGE */
+ CmdType operation; /* INSERT, UPDATE, or DELETE */
bool canSetTag; /* do we set the command tag/es_processed? */
Index nominalRelation; /* Parent RT index for use of EXPLAIN */
/* RT indexes of non-leaf tables in a partition tree */
List *partitioned_rels;
bool partColsUpdated; /* some part key in hierarchy updated */
List *resultRelations; /* integer list of RT indexes */
- Index mergeTargetRelation;/* RT index of merge target relation */
List *subpaths; /* Path(s) producing source data */
List *subroots; /* per-target-table PlannerInfos */
List *withCheckOptionLists; /* per-target-table WCO lists */
@@ -1694,8 +1693,6 @@ typedef struct ModifyTablePath
List *rowMarks; /* PlanRowMarks (non-locking only) */
OnConflictExpr *onconflict; /* ON CONFLICT clause, or NULL */
int epqParam; /* ID of Param for EvalPlanQual re-eval */
- List *mergeSourceTargetList;
- List *mergeActionList; /* actions for MERGE */
} ModifyTablePath;
/*
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 895bf6959da..381bc30813c 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -241,14 +241,11 @@ extern ModifyTablePath *create_modifytable_path(PlannerInfo *root,
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);
extern LimitPath *create_limit_path(PlannerInfo *root, RelOptInfo *rel,
Path *subpath,
Node *limitOffset, Node *limitCount,
diff --git a/src/include/parser/analyze.h b/src/include/parser/analyze.h
index 41fb10666e5..687ae1b5b7c 100644
--- a/src/include/parser/analyze.h
+++ b/src/include/parser/analyze.h
@@ -32,11 +32,6 @@ extern Query *parse_sub_analyze(Node *parseTree, ParseState *parentParseState,
bool locked_from_parent,
bool resolve_unknowns);
-extern List *transformInsertRow(ParseState *pstate, List *exprlist,
- List *stmtcols, List *icolumns, List *attrnos,
- bool strip_indirection);
-extern List *transformUpdateTargetList(ParseState *pstate,
- List *targetList);
extern Query *transformTopLevelStmt(ParseState *pstate, RawStmt *parseTree);
extern Query *transformStmt(ParseState *pstate, Node *parseTree);
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 4dff55a8e99..cf32197bc3e 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -244,10 +244,8 @@ PG_KEYWORD("locked", LOCKED, UNRESERVED_KEYWORD)
PG_KEYWORD("logged", LOGGED, UNRESERVED_KEYWORD)
PG_KEYWORD("mapping", MAPPING, UNRESERVED_KEYWORD)
PG_KEYWORD("match", MATCH, UNRESERVED_KEYWORD)
-PG_KEYWORD("matched", MATCHED, UNRESERVED_KEYWORD)
PG_KEYWORD("materialized", MATERIALIZED, UNRESERVED_KEYWORD)
PG_KEYWORD("maxvalue", MAXVALUE, UNRESERVED_KEYWORD)
-PG_KEYWORD("merge", MERGE, UNRESERVED_KEYWORD)
PG_KEYWORD("method", METHOD, UNRESERVED_KEYWORD)
PG_KEYWORD("minute", MINUTE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("minvalue", MINVALUE, UNRESERVED_KEYWORD)
diff --git a/src/include/parser/parse_clause.h b/src/include/parser/parse_clause.h
index 30121c98ed8..2c0e0928628 100644
--- a/src/include/parser/parse_clause.h
+++ b/src/include/parser/parse_clause.h
@@ -20,10 +20,7 @@ extern void transformFromClause(ParseState *pstate, List *frmList);
extern int setTargetTable(ParseState *pstate, RangeVar *relation,
bool inh, bool alsoSource, AclMode requiredPerms);
extern bool interpretOidsOption(List *defList, bool allowOids);
-extern Node *transformFromClauseItem(ParseState *pstate, Node *n,
- RangeTblEntry **top_rte, int *top_rti,
- RangeTblEntry **right_rte, int *right_rti,
- List **namespace);
+
extern Node *transformWhereClause(ParseState *pstate, Node *clause,
ParseExprKind exprKind, const char *constructName);
extern Node *transformLimitClause(ParseState *pstate, Node *clause,
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 3fd2151ccbe..0230543810f 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -50,7 +50,6 @@ typedef enum ParseExprKind
EXPR_KIND_INSERT_TARGET, /* INSERT target list item */
EXPR_KIND_UPDATE_SOURCE, /* UPDATE assignment source item */
EXPR_KIND_UPDATE_TARGET, /* UPDATE assignment target item */
- EXPR_KIND_MERGE_WHEN_AND, /* MERGE WHEN ... AND condition */
EXPR_KIND_GROUP_BY, /* GROUP BY */
EXPR_KIND_ORDER_BY, /* ORDER BY */
EXPR_KIND_DISTINCT_ON, /* DISTINCT ON */
@@ -128,7 +127,7 @@ typedef Node *(*CoerceParamHook) (ParseState *pstate, Param *param,
* p_parent_cte: CommonTableExpr that immediately contains the current query,
* if any.
*
- * p_target_relation: target relation, if query is INSERT/UPDATE/DELETE/MERGE
+ * p_target_relation: target relation, if query is INSERT, UPDATE, or DELETE.
*
* p_target_rangetblentry: target relation's entry in the rtable list.
*
@@ -182,7 +181,7 @@ struct ParseState
List *p_ctenamespace; /* current namespace for common table exprs */
List *p_future_ctes; /* common table exprs not yet in namespace */
CommonTableExpr *p_parent_cte; /* this query's containing CTE */
- Relation p_target_relation; /* INSERT/UPDATE/DELETE/MERGE target rel */
+ Relation p_target_relation; /* INSERT/UPDATE/DELETE target rel */
RangeTblEntry *p_target_rangetblentry; /* target rel's RTE */
bool p_is_insert; /* process assignment like INSERT not UPDATE */
List *p_windowdefs; /* raw representations of window clauses */
diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h
index 1ab5de39422..8128199fc31 100644
--- a/src/include/rewrite/rewriteHandler.h
+++ b/src/include/rewrite/rewriteHandler.h
@@ -25,7 +25,6 @@ extern void AcquireRewriteLocks(Query *parsetree,
extern Node *build_column_default(Relation rel, int attrno);
extern void rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte,
Relation target_relation);
-extern void rewriteTargetListMerge(Query *parsetree, Relation target_relation);
extern Query *get_view_query(Relation view);
extern const char *view_query_is_auto_updatable(Query *viewquery,
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index a432636322b..4c0114c514d 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -3055,9 +3055,9 @@ PQoidValue(const PGresult *res)
/*
* PQcmdTuples -
- * If the last command was INSERT/UPDATE/DELETE/MERGE/MOVE/FETCH/COPY,
- * return a string containing the number of inserted/affected tuples.
- * If not, return "".
+ * If the last command was INSERT/UPDATE/DELETE/MOVE/FETCH/COPY, return
+ * a string containing the number of inserted/affected tuples. If not,
+ * return "".
*
* XXX: this should probably return an int
*/
@@ -3084,8 +3084,7 @@ PQcmdTuples(PGresult *res)
strncmp(res->cmdStatus, "DELETE ", 7) == 0 ||
strncmp(res->cmdStatus, "UPDATE ", 7) == 0)
p = res->cmdStatus + 7;
- else if (strncmp(res->cmdStatus, "FETCH ", 6) == 0 ||
- strncmp(res->cmdStatus, "MERGE ", 6) == 0)
+ else if (strncmp(res->cmdStatus, "FETCH ", 6) == 0)
p = res->cmdStatus + 6;
else if (strncmp(res->cmdStatus, "MOVE ", 5) == 0 ||
strncmp(res->cmdStatus, "COPY ", 5) == 0)
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 67123f85c95..f574aa77f0c 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -3967,7 +3967,7 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
/*
* On the first call for this statement generate the plan, and detect
- * whether the statement is INSERT/UPDATE/DELETE/MERGE
+ * whether the statement is INSERT/UPDATE/DELETE
*/
if (expr->plan == NULL)
{
@@ -3988,7 +3988,6 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
{
if (q->commandType == CMD_INSERT ||
q->commandType == CMD_UPDATE ||
- q->commandType == CMD_MERGE ||
q->commandType == CMD_DELETE)
stmt->mod_stmt = true;
}
@@ -4046,7 +4045,6 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
case SPI_OK_INSERT_RETURNING:
case SPI_OK_UPDATE_RETURNING:
case SPI_OK_DELETE_RETURNING:
- case SPI_OK_MERGE:
Assert(stmt->mod_stmt);
exec_set_found(estate, (SPI_processed != 0));
break;
@@ -4224,7 +4222,6 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
case SPI_OK_INSERT_RETURNING:
case SPI_OK_UPDATE_RETURNING:
case SPI_OK_DELETE_RETURNING:
- case SPI_OK_MERGE:
case SPI_OK_UTILITY:
case SPI_OK_REWRITTEN:
break;
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index d928efa13ea..b8562ca8b4e 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -304,7 +304,6 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt);
%token <keyword> K_LAST
%token <keyword> K_LOG
%token <keyword> K_LOOP
-%token <keyword> K_MERGE
%token <keyword> K_MESSAGE
%token <keyword> K_MESSAGE_TEXT
%token <keyword> K_MOVE
@@ -1948,10 +1947,6 @@ stmt_execsql : K_IMPORT
{
$$ = make_execsql_stmt(K_INSERT, @1);
}
- | K_MERGE
- {
- $$ = make_execsql_stmt(K_MERGE, @1);
- }
| T_WORD
{
int tok;
@@ -2474,7 +2469,6 @@ unreserved_keyword :
| K_IS
| K_LAST
| K_LOG
- | K_MERGE
| K_MESSAGE
| K_MESSAGE_TEXT
| K_MOVE
@@ -2936,8 +2930,6 @@ make_execsql_stmt(int firsttoken, int location)
{
if (prev_tok == K_INSERT)
continue; /* INSERT INTO is not an INTO-target */
- if (prev_tok == K_MERGE)
- continue; /* MERGE INTO is not an INTO-target */
if (firsttoken == K_IMPORT)
continue; /* IMPORT ... INTO is not an INTO-target */
if (have_into)
diff --git a/src/pl/plpgsql/src/pl_scanner.c b/src/pl/plpgsql/src/pl_scanner.c
index 81b5bd1fe03..08614a89a87 100644
--- a/src/pl/plpgsql/src/pl_scanner.c
+++ b/src/pl/plpgsql/src/pl_scanner.c
@@ -138,7 +138,6 @@ static const ScanKeyword unreserved_keywords[] = {
PG_KEYWORD("is", K_IS, UNRESERVED_KEYWORD)
PG_KEYWORD("last", K_LAST, UNRESERVED_KEYWORD)
PG_KEYWORD("log", K_LOG, UNRESERVED_KEYWORD)
- PG_KEYWORD("merge", K_MERGE, UNRESERVED_KEYWORD)
PG_KEYWORD("message", K_MESSAGE, UNRESERVED_KEYWORD)
PG_KEYWORD("message_text", K_MESSAGE_TEXT, UNRESERVED_KEYWORD)
PG_KEYWORD("move", K_MOVE, UNRESERVED_KEYWORD)
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 3f617201d67..dc90fe532fd 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -846,8 +846,8 @@ typedef struct PLpgSQL_stmt_execsql
PLpgSQL_stmt_type cmd_type;
int lineno;
PLpgSQL_expr *sqlstmt;
- bool mod_stmt; /* is the stmt INSERT/UPDATE/DELETE/MERGE?
- * Note mod_stmt is set when we plan the query */
+ bool mod_stmt; /* is the stmt INSERT/UPDATE/DELETE? Note:
+ * mod_stmt is set when we plan the query */
bool into; /* INTO supplied? */
bool strict; /* INTO STRICT flag */
PLpgSQL_variable *target; /* INTO target (record or row) */
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index 99dd7c6bdbf..d3965fe73f7 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -33,10 +33,6 @@ test: insert-conflict-do-update
test: insert-conflict-do-update-2
test: insert-conflict-do-update-3
test: insert-conflict-toast
-test: merge-insert-update
-test: merge-delete
-test: merge-update
-test: merge-match-recheck
test: delete-abort-savept
test: delete-abort-savept-2
test: aborted-keyrevoke
diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out
index 3a6016c80a6..d7d5178f5d8 100644
--- a/src/test/regress/expected/identity.out
+++ b/src/test/regress/expected/identity.out
@@ -386,58 +386,3 @@ CREATE TABLE itest_child PARTITION OF itest_parent (
) FOR VALUES FROM ('2016-07-01') TO ('2016-08-01'); -- error
ERROR: identity columns are not supported on partitions
DROP TABLE itest_parent;
--- MERGE tests
-CREATE TABLE itest14 (a int GENERATED ALWAYS AS IDENTITY, b text);
-CREATE TABLE itest15 (a int GENERATED BY DEFAULT AS IDENTITY, b text);
-MERGE INTO itest14 t
-USING (SELECT 10 AS s_a, 'inserted by merge' AS s_b) s
-ON t.a = s.s_a
-WHEN NOT MATCHED THEN
- INSERT (a, b) VALUES (s.s_a, s.s_b);
-ERROR: cannot insert into column "a"
-DETAIL: Column "a" is an identity column defined as GENERATED ALWAYS.
-HINT: Use OVERRIDING SYSTEM VALUE to override.
-MERGE INTO itest14 t
-USING (SELECT 20 AS s_a, 'inserted by merge' AS s_b) s
-ON t.a = s.s_a
-WHEN NOT MATCHED THEN
- INSERT (a, b) OVERRIDING USER VALUE VALUES (s.s_a, s.s_b);
-ERROR: cannot insert into column "a"
-DETAIL: Column "a" is an identity column defined as GENERATED ALWAYS.
-HINT: Use OVERRIDING SYSTEM VALUE to override.
-MERGE INTO itest14 t
-USING (SELECT 30 AS s_a, 'inserted by merge' AS s_b) s
-ON t.a = s.s_a
-WHEN NOT MATCHED THEN
- INSERT (a, b) OVERRIDING SYSTEM VALUE VALUES (s.s_a, s.s_b);
-MERGE INTO itest15 t
-USING (SELECT 10 AS s_a, 'inserted by merge' AS s_b) s
-ON t.a = s.s_a
-WHEN NOT MATCHED THEN
- INSERT (a, b) VALUES (s.s_a, s.s_b);
-MERGE INTO itest15 t
-USING (SELECT 20 AS s_a, 'inserted by merge' AS s_b) s
-ON t.a = s.s_a
-WHEN NOT MATCHED THEN
- INSERT (a, b) OVERRIDING USER VALUE VALUES (s.s_a, s.s_b);
-MERGE INTO itest15 t
-USING (SELECT 30 AS s_a, 'inserted by merge' AS s_b) s
-ON t.a = s.s_a
-WHEN NOT MATCHED THEN
- INSERT (a, b) OVERRIDING SYSTEM VALUE VALUES (s.s_a, s.s_b);
-SELECT * FROM itest14;
- a | b
-----+-------------------
- 30 | inserted by merge
-(1 row)
-
-SELECT * FROM itest15;
- a | b
-----+-------------------
- 10 | inserted by merge
- 1 | inserted by merge
- 30 | inserted by merge
-(3 rows)
-
-DROP TABLE itest14;
-DROP TABLE itest15;
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 864f2c13457..ac8968d24f5 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -517,104 +517,6 @@ SELECT atest6 FROM atest6; -- ok
(0 rows)
COPY atest6 TO stdout; -- ok
--- test column privileges with MERGE
-SET SESSION AUTHORIZATION regress_priv_user1;
-CREATE TABLE mtarget (a int, b text);
-CREATE TABLE msource (a int, b text);
-INSERT INTO mtarget VALUES (1, 'init1'), (2, 'init2');
-INSERT INTO msource VALUES (1, 'source1'), (2, 'source2'), (3, 'source3');
-GRANT SELECT (a) ON msource TO regress_priv_user4;
-GRANT SELECT (a) ON mtarget TO regress_priv_user4;
-GRANT INSERT (a,b) ON mtarget TO regress_priv_user4;
-GRANT UPDATE (b) ON mtarget TO regress_priv_user4;
-SET SESSION AUTHORIZATION regress_priv_user4;
---
--- test source privileges
---
--- fail (no SELECT priv on s.b)
-MERGE INTO mtarget t USING msource s ON t.a = s.a
-WHEN MATCHED THEN
- UPDATE SET b = s.b
-WHEN NOT MATCHED THEN
- INSERT VALUES (a, NULL);
-ERROR: permission denied for table msource
--- fail (s.b used in the INSERTed values)
-MERGE INTO mtarget t USING msource s ON t.a = s.a
-WHEN MATCHED THEN
- UPDATE SET b = 'x'
-WHEN NOT MATCHED THEN
- INSERT VALUES (a, b);
-ERROR: permission denied for table msource
--- fail (s.b used in the WHEN quals)
-MERGE INTO mtarget t USING msource s ON t.a = s.a
-WHEN MATCHED AND s.b = 'x' THEN
- UPDATE SET b = 'x'
-WHEN NOT MATCHED THEN
- INSERT VALUES (a, NULL);
-ERROR: permission denied for table msource
--- this should be ok since only s.a is accessed
-BEGIN;
-MERGE INTO mtarget t USING msource s ON t.a = s.a
-WHEN MATCHED THEN
- UPDATE SET b = 'ok'
-WHEN NOT MATCHED THEN
- INSERT VALUES (a, NULL);
-ROLLBACK;
-SET SESSION AUTHORIZATION regress_priv_user1;
-GRANT SELECT (b) ON msource TO regress_priv_user4;
-SET SESSION AUTHORIZATION regress_priv_user4;
--- should now be ok
-BEGIN;
-MERGE INTO mtarget t USING msource s ON t.a = s.a
-WHEN MATCHED THEN
- UPDATE SET b = s.b
-WHEN NOT MATCHED THEN
- INSERT VALUES (a, b);
-ROLLBACK;
---
--- test target privileges
---
--- fail (no SELECT priv on t.b)
-MERGE INTO mtarget t USING msource s ON t.a = s.a
-WHEN MATCHED THEN
- UPDATE SET b = t.b
-WHEN NOT MATCHED THEN
- INSERT VALUES (a, NULL);
-ERROR: permission denied for table mtarget
--- fail (no UPDATE on t.a)
-MERGE INTO mtarget t USING msource s ON t.a = s.a
-WHEN MATCHED THEN
- UPDATE SET b = s.b, a = t.a + 1
-WHEN NOT MATCHED THEN
- INSERT VALUES (a, b);
-ERROR: permission denied for table mtarget
--- fail (no SELECT on t.b)
-MERGE INTO mtarget t USING msource s ON t.a = s.a
-WHEN MATCHED AND t.b IS NOT NULL THEN
- UPDATE SET b = s.b
-WHEN NOT MATCHED THEN
- INSERT VALUES (a, b);
-ERROR: permission denied for table mtarget
--- ok
-BEGIN;
-MERGE INTO mtarget t USING msource s ON t.a = s.a
-WHEN MATCHED THEN
- UPDATE SET b = s.b;
-ROLLBACK;
--- fail (no DELETE)
-MERGE INTO mtarget t USING msource s ON t.a = s.a
-WHEN MATCHED AND t.b IS NOT NULL THEN
- DELETE;
-ERROR: permission denied for table mtarget
--- grant delete privileges
-SET SESSION AUTHORIZATION regress_priv_user1;
-GRANT DELETE ON mtarget TO regress_priv_user4;
--- should be ok now
-BEGIN;
-MERGE INTO mtarget t USING msource s ON t.a = s.a
-WHEN MATCHED AND t.b IS NOT NULL THEN
- DELETE;
-ROLLBACK;
-- check error reporting with column privs
SET SESSION AUTHORIZATION regress_priv_user1;
CREATE TABLE t1 (c1 int, c2 int, c3 int check (c3 < 5), primary key (c1, c2));
diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out
index bf7af3ba826..f1ae40df61c 100644
--- a/src/test/regress/expected/rowsecurity.out
+++ b/src/test/regress/expected/rowsecurity.out
@@ -2139,188 +2139,6 @@ INSERT INTO document VALUES (1, (SELECT cid from category WHERE cname = 'novel')
ON CONFLICT (did) DO UPDATE SET dauthor = 'regress_rls_carol';
ERROR: new row violates row-level security policy for table "document"
--
--- MERGE
---
-RESET SESSION AUTHORIZATION;
-DROP POLICY p3_with_all ON document;
-ALTER TABLE document ADD COLUMN dnotes text DEFAULT '';
--- all documents are readable
-CREATE POLICY p1 ON document FOR SELECT USING (true);
--- one may insert documents only authored by them
-CREATE POLICY p2 ON document FOR INSERT WITH CHECK (dauthor = current_user);
--- one may only update documents in 'novel' category
-CREATE POLICY p3 ON document FOR UPDATE
- USING (cid = (SELECT cid from category WHERE cname = 'novel'))
- WITH CHECK (dauthor = current_user);
--- one may only delete documents in 'manga' category
-CREATE POLICY p4 ON document FOR DELETE
- USING (cid = (SELECT cid from category WHERE cname = 'manga'));
-SELECT * FROM document;
- did | cid | dlevel | dauthor | dtitle | dnotes
------+-----+--------+-------------------+----------------------------------+--------
- 1 | 11 | 1 | regress_rls_bob | my first novel |
- 3 | 22 | 2 | regress_rls_bob | my science fiction |
- 4 | 44 | 1 | regress_rls_bob | my first manga |
- 5 | 44 | 2 | regress_rls_bob | my second manga |
- 6 | 22 | 1 | regress_rls_carol | great science fiction |
- 7 | 33 | 2 | regress_rls_carol | great technology book |
- 8 | 44 | 1 | regress_rls_carol | great manga |
- 9 | 22 | 1 | regress_rls_dave | awesome science fiction |
- 10 | 33 | 2 | regress_rls_dave | awesome technology book |
- 11 | 33 | 1 | regress_rls_carol | hoge |
- 33 | 22 | 1 | regress_rls_bob | okay science fiction |
- 2 | 11 | 2 | regress_rls_bob | my first novel |
- 78 | 33 | 1 | regress_rls_bob | some technology novel |
- 79 | 33 | 1 | regress_rls_bob | technology book, can only insert |
-(14 rows)
-
-SET SESSION AUTHORIZATION regress_rls_bob;
--- Fails, since update violates WITH CHECK qual on dauthor
-MERGE INTO document d
-USING (SELECT 1 as sdid) s
-ON did = s.sdid
-WHEN MATCHED THEN
- UPDATE SET dnotes = dnotes || ' notes added by merge1 ', dauthor = 'regress_rls_alice';
-ERROR: new row violates row-level security policy for table "document"
--- Should be OK since USING and WITH CHECK quals pass
-MERGE INTO document d
-USING (SELECT 1 as sdid) s
-ON did = s.sdid
-WHEN MATCHED THEN
- UPDATE SET dnotes = dnotes || ' notes added by merge2 ';
--- Even when dauthor is updated explicitly, but to the existing value
-MERGE INTO document d
-USING (SELECT 1 as sdid) s
-ON did = s.sdid
-WHEN MATCHED THEN
- UPDATE SET dnotes = dnotes || ' notes added by merge3 ', dauthor = 'regress_rls_bob';
--- There is a MATCH for did = 3, but UPDATE's USING qual does not allow
--- updating an item in category 'science fiction'
-MERGE INTO document d
-USING (SELECT 3 as sdid) s
-ON did = s.sdid
-WHEN MATCHED THEN
- UPDATE SET dnotes = dnotes || ' notes added by merge ';
-ERROR: target row violates row-level security policy (USING expression) for table "document"
--- The same thing with DELETE action, but fails again because no permissions
--- to delete items in 'science fiction' category that did 3 belongs to.
-MERGE INTO document d
-USING (SELECT 3 as sdid) s
-ON did = s.sdid
-WHEN MATCHED THEN
- DELETE;
-ERROR: target row violates row-level security policy (USING expression) for table "document"
--- Document with did 4 belongs to 'manga' category which is allowed for
--- deletion. But this fails because the UPDATE action is matched first and
--- UPDATE policy does not allow updation in the category.
-MERGE INTO document d
-USING (SELECT 4 as sdid) s
-ON did = s.sdid
-WHEN MATCHED AND dnotes = '' THEN
- UPDATE SET dnotes = dnotes || ' notes added by merge '
-WHEN MATCHED THEN
- DELETE;
-ERROR: target row violates row-level security policy (USING expression) for table "document"
--- UPDATE action is not matched this time because of the WHEN AND qual.
--- DELETE still fails because role regress_rls_bob does not have SELECT
--- privileges on 'manga' category row in the category table.
-MERGE INTO document d
-USING (SELECT 4 as sdid) s
-ON did = s.sdid
-WHEN MATCHED AND dnotes <> '' THEN
- UPDATE SET dnotes = dnotes || ' notes added by merge '
-WHEN MATCHED THEN
- DELETE;
-ERROR: target row violates row-level security policy (USING expression) for table "document"
-SELECT * FROM document WHERE did = 4;
- did | cid | dlevel | dauthor | dtitle | dnotes
------+-----+--------+-----------------+----------------+--------
- 4 | 44 | 1 | regress_rls_bob | my first manga |
-(1 row)
-
--- Switch to regress_rls_carol role and try the DELETE again. It should succeed
--- this time
-RESET SESSION AUTHORIZATION;
-SET SESSION AUTHORIZATION regress_rls_carol;
-MERGE INTO document d
-USING (SELECT 4 as sdid) s
-ON did = s.sdid
-WHEN MATCHED AND dnotes <> '' THEN
- UPDATE SET dnotes = dnotes || ' notes added by merge '
-WHEN MATCHED THEN
- DELETE;
--- Switch back to regress_rls_bob role
-RESET SESSION AUTHORIZATION;
-SET SESSION AUTHORIZATION regress_rls_bob;
--- Try INSERT action. This fails because we are trying to insert
--- dauthor = regress_rls_dave and INSERT's WITH CHECK does not allow
--- that
-MERGE INTO document d
-USING (SELECT 12 as sdid) s
-ON did = s.sdid
-WHEN MATCHED THEN
- DELETE
-WHEN NOT MATCHED THEN
- INSERT VALUES (12, 11, 1, 'regress_rls_dave', 'another novel');
-ERROR: new row violates row-level security policy for table "document"
--- This should be fine
-MERGE INTO document d
-USING (SELECT 12 as sdid) s
-ON did = s.sdid
-WHEN MATCHED THEN
- DELETE
-WHEN NOT MATCHED THEN
- INSERT VALUES (12, 11, 1, 'regress_rls_bob', 'another novel');
--- ok
-MERGE INTO document d
-USING (SELECT 1 as sdid) s
-ON did = s.sdid
-WHEN MATCHED THEN
- UPDATE SET dnotes = dnotes || ' notes added by merge4 '
-WHEN NOT MATCHED THEN
- INSERT VALUES (12, 11, 1, 'regress_rls_bob', 'another novel');
--- drop and create a new SELECT policy which prevents us from reading
--- any document except with category 'magna'
-RESET SESSION AUTHORIZATION;
-DROP POLICY p1 ON document;
-CREATE POLICY p1 ON document FOR SELECT
- USING (cid = (SELECT cid from category WHERE cname = 'manga'));
-SET SESSION AUTHORIZATION regress_rls_bob;
--- MERGE can no longer see the matching row and hence attempts the
--- NOT MATCHED action, which results in unique key violation
-MERGE INTO document d
-USING (SELECT 1 as sdid) s
-ON did = s.sdid
-WHEN MATCHED THEN
- UPDATE SET dnotes = dnotes || ' notes added by merge5 '
-WHEN NOT MATCHED THEN
- INSERT VALUES (12, 11, 1, 'regress_rls_bob', 'another novel');
-ERROR: duplicate key value violates unique constraint "document_pkey"
-RESET SESSION AUTHORIZATION;
--- drop the restrictive SELECT policy so that we can look at the
--- final state of the table
-DROP POLICY p1 ON document;
--- Just check everything went per plan
-SELECT * FROM document;
- did | cid | dlevel | dauthor | dtitle | dnotes
------+-----+--------+-------------------+----------------------------------+-----------------------------------------------------------------------
- 3 | 22 | 2 | regress_rls_bob | my science fiction |
- 5 | 44 | 2 | regress_rls_bob | my second manga |
- 6 | 22 | 1 | regress_rls_carol | great science fiction |
- 7 | 33 | 2 | regress_rls_carol | great technology book |
- 8 | 44 | 1 | regress_rls_carol | great manga |
- 9 | 22 | 1 | regress_rls_dave | awesome science fiction |
- 10 | 33 | 2 | regress_rls_dave | awesome technology book |
- 11 | 33 | 1 | regress_rls_carol | hoge |
- 33 | 22 | 1 | regress_rls_bob | okay science fiction |
- 2 | 11 | 2 | regress_rls_bob | my first novel |
- 78 | 33 | 1 | regress_rls_bob | some technology novel |
- 79 | 33 | 1 | regress_rls_bob | technology book, can only insert |
- 12 | 11 | 1 | regress_rls_bob | another novel |
- 1 | 11 | 1 | regress_rls_bob | my first novel | notes added by merge2 notes added by merge3 notes added by merge4
-(14 rows)
-
---
-- ROLE/GROUP
--
SET SESSION AUTHORIZATION regress_rls_alice;
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index a4ec5690aa2..ae0cd253d5f 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -3265,37 +3265,6 @@ CREATE RULE rules_parted_table_insert AS ON INSERT to rules_parted_table
ALTER RULE rules_parted_table_insert ON rules_parted_table RENAME TO rules_parted_table_insert_redirect;
DROP TABLE rules_parted_table;
--
--- test MERGE
---
-CREATE TABLE rule_merge1 (a int, b text);
-CREATE TABLE rule_merge2 (a int, b text);
-CREATE RULE rule1 AS ON INSERT TO rule_merge1
- DO INSTEAD INSERT INTO rule_merge2 VALUES (NEW.*);
-CREATE RULE rule2 AS ON UPDATE TO rule_merge1
- DO INSTEAD UPDATE rule_merge2 SET a = NEW.a, b = NEW.b
- WHERE a = OLD.a;
-CREATE RULE rule3 AS ON DELETE TO rule_merge1
- DO INSTEAD DELETE FROM rule_merge2 WHERE a = OLD.a;
--- MERGE not supported for table with rules
-MERGE INTO rule_merge1 t USING (SELECT 1 AS a) s
- ON t.a = s.a
- WHEN MATCHED AND t.a < 2 THEN
- UPDATE SET b = b || ' updated by merge'
- WHEN MATCHED AND t.a > 2 THEN
- DELETE
- WHEN NOT MATCHED THEN
- INSERT VALUES (s.a, '');
-ERROR: MERGE is not supported for relations with rules
--- should be ok with the other table though
-MERGE INTO rule_merge2 t USING (SELECT 1 AS a) s
- ON t.a = s.a
- WHEN MATCHED AND t.a < 2 THEN
- UPDATE SET b = b || ' updated by merge'
- WHEN MATCHED AND t.a > 2 THEN
- DELETE
- WHEN NOT MATCHED THEN
- INSERT VALUES (s.a, '');
---
-- Test enabling/disabling
--
CREATE TABLE ruletest1 (a int);
diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out
index 7f4a94ef7dc..387e40d67d4 100644
--- a/src/test/regress/expected/triggers.out
+++ b/src/test/regress/expected/triggers.out
@@ -2761,54 +2761,6 @@ delete from self_ref where a = 1;
NOTICE: trigger_func(self_ref) called: action = DELETE, when = BEFORE, level = STATEMENT
NOTICE: trigger = self_ref_s_trig, old table = (1,), (2,1), (3,2), (4,3)
drop table self_ref;
---
--- test transition tables with MERGE
---
-create table merge_target_table (a int primary key, b text);
-create trigger merge_target_table_insert_trig
- after insert on merge_target_table referencing new table as new_table
- for each statement execute procedure dump_insert();
-create trigger merge_target_table_update_trig
- after update on merge_target_table referencing old table as old_table new table as new_table
- for each statement execute procedure dump_update();
-create trigger merge_target_table_delete_trig
- after delete on merge_target_table referencing old table as old_table
- for each statement execute procedure dump_delete();
-create table merge_source_table (a int, b text);
-insert into merge_source_table
- values (1, 'initial1'), (2, 'initial2'),
- (3, 'initial3'), (4, 'initial4');
-merge into merge_target_table t
-using merge_source_table s
-on t.a = s.a
-when not matched then
- insert values (a, b);
-NOTICE: trigger = merge_target_table_insert_trig, new table = (1,initial1), (2,initial2), (3,initial3), (4,initial4)
-merge into merge_target_table t
-using merge_source_table s
-on t.a = s.a
-when matched and s.a <= 2 then
- update set b = t.b || ' updated by merge'
-when matched and s.a > 2 then
- delete
-when not matched then
- insert values (a, b);
-NOTICE: trigger = merge_target_table_delete_trig, old table = (3,initial3), (4,initial4)
-NOTICE: trigger = merge_target_table_update_trig, old table = (1,initial1), (2,initial2), new table = (1,"initial1 updated by merge"), (2,"initial2 updated by merge")
-NOTICE: trigger = merge_target_table_insert_trig, new table = <NULL>
-merge into merge_target_table t
-using merge_source_table s
-on t.a = s.a
-when matched and s.a <= 2 then
- update set b = t.b || ' updated again by merge'
-when matched and s.a > 2 then
- delete
-when not matched then
- insert values (a, b);
-NOTICE: trigger = merge_target_table_delete_trig, old table = <NULL>
-NOTICE: trigger = merge_target_table_update_trig, old table = (1,"initial1 updated by merge"), (2,"initial2 updated by merge"), new table = (1,"initial1 updated by merge updated again by merge"), (2,"initial2 updated by merge updated again by merge")
-NOTICE: trigger = merge_target_table_insert_trig, new table = (3,initial3), (4,initial4)
-drop table merge_source_table, merge_target_table;
-- cleanup
drop function dump_insert();
drop function dump_update();
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 20d67457308..d858a0e7db4 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -84,7 +84,7 @@ test: select_into select_distinct select_distinct_on select_implicit select_havi
# ----------
# Another group of parallel tests
# ----------
-test: brin gin gist spgist privileges init_privs security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator password func_index merge
+test: brin gin gist spgist privileges init_privs security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator password func_index
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index a08169f256a..99f8ca37ba9 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -123,7 +123,6 @@ test: tablesample
test: groupingsets
test: drop_operator
test: password
-test: merge
test: alter_generic
test: alter_operator
test: misc
diff --git a/src/test/regress/sql/identity.sql b/src/test/regress/sql/identity.sql
index f8f34eaf185..a35f331f4e0 100644
--- a/src/test/regress/sql/identity.sql
+++ b/src/test/regress/sql/identity.sql
@@ -246,48 +246,3 @@ CREATE TABLE itest_child PARTITION OF itest_parent (
f3 WITH OPTIONS GENERATED ALWAYS AS IDENTITY
) FOR VALUES FROM ('2016-07-01') TO ('2016-08-01'); -- error
DROP TABLE itest_parent;
-
--- MERGE tests
-CREATE TABLE itest14 (a int GENERATED ALWAYS AS IDENTITY, b text);
-CREATE TABLE itest15 (a int GENERATED BY DEFAULT AS IDENTITY, b text);
-
-MERGE INTO itest14 t
-USING (SELECT 10 AS s_a, 'inserted by merge' AS s_b) s
-ON t.a = s.s_a
-WHEN NOT MATCHED THEN
- INSERT (a, b) VALUES (s.s_a, s.s_b);
-
-MERGE INTO itest14 t
-USING (SELECT 20 AS s_a, 'inserted by merge' AS s_b) s
-ON t.a = s.s_a
-WHEN NOT MATCHED THEN
- INSERT (a, b) OVERRIDING USER VALUE VALUES (s.s_a, s.s_b);
-
-MERGE INTO itest14 t
-USING (SELECT 30 AS s_a, 'inserted by merge' AS s_b) s
-ON t.a = s.s_a
-WHEN NOT MATCHED THEN
- INSERT (a, b) OVERRIDING SYSTEM VALUE VALUES (s.s_a, s.s_b);
-
-MERGE INTO itest15 t
-USING (SELECT 10 AS s_a, 'inserted by merge' AS s_b) s
-ON t.a = s.s_a
-WHEN NOT MATCHED THEN
- INSERT (a, b) VALUES (s.s_a, s.s_b);
-
-MERGE INTO itest15 t
-USING (SELECT 20 AS s_a, 'inserted by merge' AS s_b) s
-ON t.a = s.s_a
-WHEN NOT MATCHED THEN
- INSERT (a, b) OVERRIDING USER VALUE VALUES (s.s_a, s.s_b);
-
-MERGE INTO itest15 t
-USING (SELECT 30 AS s_a, 'inserted by merge' AS s_b) s
-ON t.a = s.s_a
-WHEN NOT MATCHED THEN
- INSERT (a, b) OVERRIDING SYSTEM VALUE VALUES (s.s_a, s.s_b);
-
-SELECT * FROM itest14;
-SELECT * FROM itest15;
-DROP TABLE itest14;
-DROP TABLE itest15;
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index 0a8abf20769..f7f3bbbeeb6 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -349,114 +349,6 @@ UPDATE atest5 SET one = 1; -- fail
SELECT atest6 FROM atest6; -- ok
COPY atest6 TO stdout; -- ok
--- test column privileges with MERGE
-SET SESSION AUTHORIZATION regress_priv_user1;
-CREATE TABLE mtarget (a int, b text);
-CREATE TABLE msource (a int, b text);
-INSERT INTO mtarget VALUES (1, 'init1'), (2, 'init2');
-INSERT INTO msource VALUES (1, 'source1'), (2, 'source2'), (3, 'source3');
-
-GRANT SELECT (a) ON msource TO regress_priv_user4;
-GRANT SELECT (a) ON mtarget TO regress_priv_user4;
-GRANT INSERT (a,b) ON mtarget TO regress_priv_user4;
-GRANT UPDATE (b) ON mtarget TO regress_priv_user4;
-
-SET SESSION AUTHORIZATION regress_priv_user4;
-
---
--- test source privileges
---
-
--- fail (no SELECT priv on s.b)
-MERGE INTO mtarget t USING msource s ON t.a = s.a
-WHEN MATCHED THEN
- UPDATE SET b = s.b
-WHEN NOT MATCHED THEN
- INSERT VALUES (a, NULL);
-
--- fail (s.b used in the INSERTed values)
-MERGE INTO mtarget t USING msource s ON t.a = s.a
-WHEN MATCHED THEN
- UPDATE SET b = 'x'
-WHEN NOT MATCHED THEN
- INSERT VALUES (a, b);
-
--- fail (s.b used in the WHEN quals)
-MERGE INTO mtarget t USING msource s ON t.a = s.a
-WHEN MATCHED AND s.b = 'x' THEN
- UPDATE SET b = 'x'
-WHEN NOT MATCHED THEN
- INSERT VALUES (a, NULL);
-
--- this should be ok since only s.a is accessed
-BEGIN;
-MERGE INTO mtarget t USING msource s ON t.a = s.a
-WHEN MATCHED THEN
- UPDATE SET b = 'ok'
-WHEN NOT MATCHED THEN
- INSERT VALUES (a, NULL);
-ROLLBACK;
-
-SET SESSION AUTHORIZATION regress_priv_user1;
-GRANT SELECT (b) ON msource TO regress_priv_user4;
-SET SESSION AUTHORIZATION regress_priv_user4;
-
--- should now be ok
-BEGIN;
-MERGE INTO mtarget t USING msource s ON t.a = s.a
-WHEN MATCHED THEN
- UPDATE SET b = s.b
-WHEN NOT MATCHED THEN
- INSERT VALUES (a, b);
-ROLLBACK;
-
---
--- test target privileges
---
-
--- fail (no SELECT priv on t.b)
-MERGE INTO mtarget t USING msource s ON t.a = s.a
-WHEN MATCHED THEN
- UPDATE SET b = t.b
-WHEN NOT MATCHED THEN
- INSERT VALUES (a, NULL);
-
--- fail (no UPDATE on t.a)
-MERGE INTO mtarget t USING msource s ON t.a = s.a
-WHEN MATCHED THEN
- UPDATE SET b = s.b, a = t.a + 1
-WHEN NOT MATCHED THEN
- INSERT VALUES (a, b);
-
--- fail (no SELECT on t.b)
-MERGE INTO mtarget t USING msource s ON t.a = s.a
-WHEN MATCHED AND t.b IS NOT NULL THEN
- UPDATE SET b = s.b
-WHEN NOT MATCHED THEN
- INSERT VALUES (a, b);
-
--- ok
-BEGIN;
-MERGE INTO mtarget t USING msource s ON t.a = s.a
-WHEN MATCHED THEN
- UPDATE SET b = s.b;
-ROLLBACK;
-
--- fail (no DELETE)
-MERGE INTO mtarget t USING msource s ON t.a = s.a
-WHEN MATCHED AND t.b IS NOT NULL THEN
- DELETE;
-
--- grant delete privileges
-SET SESSION AUTHORIZATION regress_priv_user1;
-GRANT DELETE ON mtarget TO regress_priv_user4;
--- should be ok now
-BEGIN;
-MERGE INTO mtarget t USING msource s ON t.a = s.a
-WHEN MATCHED AND t.b IS NOT NULL THEN
- DELETE;
-ROLLBACK;
-
-- check error reporting with column privs
SET SESSION AUTHORIZATION regress_priv_user1;
CREATE TABLE t1 (c1 int, c2 int, c3 int check (c3 < 5), primary key (c1, c2));
diff --git a/src/test/regress/sql/rowsecurity.sql b/src/test/regress/sql/rowsecurity.sql
index 6c752089980..f3a31dbee03 100644
--- a/src/test/regress/sql/rowsecurity.sql
+++ b/src/test/regress/sql/rowsecurity.sql
@@ -813,162 +813,6 @@ INSERT INTO document VALUES (1, (SELECT cid from category WHERE cname = 'novel')
ON CONFLICT (did) DO UPDATE SET dauthor = 'regress_rls_carol';
--
--- MERGE
---
-RESET SESSION AUTHORIZATION;
-DROP POLICY p3_with_all ON document;
-
-ALTER TABLE document ADD COLUMN dnotes text DEFAULT '';
--- all documents are readable
-CREATE POLICY p1 ON document FOR SELECT USING (true);
--- one may insert documents only authored by them
-CREATE POLICY p2 ON document FOR INSERT WITH CHECK (dauthor = current_user);
--- one may only update documents in 'novel' category
-CREATE POLICY p3 ON document FOR UPDATE
- USING (cid = (SELECT cid from category WHERE cname = 'novel'))
- WITH CHECK (dauthor = current_user);
--- one may only delete documents in 'manga' category
-CREATE POLICY p4 ON document FOR DELETE
- USING (cid = (SELECT cid from category WHERE cname = 'manga'));
-
-SELECT * FROM document;
-
-SET SESSION AUTHORIZATION regress_rls_bob;
-
--- Fails, since update violates WITH CHECK qual on dauthor
-MERGE INTO document d
-USING (SELECT 1 as sdid) s
-ON did = s.sdid
-WHEN MATCHED THEN
- UPDATE SET dnotes = dnotes || ' notes added by merge1 ', dauthor = 'regress_rls_alice';
-
--- Should be OK since USING and WITH CHECK quals pass
-MERGE INTO document d
-USING (SELECT 1 as sdid) s
-ON did = s.sdid
-WHEN MATCHED THEN
- UPDATE SET dnotes = dnotes || ' notes added by merge2 ';
-
--- Even when dauthor is updated explicitly, but to the existing value
-MERGE INTO document d
-USING (SELECT 1 as sdid) s
-ON did = s.sdid
-WHEN MATCHED THEN
- UPDATE SET dnotes = dnotes || ' notes added by merge3 ', dauthor = 'regress_rls_bob';
-
--- There is a MATCH for did = 3, but UPDATE's USING qual does not allow
--- updating an item in category 'science fiction'
-MERGE INTO document d
-USING (SELECT 3 as sdid) s
-ON did = s.sdid
-WHEN MATCHED THEN
- UPDATE SET dnotes = dnotes || ' notes added by merge ';
-
--- The same thing with DELETE action, but fails again because no permissions
--- to delete items in 'science fiction' category that did 3 belongs to.
-MERGE INTO document d
-USING (SELECT 3 as sdid) s
-ON did = s.sdid
-WHEN MATCHED THEN
- DELETE;
-
--- Document with did 4 belongs to 'manga' category which is allowed for
--- deletion. But this fails because the UPDATE action is matched first and
--- UPDATE policy does not allow updation in the category.
-MERGE INTO document d
-USING (SELECT 4 as sdid) s
-ON did = s.sdid
-WHEN MATCHED AND dnotes = '' THEN
- UPDATE SET dnotes = dnotes || ' notes added by merge '
-WHEN MATCHED THEN
- DELETE;
-
--- UPDATE action is not matched this time because of the WHEN AND qual.
--- DELETE still fails because role regress_rls_bob does not have SELECT
--- privileges on 'manga' category row in the category table.
-MERGE INTO document d
-USING (SELECT 4 as sdid) s
-ON did = s.sdid
-WHEN MATCHED AND dnotes <> '' THEN
- UPDATE SET dnotes = dnotes || ' notes added by merge '
-WHEN MATCHED THEN
- DELETE;
-
-SELECT * FROM document WHERE did = 4;
-
--- Switch to regress_rls_carol role and try the DELETE again. It should succeed
--- this time
-RESET SESSION AUTHORIZATION;
-SET SESSION AUTHORIZATION regress_rls_carol;
-
-MERGE INTO document d
-USING (SELECT 4 as sdid) s
-ON did = s.sdid
-WHEN MATCHED AND dnotes <> '' THEN
- UPDATE SET dnotes = dnotes || ' notes added by merge '
-WHEN MATCHED THEN
- DELETE;
-
--- Switch back to regress_rls_bob role
-RESET SESSION AUTHORIZATION;
-SET SESSION AUTHORIZATION regress_rls_bob;
-
--- Try INSERT action. This fails because we are trying to insert
--- dauthor = regress_rls_dave and INSERT's WITH CHECK does not allow
--- that
-MERGE INTO document d
-USING (SELECT 12 as sdid) s
-ON did = s.sdid
-WHEN MATCHED THEN
- DELETE
-WHEN NOT MATCHED THEN
- INSERT VALUES (12, 11, 1, 'regress_rls_dave', 'another novel');
-
--- This should be fine
-MERGE INTO document d
-USING (SELECT 12 as sdid) s
-ON did = s.sdid
-WHEN MATCHED THEN
- DELETE
-WHEN NOT MATCHED THEN
- INSERT VALUES (12, 11, 1, 'regress_rls_bob', 'another novel');
-
--- ok
-MERGE INTO document d
-USING (SELECT 1 as sdid) s
-ON did = s.sdid
-WHEN MATCHED THEN
- UPDATE SET dnotes = dnotes || ' notes added by merge4 '
-WHEN NOT MATCHED THEN
- INSERT VALUES (12, 11, 1, 'regress_rls_bob', 'another novel');
-
--- drop and create a new SELECT policy which prevents us from reading
--- any document except with category 'magna'
-RESET SESSION AUTHORIZATION;
-DROP POLICY p1 ON document;
-CREATE POLICY p1 ON document FOR SELECT
- USING (cid = (SELECT cid from category WHERE cname = 'manga'));
-
-SET SESSION AUTHORIZATION regress_rls_bob;
-
--- MERGE can no longer see the matching row and hence attempts the
--- NOT MATCHED action, which results in unique key violation
-MERGE INTO document d
-USING (SELECT 1 as sdid) s
-ON did = s.sdid
-WHEN MATCHED THEN
- UPDATE SET dnotes = dnotes || ' notes added by merge5 '
-WHEN NOT MATCHED THEN
- INSERT VALUES (12, 11, 1, 'regress_rls_bob', 'another novel');
-
-RESET SESSION AUTHORIZATION;
--- drop the restrictive SELECT policy so that we can look at the
--- final state of the table
-DROP POLICY p1 ON document;
--- Just check everything went per plan
-SELECT * FROM document;
-
---
-- ROLE/GROUP
--
SET SESSION AUTHORIZATION regress_rls_alice;
diff --git a/src/test/regress/sql/rules.sql b/src/test/regress/sql/rules.sql
index b866268892f..a82f52d1548 100644
--- a/src/test/regress/sql/rules.sql
+++ b/src/test/regress/sql/rules.sql
@@ -1192,39 +1192,6 @@ ALTER RULE rules_parted_table_insert ON rules_parted_table RENAME TO rules_parte
DROP TABLE rules_parted_table;
--
--- test MERGE
---
-CREATE TABLE rule_merge1 (a int, b text);
-CREATE TABLE rule_merge2 (a int, b text);
-CREATE RULE rule1 AS ON INSERT TO rule_merge1
- DO INSTEAD INSERT INTO rule_merge2 VALUES (NEW.*);
-CREATE RULE rule2 AS ON UPDATE TO rule_merge1
- DO INSTEAD UPDATE rule_merge2 SET a = NEW.a, b = NEW.b
- WHERE a = OLD.a;
-CREATE RULE rule3 AS ON DELETE TO rule_merge1
- DO INSTEAD DELETE FROM rule_merge2 WHERE a = OLD.a;
-
--- MERGE not supported for table with rules
-MERGE INTO rule_merge1 t USING (SELECT 1 AS a) s
- ON t.a = s.a
- WHEN MATCHED AND t.a < 2 THEN
- UPDATE SET b = b || ' updated by merge'
- WHEN MATCHED AND t.a > 2 THEN
- DELETE
- WHEN NOT MATCHED THEN
- INSERT VALUES (s.a, '');
-
--- should be ok with the other table though
-MERGE INTO rule_merge2 t USING (SELECT 1 AS a) s
- ON t.a = s.a
- WHEN MATCHED AND t.a < 2 THEN
- UPDATE SET b = b || ' updated by merge'
- WHEN MATCHED AND t.a > 2 THEN
- DELETE
- WHEN NOT MATCHED THEN
- INSERT VALUES (s.a, '');
-
---
-- Test enabling/disabling
--
CREATE TABLE ruletest1 (a int);
diff --git a/src/test/regress/sql/triggers.sql b/src/test/regress/sql/triggers.sql
index b51c884eee4..c6f31dd8c8f 100644
--- a/src/test/regress/sql/triggers.sql
+++ b/src/test/regress/sql/triggers.sql
@@ -2110,53 +2110,6 @@ delete from self_ref where a = 1;
drop table self_ref;
---
--- test transition tables with MERGE
---
-create table merge_target_table (a int primary key, b text);
-create trigger merge_target_table_insert_trig
- after insert on merge_target_table referencing new table as new_table
- for each statement execute procedure dump_insert();
-create trigger merge_target_table_update_trig
- after update on merge_target_table referencing old table as old_table new table as new_table
- for each statement execute procedure dump_update();
-create trigger merge_target_table_delete_trig
- after delete on merge_target_table referencing old table as old_table
- for each statement execute procedure dump_delete();
-
-create table merge_source_table (a int, b text);
-insert into merge_source_table
- values (1, 'initial1'), (2, 'initial2'),
- (3, 'initial3'), (4, 'initial4');
-
-merge into merge_target_table t
-using merge_source_table s
-on t.a = s.a
-when not matched then
- insert values (a, b);
-
-merge into merge_target_table t
-using merge_source_table s
-on t.a = s.a
-when matched and s.a <= 2 then
- update set b = t.b || ' updated by merge'
-when matched and s.a > 2 then
- delete
-when not matched then
- insert values (a, b);
-
-merge into merge_target_table t
-using merge_source_table s
-on t.a = s.a
-when matched and s.a <= 2 then
- update set b = t.b || ' updated again by merge'
-when matched and s.a > 2 then
- delete
-when not matched then
- insert values (a, b);
-
-drop table merge_source_table, merge_target_table;
-
-- cleanup
drop function dump_insert();
drop function dump_update();
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 6d8a44cd9e8..abc10a8ffd4 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1228,8 +1228,6 @@ MemoryContextCallbackFunction
MemoryContextCounters
MemoryContextData
MemoryContextMethods
-MergeAction
-MergeActionState
MergeAppend
MergeAppendPath
MergeAppendState
@@ -1237,7 +1235,6 @@ MergeJoin
MergeJoinClause
MergeJoinState
MergePath
-MergeStmt
MergeScanSelCache
MetaCommand
MinMaxAggInfo