Expand comments and add an assertion in nodeModifyTable.c.
Most comments concern RELKIND_VIEW. One addresses the ExecUpdate() "tupleid" parameter. A later commit will rely on these facts, but they hold already. Back-patch to v12 (all supported versions), the plan for that commit. Reviewed (in an earlier version) by Robert Haas. Discussion: https://postgr.es/m/20240512232923.aa.nmisch@google.com
This commit is contained in:
parent
c35f419d6e
commit
d5f788b41d
@ -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))
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user