v27-0005-Fixes-per-review-comments.patch
application/octet-stream
Filename: v27-0005-Fixes-per-review-comments.patch
Type: application/octet-stream
Part: 0
Patch
Format: format-patch
Series: patch v27-0005
Subject: Fixes per review comments
| File | + | − |
|---|---|---|
| src/backend/access/heap/heapam.c | 13 | 12 |
| src/backend/executor/nodeMerge.c | 30 | 21 |
| src/backend/executor/nodeModifyTable.c | 6 | 7 |
| src/backend/parser/parse_merge.c | 4 | 5 |
| src/include/access/heapam.h | 11 | 1 |
| src/test/regress/expected/merge.out | 62 | 2 |
| src/test/regress/sql/merge.sql | 58 | 0 |
From 28c6a5aa7a3f1018738581489f7b03517850bf89 Mon Sep 17 00:00:00 2001
From: Pavan Deolasee <pavan.deolasee@gmail.com>
Date: Wed, 28 Mar 2018 11:44:48 +0530
Subject: [PATCH v27 5/5] Fixes per review comments
Use the correct lockmode while running EvalPlanQual
Fix some typos
matviews can't use MERGE, just like they can't use UPDATE/INSERTs
Fix a compiler warning
buffer can be released without checking BufferIsValid()
Couple of new test cases to check ERROR conditions and some minor cleanup
Call ResetExprContext() once per tuple processed
---
src/backend/access/heap/heapam.c | 25 ++++++-------
src/backend/executor/nodeMerge.c | 51 ++++++++++++++++-----------
src/backend/executor/nodeModifyTable.c | 13 ++++---
src/backend/parser/parse_merge.c | 9 +++--
src/include/access/heapam.h | 12 ++++++-
src/test/regress/expected/merge.out | 64 ++++++++++++++++++++++++++++++++--
src/test/regress/sql/merge.sql | 58 ++++++++++++++++++++++++++++++
7 files changed, 184 insertions(+), 48 deletions(-)
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index decc3d37c3..f96567f5d5 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -3508,7 +3508,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, LockTupleMode *lockmode)
+ HeapUpdateFailureData *hufd)
{
HTSU_Result result;
TransactionId xid = GetCurrentTransactionId();
@@ -3548,8 +3548,10 @@ 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.
@@ -3665,7 +3667,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
*/
if (!bms_overlap(modified_attrs, key_attrs))
{
- *lockmode = LockTupleNoKeyExclusive;
+ lockmode = hufd->lockmode = LockTupleNoKeyExclusive;
mxact_status = MultiXactStatusNoKeyUpdate;
key_intact = true;
@@ -3682,7 +3684,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
}
else
{
- *lockmode = LockTupleExclusive;
+ lockmode = hufd->lockmode = LockTupleExclusive;
mxact_status = MultiXactStatusUpdate;
key_intact = false;
}
@@ -3760,12 +3762,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 */
@@ -3849,7 +3851,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);
@@ -3897,7 +3899,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);
@@ -3935,7 +3937,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);
@@ -4052,7 +4054,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);
@@ -4364,7 +4366,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);
@@ -4588,12 +4590,11 @@ 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, &lockmode);
+ &hufd);
switch (result)
{
case HeapTupleSelfUpdated:
diff --git a/src/backend/executor/nodeMerge.c b/src/backend/executor/nodeMerge.c
index ee41ed2eb2..67f1bfbb16 100644
--- a/src/backend/executor/nodeMerge.c
+++ b/src/backend/executor/nodeMerge.c
@@ -139,7 +139,6 @@ ExecMergeMatched(ModifyTableState *mtstate, EState *estate,
lmerge_matched:;
slot = saved_slot;
- buffer = InvalidBuffer;
/*
* UPDATE/DELETE is only invoked for matched rows. And we must have found
@@ -263,7 +262,7 @@ lmerge_matched:;
ereport(ERROR,
(errcode(ERRCODE_CARDINALITY_VIOLATION),
errmsg("MERGE command cannot affect row a second time"),
- errhint("Ensure that not more than one source rows match any one target row")));
+ errhint("Ensure that not more than one source row match any one target row")));
/* This shouldn't happen */
elog(ERROR, "attempted to update or delete invisible tuple");
break;
@@ -284,7 +283,7 @@ lmerge_matched:;
* tuple. If EvalPlanQual() does not return a tuple (can
* that happen?), then again we switch to NOT MATCHED
* action. If it does return a tuple and the join qual is
- * still satified, then we just need to recheck the
+ * still satisfied, then we just need to recheck the
* MATCHED actions, starting from the top, and execute the
* first qualifying action.
*/
@@ -335,8 +334,7 @@ lmerge_matched:;
* newer tuple found in the update chain.
*/
*tupleid = hufd.ctid;
- if (BufferIsValid(buffer))
- ReleaseBuffer(buffer);
+ ReleaseBuffer(buffer);
goto lmerge_matched;
}
}
@@ -348,8 +346,7 @@ lmerge_matched:;
*/
*tupleid = hufd.ctid;
estate->es_result_relation_info = saved_resultRelInfo;
- if (BufferIsValid(buffer))
- ReleaseBuffer(buffer);
+ ReleaseBuffer(buffer);
return false;
default:
@@ -365,14 +362,13 @@ lmerge_matched:;
/*
* We've activated one of the WHEN clauses, so we don't search
- * further. This is required behaviour, not an optimisation.
+ * further. This is required behaviour, not an optimization.
*/
estate->es_result_relation_info = saved_resultRelInfo;
break;
}
- if (BufferIsValid(buffer))
- ReleaseBuffer(buffer);
+ ReleaseBuffer(buffer);
/*
* Successfully executed an action or no qualifying action was found.
@@ -403,7 +399,7 @@ ExecMergeNotMatched(ModifyTableState *mtstate, EState *estate,
resultRelInfo = mtstate->resultRelInfo;
/*
- * For INSERT actions, root relation's merge action is OK since the the
+ * For INSERT actions, root relation's merge action is OK since the
* INSERT's targetlist and the WHEN conditions can only refer to the
* source relation and hence it does not matter which result relation we
* work with.
@@ -484,6 +480,7 @@ void
ExecMerge(ModifyTableState *mtstate, EState *estate, TupleTableSlot *slot,
JunkFilter *junkfilter, ResultRelInfo *resultRelInfo)
{
+ ExprContext *econtext = mtstate->ps.ps_ExprContext;
ItemPointer tupleid;
ItemPointerData tuple_ctid;
bool matched = false;
@@ -493,9 +490,14 @@ ExecMerge(ModifyTableState *mtstate, EState *estate, TupleTableSlot *slot,
relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
Assert(relkind == RELKIND_RELATION ||
- relkind == RELKIND_MATVIEW ||
relkind == RELKIND_PARTITIONED_TABLE);
+ /*
+ * Reset per-tuple memory context to free any expression evaluation
+ * storage allocated in the previous cycle.
+ */
+ ResetExprContext(econtext);
+
/*
* We run a JOIN between the target relation and the source relation to
* find a set of candidate source rows that has matching row in the target
@@ -523,12 +525,12 @@ ExecMerge(ModifyTableState *mtstate, EState *estate, TupleTableSlot *slot,
/*
* If we are dealing with a WHEN MATCHED case, we look at the given WHEN
- * MATCHED actions in an order and execute the first action which also
- * satisfies the additional WHEN MATCHED AND quals. If an action without
- * any additional quals is found, that action is executed.
+ * MATCHED actions in an order and execute the first action for which
+ * the additional WHEN MATCHED AND quals pass. If an action without
+ * quals is found, that action is executed.
*
* Similarly, if we are dealing with WHEN NOT MATCHED case, we look at the
- * given WHEN NOT MATCHED actions in an ordr and execute the first
+ * given WHEN NOT MATCHED actions in an order and execute the first
* qualifying action.
*
* Things get interesting in case of concurrent update/delete of the
@@ -538,9 +540,9 @@ ExecMerge(ModifyTableState *mtstate, EState *estate, TupleTableSlot *slot,
* A concurrent update for example can:
*
* 1. modify the target tuple so that it no longer satisfies the
- * additional quals attached to the current WHEN MATCHED action OR 2.
- * modify the target tuple so that the join quals no longer pass and hence
- * the source tuple no longer has a match.
+ * additional quals attached to the current WHEN MATCHED action OR
+ * 2. modify the target tuple so that the join quals no longer pass and
+ * hence the source tuple no longer has a match.
*
* In the first case, we are still dealing with a WHEN MATCHED case, but
* we should recheck the list of WHEN MATCHED actions and choose the first
@@ -559,7 +561,14 @@ ExecMerge(ModifyTableState *mtstate, EState *estate, TupleTableSlot *slot,
* from ExecMergeNotMatched to ExecMergeMatched, there is no risk of a
* livelock.
*/
- if (!matched ||
- !ExecMergeMatched(mtstate, estate, slot, junkfilter, tupleid))
+ if (matched)
+ matched = ExecMergeMatched(mtstate, estate, slot, junkfilter, tupleid);
+
+ /*
+ * Either we were dealing with a NOT MATCHED tuple or ExecMergeNotMatched()
+ * returned "false", indicating the previously MATCHED tuple is no longer a
+ * matching tuple.
+ */
+ if (!matched)
ExecMergeNotMatched(mtstate, estate, slot);
}
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 08f812fc6d..5798eb12a5 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -398,11 +398,11 @@ ExecInsert(ModifyTableState *mtstate,
*/
if (mtstate->operation == CMD_UPDATE)
wco_kind = WCO_RLS_UPDATE_CHECK;
- else if (mtstate->operation == CMD_INSERT)
- wco_kind = WCO_RLS_INSERT_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;
/*
* ExecWithCheckOptions() will skip any WCOs which are not of the kind
@@ -1083,7 +1083,6 @@ ExecUpdate(ModifyTableState *mtstate,
}
else
{
- LockTupleMode lockmode;
bool partition_constraint_failed;
/*
@@ -1204,7 +1203,7 @@ lreplace:;
* retrieve the tuple conversion map for this resultRelInfo.
*
* If we're running MERGE then resultRelInfo is per-partition
- * resultRelInfo as initialised in ExecInitPartitionInfo(). Note
+ * 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
@@ -1285,7 +1284,7 @@ lreplace:;
estate->es_output_cid,
estate->es_crosscheck_snapshot,
true /* wait for commit */ ,
- &hufd, &lockmode);
+ &hufd);
/*
* Copy the necessary information, if the caller has asked for it. We
@@ -1357,7 +1356,7 @@ lreplace:;
epqstate,
resultRelationDesc,
GetEPQRangeTableIndex(resultRelInfo),
- lockmode,
+ hufd.lockmode,
&hufd.ctid,
hufd.xmax);
if (!TupIsNull(epqslot))
@@ -2684,7 +2683,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
mtstate->mt_partition_tuple_routing ?
NULL : relationDesc);
- /* initialise slot for merge actions */
+ /* initialize slot for merge actions */
Assert(mtstate->mt_mergeproj == NULL);
mtstate->mt_mergeproj =
ExecInitExtraTupleSlot(mtstate->ps.state,
diff --git a/src/backend/parser/parse_merge.c b/src/backend/parser/parse_merge.c
index 6df63439bb..25416a321c 100644
--- a/src/backend/parser/parse_merge.c
+++ b/src/backend/parser/parse_merge.c
@@ -83,7 +83,7 @@ transformMergeJoinClause(ParseState *pstate, Node *merge,
* We created an internal join between the target and the source relation
* to carry out the MERGE actions. Normally such an unaliased join hides
* the joining relations, unless the column references are qualified.
- * Also, any unqualified column refernces are resolved to the Join RTE, if
+ * Also, any unqualified column references are resolved to the Join RTE, if
* there is a matching entry in the targetlist. But the way MERGE
* execution is later setup, we expect all column references to resolve to
* either the source or the target relation. Hence we must not add the
@@ -351,7 +351,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt)
* Simplify the MERGE query as much as possible
*
* These seem like things that could go into Optimizer, but they are
- * semantic simplications rather than optimizations, per se.
+ * semantic simplifications rather than optimizations, per se.
*
* If there are no INSERT actions we won't be using the non-matching
* candidate rows for anything, so no need for an outer join. We do still
@@ -426,7 +426,6 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt)
* XXX MERGE is unsupported in various cases
*/
if (!(pstate->p_target_relation->rd_rel->relkind == RELKIND_RELATION ||
- pstate->p_target_relation->rd_rel->relkind == RELKIND_MATVIEW ||
pstate->p_target_relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -463,7 +462,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt)
/*
* Set namespace for the specific action. This must be done before
- * analysing the WHEN quals and the action targetlisst.
+ * analyzing the WHEN quals and the action targetlisst.
*/
setNamespaceForMergeAction(pstate, action);
@@ -471,7 +470,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt)
* Transform the when condition.
*
* Note that these quals are NOT added to the join quals; instead they
- * are evaluated sepaartely during execution to decide which of the
+ * are evaluated separately during execution to decide which of the
* WHEN MATCHED or WHEN NOT MATCHED actions to execute.
*/
action->qual = transformWhereClause(pstate, action->condition,
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 100174138d..608f50b061 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -53,17 +53,26 @@ 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
{
@@ -71,6 +80,7 @@ typedef struct HeapUpdateFailureData
ItemPointerData ctid;
TransactionId xmax;
CommandId cmax;
+ LockTupleMode lockmode;
} HeapUpdateFailureData;
@@ -163,7 +173,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, LockTupleMode *lockmode);
+ HeapUpdateFailureData *hufd);
extern HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple,
CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
bool follow_update,
diff --git a/src/test/regress/expected/merge.out b/src/test/regress/expected/merge.out
index 90f3177743..7546ed84f1 100644
--- a/src/test/regress/expected/merge.out
+++ b/src/test/regress/expected/merge.out
@@ -84,6 +84,24 @@ WHEN NOT MATCHED THEN
ERROR: syntax error at or near "INTO"
LINE 5: INSERT INTO target DEFAULT VALUES
^
+-- Multiple VALUES clause
+MERGE INTO target t
+USING source AS s
+ON t.tid = s.sid
+WHEN NOT MATCHED THEN
+ INSERT VALUES (1,1), (2,2);
+ERROR: Multiple VALUES clauses not allowed in MERGE INSERT statement
+;
+-- SELECT query for INSERT
+MERGE INTO target t
+USING source AS s
+ON t.tid = s.sid
+WHEN NOT MATCHED THEN
+ INSERT SELECT (1, 1);
+ERROR: syntax error at or near "SELECT"
+LINE 5: INSERT SELECT (1, 1);
+ ^
+;
-- NOT MATCHED/UPDATE
MERGE INTO target t
USING source AS s
@@ -104,6 +122,48 @@ WHEN MATCHED THEN
ERROR: syntax error at or near "target"
LINE 5: UPDATE target SET balance = 0
^
+-- unsupported relation types
+-- view
+CREATE VIEW tv AS SELECT * FROM target;
+MERGE INTO tv t
+USING source s
+ON t.tid = s.sid
+WHEN NOT MATCHED THEN
+ INSERT DEFAULT VALUES;
+ERROR: MERGE is not supported for this relation type
+DROP VIEW tv;
+-- materialized view
+CREATE MATERIALIZED VIEW mv AS SELECT * FROM target;
+MERGE INTO mv t
+USING source s
+ON t.tid = s.sid
+WHEN NOT MATCHED THEN
+ INSERT DEFAULT VALUES;
+ERROR: MERGE is not supported for this relation type
+DROP MATERIALIZED VIEW mv;
+-- inherited table
+CREATE TABLE inhp (tid int, balance int);
+CREATE TABLE child1() INHERITS (inhp);
+CREATE TABLE child2() INHERITS (child1);
+MERGE INTO inhp t
+USING source s
+ON t.tid = s.sid
+WHEN NOT MATCHED THEN
+ INSERT DEFAULT VALUES;
+ERROR: MERGE is not supported for relations with inheritance
+MERGE INTO child1 t
+USING source s
+ON t.tid = s.sid
+WHEN NOT MATCHED THEN
+ INSERT DEFAULT VALUES;
+ERROR: MERGE is not supported for relations with inheritance
+-- this should be ok
+MERGE INTO child2 t
+USING source s
+ON t.tid = s.sid
+WHEN NOT MATCHED THEN
+ INSERT DEFAULT VALUES;
+DROP TABLE inhp, child1, child2;
-- permissions
MERGE INTO target
USING source2
@@ -382,7 +442,7 @@ WHEN MATCHED THEN
UPDATE SET balance = 0
;
ERROR: MERGE command cannot affect row a second time
-HINT: Ensure that not more than one source rows match any one target row
+HINT: Ensure that not more than one source row match any one target row
ROLLBACK;
BEGIN;
MERGE INTO target t
@@ -392,7 +452,7 @@ WHEN MATCHED THEN
DELETE
;
ERROR: MERGE command cannot affect row a second time
-HINT: Ensure that not more than one source rows match any one target row
+HINT: Ensure that not more than one source row match any one target row
ROLLBACK;
-- correct source data
DELETE FROM source WHERE sid = 2;
diff --git a/src/test/regress/sql/merge.sql b/src/test/regress/sql/merge.sql
index cd6144bb5f..db2f0acb86 100644
--- a/src/test/regress/sql/merge.sql
+++ b/src/test/regress/sql/merge.sql
@@ -63,6 +63,20 @@ ON t.tid = s.sid
WHEN NOT MATCHED THEN
INSERT INTO target DEFAULT VALUES
;
+-- Multiple VALUES clause
+MERGE INTO target t
+USING source AS s
+ON t.tid = s.sid
+WHEN NOT MATCHED THEN
+ INSERT VALUES (1,1), (2,2);
+;
+-- SELECT query for INSERT
+MERGE INTO target t
+USING source AS s
+ON t.tid = s.sid
+WHEN NOT MATCHED THEN
+ INSERT SELECT (1, 1);
+;
-- NOT MATCHED/UPDATE
MERGE INTO target t
USING source AS s
@@ -78,6 +92,50 @@ WHEN MATCHED THEN
UPDATE target SET balance = 0
;
+-- unsupported relation types
+-- view
+CREATE VIEW tv AS SELECT * FROM target;
+MERGE INTO tv t
+USING source s
+ON t.tid = s.sid
+WHEN NOT MATCHED THEN
+ INSERT DEFAULT VALUES;
+DROP VIEW tv;
+
+-- materialized view
+CREATE MATERIALIZED VIEW mv AS SELECT * FROM target;
+MERGE INTO mv t
+USING source s
+ON t.tid = s.sid
+WHEN NOT MATCHED THEN
+ INSERT DEFAULT VALUES;
+DROP MATERIALIZED VIEW mv;
+
+-- inherited table
+CREATE TABLE inhp (tid int, balance int);
+CREATE TABLE child1() INHERITS (inhp);
+CREATE TABLE child2() INHERITS (child1);
+
+MERGE INTO inhp t
+USING source s
+ON t.tid = s.sid
+WHEN NOT MATCHED THEN
+ INSERT DEFAULT VALUES;
+
+MERGE INTO child1 t
+USING source s
+ON t.tid = s.sid
+WHEN NOT MATCHED THEN
+ INSERT DEFAULT VALUES;
+
+-- this should be ok
+MERGE INTO child2 t
+USING source s
+ON t.tid = s.sid
+WHEN NOT MATCHED THEN
+ INSERT DEFAULT VALUES;
+DROP TABLE inhp, child1, child2;
+
-- permissions
MERGE INTO target
--
2.14.3 (Apple Git-98)