Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit d5e6891

Browse files
committed
Fix new assertion for MERGE view_name ... DO NOTHING.
Such queries don't expand automatically updatable views, and ModifyTable uses the wholerow attribute unconditionally. The user-visible behavior is fine, so change to more-specific assertions. Commit d5f788b added the wrong assertion. Back-patch to v17, where commit 5f2e179 introduced MERGE view_name. Reported by Alexander Lakhin. Discussion: https://postgr.es/m/e4b40a88-c134-6926-3196-bc4501cb87a2@gmail.com
1 parent 7102070 commit d5e6891

File tree

3 files changed

+33
-17
lines changed

3 files changed

+33
-17
lines changed

src/backend/executor/nodeModifyTable.c

+23-17
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,13 @@
2424
* values plus row-locating info for UPDATE and MERGE cases, or just the
2525
* row-locating info for DELETE cases.
2626
*
27-
* The relation to modify can be an ordinary table, a view having an
28-
* INSTEAD OF trigger, or a foreign table. Earlier processing already
29-
* pointed ModifyTable to the underlying relations of any automatically
30-
* updatable view not using an INSTEAD OF trigger, so code here can
31-
* assume it won't have one as a modification target. This node does
32-
* process ri_WithCheckOptions, which may have expressions from those
27+
* The relation to modify can be an ordinary table, a foreign table, or a
28+
* view. If it's a view, either it has sufficient INSTEAD OF triggers or
29+
* this node executes only MERGE ... DO NOTHING. If the original MERGE
30+
* targeted a view not in one of those two categories, earlier processing
31+
* already pointed the ModifyTable result relation to an underlying
32+
* relation of that other view. This node does process
33+
* ri_WithCheckOptions, which may have expressions from those other,
3334
* automatically updatable views.
3435
*
3536
* MERGE runs a join between the source relation and the target table.
@@ -2726,10 +2727,10 @@ ExecMerge(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
27262727

27272728
/*-----
27282729
* If we are dealing with a WHEN MATCHED case, tupleid or oldtuple is
2729-
* valid, depending on whether the result relation is a table or a view
2730-
* having an INSTEAD OF trigger. We execute the first action for which
2731-
* the additional WHEN MATCHED AND quals pass. If an action without quals
2732-
* is found, that action is executed.
2730+
* valid, depending on whether the result relation is a table or a view.
2731+
* We execute the first action for which the additional WHEN MATCHED AND
2732+
* quals pass. If an action without quals is found, that action is
2733+
* executed.
27332734
*
27342735
* Similarly, in the WHEN NOT MATCHED BY SOURCE case, tupleid or oldtuple
27352736
* is valid, and we look at the given WHEN NOT MATCHED BY SOURCE actions
@@ -2820,8 +2821,8 @@ ExecMerge(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
28202821
* Check and execute the first qualifying MATCHED or NOT MATCHED BY SOURCE
28212822
* action, depending on whether the join quals are satisfied. If the target
28222823
* relation is a table, the current target tuple is identified by tupleid.
2823-
* Otherwise, if the target relation is a view having an INSTEAD OF trigger,
2824-
* oldtuple is the current target tuple from the view.
2824+
* Otherwise, if the target relation is a view, oldtuple is the current target
2825+
* tuple from the view.
28252826
*
28262827
* We start from the first WHEN MATCHED or WHEN NOT MATCHED BY SOURCE action
28272828
* and check if the WHEN quals pass, if any. If the WHEN quals for the first
@@ -2887,11 +2888,8 @@ ExecMergeMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
28872888
*/
28882889
Assert(tupleid != NULL || oldtuple != NULL);
28892890
if (oldtuple != NULL)
2890-
{
2891-
Assert(resultRelInfo->ri_TrigDesc);
28922891
ExecForceStoreHeapTuple(oldtuple, resultRelInfo->ri_oldTupleSlot,
28932892
false);
2894-
}
28952893
else if (!table_tuple_fetch_row_version(resultRelInfo->ri_RelationDesc,
28962894
tupleid,
28972895
SnapshotAny,
@@ -2983,6 +2981,9 @@ ExecMergeMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
29832981
}
29842982
else
29852983
{
2984+
/* called table_tuple_fetch_row_version() above */
2985+
Assert(oldtuple == NULL);
2986+
29862987
result = ExecUpdateAct(context, resultRelInfo, tupleid,
29872988
NULL, newslot, canSetTag,
29882989
&updateCxt);
@@ -3031,8 +3032,13 @@ ExecMergeMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
30313032
return NULL; /* "do nothing" */
30323033
}
30333034
else
3035+
{
3036+
/* called table_tuple_fetch_row_version() above */
3037+
Assert(oldtuple == NULL);
3038+
30343039
result = ExecDeleteAct(context, resultRelInfo, tupleid,
30353040
false);
3041+
}
30363042

30373043
if (result == TM_Ok)
30383044
{
@@ -4004,8 +4010,8 @@ ExecModifyTable(PlanState *pstate)
40044010
* know enough here to set t_tableOid. Quite separately from
40054011
* this, the FDW may fetch its own junk attrs to identify the row.
40064012
*
4007-
* Other relevant relkinds, currently limited to views having
4008-
* INSTEAD OF triggers, always have a wholerow attribute.
4013+
* Other relevant relkinds, currently limited to views, always
4014+
* have a wholerow attribute.
40094015
*/
40104016
else if (AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
40114017
{

src/test/regress/expected/updatable_views.out

+5
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,9 @@ MERGE INTO ro_view13 AS t USING (VALUES (3, 'Row 3')) AS v(a,b) ON t.a = v.a
199199
ERROR: cannot insert into view "ro_view13"
200200
DETAIL: Views that do not select from a single table or view are not automatically updatable.
201201
HINT: To enable inserting into the view using MERGE, provide an INSTEAD OF INSERT trigger.
202+
MERGE INTO ro_view13 AS t USING (VALUES (2, 'Row 2')) AS v(a,b) ON t.a = v.a
203+
WHEN MATCHED THEN DO NOTHING
204+
WHEN NOT MATCHED THEN DO NOTHING; -- should be OK to do nothing
202205
MERGE INTO ro_view13 AS t USING (VALUES (3, 'Row 3')) AS v(a,b) ON t.a = v.a
203206
WHEN MATCHED THEN DO NOTHING
204207
WHEN NOT MATCHED THEN DO NOTHING; -- should be OK to do nothing
@@ -375,6 +378,8 @@ DELETE FROM ro_view18;
375378
ERROR: cannot delete from view "ro_view18"
376379
DETAIL: Views that do not select from a single table or view are not automatically updatable.
377380
HINT: To enable deleting from the view, provide an INSTEAD OF DELETE trigger or an unconditional ON DELETE DO INSTEAD rule.
381+
MERGE INTO ro_view18 AS t USING (VALUES (1, 'Row 1')) AS v(a,b) ON t.a = v.a
382+
WHEN MATCHED THEN DO NOTHING; -- should be OK to do nothing
378383
UPDATE ro_view19 SET last_value=1000;
379384
ERROR: cannot update view "ro_view19"
380385
DETAIL: Views that do not select from a single table or view are not automatically updatable.

src/test/regress/sql/updatable_views.sql

+5
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ MERGE INTO ro_view13 AS t USING (VALUES (2, 'Row 2')) AS v(a,b) ON t.a = v.a
6868
WHEN MATCHED THEN UPDATE SET b = v.b;
6969
MERGE INTO ro_view13 AS t USING (VALUES (3, 'Row 3')) AS v(a,b) ON t.a = v.a
7070
WHEN NOT MATCHED THEN INSERT VALUES (v.a, v.b);
71+
MERGE INTO ro_view13 AS t USING (VALUES (2, 'Row 2')) AS v(a,b) ON t.a = v.a
72+
WHEN MATCHED THEN DO NOTHING
73+
WHEN NOT MATCHED THEN DO NOTHING; -- should be OK to do nothing
7174
MERGE INTO ro_view13 AS t USING (VALUES (3, 'Row 3')) AS v(a,b) ON t.a = v.a
7275
WHEN MATCHED THEN DO NOTHING
7376
WHEN NOT MATCHED THEN DO NOTHING; -- should be OK to do nothing
@@ -121,6 +124,8 @@ DELETE FROM rw_view16 WHERE a=-3; -- should be OK
121124
-- Read-only views
122125
INSERT INTO ro_view17 VALUES (3, 'ROW 3');
123126
DELETE FROM ro_view18;
127+
MERGE INTO ro_view18 AS t USING (VALUES (1, 'Row 1')) AS v(a,b) ON t.a = v.a
128+
WHEN MATCHED THEN DO NOTHING; -- should be OK to do nothing
124129
UPDATE ro_view19 SET last_value=1000;
125130
UPDATE ro_view20 SET b=upper(b);
126131

0 commit comments

Comments
 (0)