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:
Noah Misch 2024-06-27 19:21:05 -07:00
parent dd8008e8ec
commit f8135d8b54

View File

@ -24,6 +24,14 @@
* values plus row-locating info for UPDATE and MERGE cases, or just the * values plus row-locating info for UPDATE and MERGE cases, or just the
* row-locating info for DELETE cases. * 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 * MERGE runs a join between the source relation and the target
* table; if any WHEN NOT MATCHED clauses are present, then the * table; if any WHEN NOT MATCHED clauses are present, then the
* join is an outer join. In this case, any unmatched tuples will * join is an outer join. In this case, any unmatched tuples will
@ -1401,18 +1409,18 @@ ExecDeleteEpilogue(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
* DELETE is like UPDATE, except that we delete the tuple and no * DELETE is like UPDATE, except that we delete the tuple and no
* index modifications are needed. * index modifications are needed.
* *
* When deleting from a table, tupleid identifies the tuple to * When deleting from a table, tupleid identifies the tuple to delete and
* delete and oldtuple is NULL. When deleting from a view, * oldtuple is NULL. When deleting through a view INSTEAD OF trigger,
* oldtuple is passed to the INSTEAD OF triggers and identifies * oldtuple is passed to the triggers and identifies what to delete, and
* what to delete, and tupleid is invalid. When deleting from a * tupleid is invalid. When deleting from a foreign table, tupleid is
* foreign table, tupleid is invalid; the FDW has to figure out * invalid; the FDW has to figure out which row to delete using data from
* which row to delete using data from the planSlot. oldtuple is * the planSlot. oldtuple is passed to foreign table triggers; it is
* passed to foreign table triggers; it is NULL when the foreign * NULL when the foreign table has no relevant triggers. We use
* table has no relevant triggers. We use tupleDeleted to indicate * tupleDeleted to indicate whether the tuple is actually deleted,
* whether the tuple is actually deleted, callers can use it to * callers can use it to decide whether to continue the operation. When
* decide whether to continue the operation. When this DELETE is a * this DELETE is a part of an UPDATE of partition-key, then the slot
* part of an UPDATE of partition-key, then the slot returned by * returned by EvalPlanQual() is passed back using output parameter
* EvalPlanQual() is passed back using output parameter epqreturnslot. * epqreturnslot.
* *
* Returns RETURNING result if any, otherwise NULL. * Returns RETURNING result if any, otherwise NULL.
* ---------------------------------------------------------------- * ----------------------------------------------------------------
@ -2244,21 +2252,22 @@ ExecCrossPartitionUpdateForeignKey(ModifyTableContext *context,
* is, we don't want to get stuck in an infinite loop * is, we don't want to get stuck in an infinite loop
* which corrupts your database.. * which corrupts your database..
* *
* When updating a table, tupleid identifies the tuple to * When updating a table, tupleid identifies the tuple to update and
* update and oldtuple is NULL. When updating a view, oldtuple * oldtuple is NULL. When updating through a view INSTEAD OF trigger,
* is passed to the INSTEAD OF triggers and identifies what to * oldtuple is passed to the triggers and identifies what to update, and
* update, and tupleid is invalid. When updating a foreign table, * tupleid is invalid. When updating a foreign table, tupleid is
* tupleid is invalid; the FDW has to figure out which row to * invalid; the FDW has to figure out which row to update using data from
* update using data from the planSlot. oldtuple is passed to * the planSlot. oldtuple is passed to foreign table triggers; it is
* foreign table triggers; it is NULL when the foreign table has * NULL when the foreign table has no relevant triggers.
* no relevant triggers.
* *
* slot contains the new tuple value to be stored. * slot contains the new tuple value to be stored.
* planSlot is the output of the ModifyTable's subplan; we use it * planSlot is the output of the ModifyTable's subplan; we use it
* to access values from other input tables (for RETURNING), * to access values from other input tables (for RETURNING),
* row-ID junk columns, etc. * 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 * static TupleTableSlot *
@ -3800,8 +3809,8 @@ ExecModifyTable(PlanState *pstate)
* know enough here to set t_tableOid. Quite separately from * know enough here to set t_tableOid. Quite separately from
* this, the FDW may fetch its own junk attrs to identify the row. * this, the FDW may fetch its own junk attrs to identify the row.
* *
* Other relevant relkinds, currently limited to views, always * Other relevant relkinds, currently limited to views having
* have a wholerow attribute. * INSTEAD OF triggers, always have a wholerow attribute.
*/ */
else if (AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo)) else if (AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
{ {