aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor')
-rw-r--r--src/backend/executor/nodeModifyTable.c70
1 files changed, 41 insertions, 29 deletions
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index cee60d3659b..a2442b7b0de 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -24,6 +24,14 @@
* values plus row-locating info for UPDATE and MERGE cases, or just the
* row-locating info for DELETE cases.
*
+ * The relation to modify can be an ordinary table, a view having an
+ * INSTEAD OF trigger, or a foreign table. Earlier processing already
+ * pointed ModifyTable to the underlying relations of any automatically
+ * updatable view not using an INSTEAD OF trigger, so code here can
+ * assume it won't have one as a modification target. This node does
+ * process ri_WithCheckOptions, which may have expressions from those
+ * automatically updatable views.
+ *
* MERGE runs a join between the source relation and the target table.
* If any WHEN NOT MATCHED [BY TARGET] clauses are present, then the join
* is an outer join that might output tuples without a matching target
@@ -1398,18 +1406,18 @@ ExecDeleteEpilogue(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
* DELETE is like UPDATE, except that we delete the tuple and no
* index modifications are needed.
*
- * When deleting from a table, tupleid identifies the tuple to
- * delete and oldtuple is NULL. When deleting from a view,
- * oldtuple is passed to the INSTEAD OF triggers and identifies
- * what to delete, and tupleid is invalid. When deleting from a
- * foreign table, tupleid is invalid; the FDW has to figure out
- * which row to delete using data from the planSlot. oldtuple is
- * passed to foreign table triggers; it is NULL when the foreign
- * table has no relevant triggers. We use tupleDeleted to indicate
- * whether the tuple is actually deleted, callers can use it to
- * decide whether to continue the operation. When this DELETE is a
- * part of an UPDATE of partition-key, then the slot returned by
- * EvalPlanQual() is passed back using output parameter epqreturnslot.
+ * When deleting from a table, tupleid identifies the tuple to delete and
+ * oldtuple is NULL. When deleting through a view INSTEAD OF trigger,
+ * oldtuple is passed to the triggers and identifies what to delete, and
+ * tupleid is invalid. When deleting from a foreign table, tupleid is
+ * invalid; the FDW has to figure out which row to delete using data from
+ * the planSlot. oldtuple is passed to foreign table triggers; it is
+ * NULL when the foreign table has no relevant triggers. We use
+ * tupleDeleted to indicate whether the tuple is actually deleted,
+ * callers can use it to decide whether to continue the operation. When
+ * this DELETE is a part of an UPDATE of partition-key, then the slot
+ * returned by EvalPlanQual() is passed back using output parameter
+ * epqreturnslot.
*
* Returns RETURNING result if any, otherwise NULL.
* ----------------------------------------------------------------
@@ -2238,21 +2246,22 @@ ExecCrossPartitionUpdateForeignKey(ModifyTableContext *context,
* is, we don't want to get stuck in an infinite loop
* which corrupts your database..
*
- * When updating a table, tupleid identifies the tuple to
- * update and oldtuple is NULL. When updating a view, oldtuple
- * is passed to the INSTEAD OF triggers and identifies what to
- * update, and tupleid is invalid. When updating a foreign table,
- * tupleid is invalid; the FDW has to figure out which row to
- * update using data from the planSlot. oldtuple is passed to
- * foreign table triggers; it is NULL when the foreign table has
- * no relevant triggers.
+ * When updating a table, tupleid identifies the tuple to update and
+ * oldtuple is NULL. When updating through a view INSTEAD OF trigger,
+ * oldtuple is passed to the triggers and identifies what to update, and
+ * tupleid is invalid. When updating a foreign table, tupleid is
+ * invalid; the FDW has to figure out which row to update using data from
+ * the planSlot. oldtuple is passed to foreign table triggers; it is
+ * NULL when the foreign table has no relevant triggers.
*
* slot contains the new tuple value to be stored.
* planSlot is the output of the ModifyTable's subplan; we use it
* to access values from other input tables (for RETURNING),
* row-ID junk columns, etc.
*
- * Returns RETURNING result if any, otherwise NULL.
+ * Returns RETURNING result if any, otherwise NULL. On exit, if tupleid
+ * had identified the tuple to update, it will identify the tuple
+ * actually updated after EvalPlanQual.
* ----------------------------------------------------------------
*/
static TupleTableSlot *
@@ -2717,10 +2726,10 @@ ExecMerge(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
/*-----
* If we are dealing with a WHEN MATCHED case, tupleid or oldtuple is
- * valid, depending on whether the result relation is a table or a view.
- * We execute the first action for which the additional WHEN MATCHED AND
- * quals pass. If an action without quals is found, that action is
- * executed.
+ * valid, depending on whether the result relation is a table or a view
+ * having an INSTEAD OF trigger. We 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, in the WHEN NOT MATCHED BY SOURCE case, tupleid or oldtuple
* is valid, and we look at the given WHEN NOT MATCHED BY SOURCE actions
@@ -2811,8 +2820,8 @@ ExecMerge(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
* Check and execute the first qualifying MATCHED or NOT MATCHED BY SOURCE
* action, depending on whether the join quals are satisfied. If the target
* relation is a table, the current target tuple is identified by tupleid.
- * Otherwise, if the target relation is a view, oldtuple is the current target
- * tuple from the view.
+ * Otherwise, if the target relation is a view having an INSTEAD OF trigger,
+ * oldtuple is the current target tuple from the view.
*
* We start from the first WHEN MATCHED or WHEN NOT MATCHED BY SOURCE action
* 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,
*/
Assert(tupleid != NULL || oldtuple != NULL);
if (oldtuple != NULL)
+ {
+ Assert(resultRelInfo->ri_TrigDesc);
ExecForceStoreHeapTuple(oldtuple, resultRelInfo->ri_oldTupleSlot,
false);
+ }
else if (!table_tuple_fetch_row_version(resultRelInfo->ri_RelationDesc,
tupleid,
SnapshotAny,
@@ -3992,8 +4004,8 @@ ExecModifyTable(PlanState *pstate)
* know enough here to set t_tableOid. Quite separately from
* this, the FDW may fetch its own junk attrs to identify the row.
*
- * Other relevant relkinds, currently limited to views, always
- * have a wholerow attribute.
+ * Other relevant relkinds, currently limited to views having
+ * INSTEAD OF triggers, always have a wholerow attribute.
*/
else if (AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
{