|
24 | 24 | *values plus row-locating info for UPDATE and MERGE cases, or just the
|
25 | 25 | *row-locating info for DELETE cases.
|
26 | 26 | *
|
| 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 |
| 33 | + *automatically updatable views. |
| 34 | + * |
27 | 35 | *MERGE runs a join between the source relation and the target table.
|
28 | 36 | *If any WHEN NOT MATCHED [BY TARGET] clauses are present, then the join
|
29 | 37 | *is an outer join that might output tuples without a matching target
|
@@ -1398,18 +1406,18 @@ ExecDeleteEpilogue(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
|
1398 | 1406 | *DELETE is like UPDATE, except that we delete the tuple and no
|
1399 | 1407 | *index modifications are needed.
|
1400 | 1408 | *
|
1401 |
| - *When deleting from a table, tupleid identifies the tuple to |
1402 |
| - *delete andoldtuple is NULL. When deletingfrom a view, |
1403 |
| - *oldtuple is passed to theINSTEAD OFtriggers and identifies |
1404 |
| - *what to delete, andtupleid is invalid. When deleting from a |
1405 |
| - *foreign table, tupleid isinvalid; the FDW has to figure out |
1406 |
| - *which row to delete using data from the planSlot. oldtuple is |
1407 |
| - *passed toforeign tabletriggers; it is NULL when the foreign |
1408 |
| - *table has no relevant triggers. We use tupleDeleted to indicate |
1409 |
| - *whether the tuple is actually deleted, callers can use it to |
1410 |
| - *decide whether to continue the operation. When this DELETE is a |
1411 |
| - *part of an UPDATE of partition-key, then the slot returned by |
1412 |
| - *EvalPlanQual() is passed back using output parameterepqreturnslot. |
| 1409 | + *When deleting from a table, tupleid identifies the tuple to delete and |
| 1410 | + *oldtuple is NULL. When deletingthrough a view INSTEAD OF trigger, |
| 1411 | + *oldtuple is passed to the triggers and identifies what to delete, and |
| 1412 | + *tupleid is invalid. When deleting from a foreign table, tupleid is |
| 1413 | + *invalid; the FDW has to figure out which row to delete using data from |
| 1414 | + *the planSlot. oldtuple is passed to foreign table triggers; it is |
| 1415 | + *NULL when theforeign tablehas no relevant triggers. We use |
| 1416 | + *tupleDeleted to indicate whether the tuple is actually deleted, |
| 1417 | + *callers can use it to decide whether to continue the operation. When |
| 1418 | + *this DELETE is a part of an UPDATE of partition-key, then the slot |
| 1419 | + *returned by EvalPlanQual() is passed back using output parameter |
| 1420 | + *epqreturnslot. |
1413 | 1421 | *
|
1414 | 1422 | *Returns RETURNING result if any, otherwise NULL.
|
1415 | 1423 | * ----------------------------------------------------------------
|
@@ -2238,21 +2246,22 @@ ExecCrossPartitionUpdateForeignKey(ModifyTableContext *context,
|
2238 | 2246 | *is, we don't want to get stuck in an infinite loop
|
2239 | 2247 | *which corrupts your database..
|
2240 | 2248 | *
|
2241 |
| - *When updating a table, tupleid identifies the tuple to |
2242 |
| - *update and oldtuple is NULL. When updating a view, oldtuple |
2243 |
| - *is passed to the INSTEAD OF triggers and identifies what to |
2244 |
| - *update, and tupleid is invalid. When updating a foreign table, |
2245 |
| - *tupleid is invalid; the FDW has to figure out which row to |
2246 |
| - *update using data from the planSlot. oldtuple is passed to |
2247 |
| - *foreign table triggers; it is NULL when the foreign table has |
2248 |
| - *no relevant triggers. |
| 2249 | + *When updating a table, tupleid identifies the tuple to update and |
| 2250 | + *oldtuple is NULL. When updating through a view INSTEAD OF trigger, |
| 2251 | + *oldtuple is passed to the triggers and identifies what to update, and |
| 2252 | + *tupleid is invalid. When updating a foreign table, tupleid is |
| 2253 | + *invalid; the FDW has to figure out which row to update using data from |
| 2254 | + *the planSlot. oldtuple is passed to foreign table triggers; it is |
| 2255 | + *NULL when the foreign table has no relevant triggers. |
2249 | 2256 | *
|
2250 | 2257 | *slot contains the new tuple value to be stored.
|
2251 | 2258 | *planSlot is the output of the ModifyTable's subplan; we use it
|
2252 | 2259 | *to access values from other input tables (for RETURNING),
|
2253 | 2260 | *row-ID junk columns, etc.
|
2254 | 2261 | *
|
2255 |
| - *Returns RETURNING result if any, otherwise NULL. |
| 2262 | + *Returns RETURNING result if any, otherwise NULL. On exit, if tupleid |
| 2263 | + *had identified the tuple to update, it will identify the tuple |
| 2264 | + *actually updated after EvalPlanQual. |
2256 | 2265 | * ----------------------------------------------------------------
|
2257 | 2266 | */
|
2258 | 2267 | staticTupleTableSlot*
|
@@ -2717,10 +2726,10 @@ ExecMerge(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
|
2717 | 2726 |
|
2718 | 2727 | /*-----
|
2719 | 2728 | * If we are dealing with a WHEN MATCHED case, tupleid or oldtuple is
|
2720 |
| - * valid, depending on whether the result relation is a table or a view. |
2721 |
| - * We execute the first action for which the additional WHEN MATCHED AND |
2722 |
| - * quals pass. If an action without quals is found, that action is |
2723 |
| - * executed. |
| 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 ANDquals pass. If an action without quals |
| 2732 | + *is found, that action isexecuted. |
2724 | 2733 | *
|
2725 | 2734 | * Similarly, in the WHEN NOT MATCHED BY SOURCE case, tupleid or oldtuple
|
2726 | 2735 | * is valid, and we look at the given WHEN NOT MATCHED BY SOURCE actions
|
@@ -2811,8 +2820,8 @@ ExecMerge(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
|
2811 | 2820 | * Check and execute the first qualifying MATCHED or NOT MATCHED BY SOURCE
|
2812 | 2821 | * action, depending on whether the join quals are satisfied. If the target
|
2813 | 2822 | * relation is a table, the current target tuple is identified by tupleid.
|
2814 |
| - * Otherwise, if the target relation is a view, oldtuple is the current target |
2815 |
| - * tuple from the view. |
| 2823 | + * Otherwise, if the target relation is a view having an INSTEAD OF trigger, |
| 2824 | + *oldtuple is the current targettuple from the view. |
2816 | 2825 | *
|
2817 | 2826 | * We start from the first WHEN MATCHED or WHEN NOT MATCHED BY SOURCE action
|
2818 | 2827 | * and check if the WHEN quals pass, if any. If the WHEN quals for the first
|
@@ -2878,8 +2887,11 @@ ExecMergeMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
|
2878 | 2887 | */
|
2879 | 2888 | Assert(tupleid!=NULL||oldtuple!=NULL);
|
2880 | 2889 | if (oldtuple!=NULL)
|
| 2890 | +{ |
| 2891 | +Assert(resultRelInfo->ri_TrigDesc); |
2881 | 2892 | ExecForceStoreHeapTuple(oldtuple,resultRelInfo->ri_oldTupleSlot,
|
2882 | 2893 | false);
|
| 2894 | +} |
2883 | 2895 | elseif (!table_tuple_fetch_row_version(resultRelInfo->ri_RelationDesc,
|
2884 | 2896 | tupleid,
|
2885 | 2897 | SnapshotAny,
|
@@ -3992,8 +4004,8 @@ ExecModifyTable(PlanState *pstate)
|
3992 | 4004 | * know enough here to set t_tableOid. Quite separately from
|
3993 | 4005 | * this, the FDW may fetch its own junk attrs to identify the row.
|
3994 | 4006 | *
|
3995 |
| - * Other relevant relkinds, currently limited to views, always |
3996 |
| - * have a wholerow attribute. |
| 4007 | + * Other relevant relkinds, currently limited to views having |
| 4008 | + *INSTEAD OF triggers, alwayshave a wholerow attribute. |
3997 | 4009 | */
|
3998 | 4010 | elseif (AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
|
3999 | 4011 | {
|
|