Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit d5e6891

Browse filesBrowse 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
Copy full SHA for d5e6891

File tree

3 files changed

+33
-17
lines changed
Filter options

3 files changed

+33
-17
lines changed

‎src/backend/executor/nodeModifyTable.c

Copy file name to clipboardExpand all lines: src/backend/executor/nodeModifyTable.c
+23-17Lines changed: 23 additions & 17 deletions
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

Copy file name to clipboardExpand all lines: src/test/regress/expected/updatable_views.out
+5Lines changed: 5 additions & 0 deletions
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

Copy file name to clipboardExpand all lines: src/test/regress/sql/updatable_views.sql
+5Lines changed: 5 additions & 0 deletions
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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.