a thinko in b676ac443b6
Hi,
I noticed $subject while rebasing my patch at [1]https://commitfest.postgresql.org/33/2992/ to enable batching
for the inserts used in cross-partition UPDATEs.
b676ac443b6 did this:
- resultRelInfo->ri_PlanSlots[resultRelInfo->ri_NumSlots] =
- MakeSingleTupleTableSlot(planSlot->tts_tupleDescriptor,
- planSlot->tts_ops);
...
+ {
+ TupleDesc tdesc =
CreateTupleDescCopy(slot->tts_tupleDescriptor);
+
+ resultRelInfo->ri_Slots[resultRelInfo->ri_NumSlots] =
+ MakeSingleTupleTableSlot(tdesc, slot->tts_ops);
...
+ resultRelInfo->ri_PlanSlots[resultRelInfo->ri_NumSlots] =
+ MakeSingleTupleTableSlot(tdesc, planSlot->tts_ops);
I think it can be incorrect to use the same TupleDesc for both the
slots in ri_Slots (for ready-to-be-inserted tuples) and ri_PlanSlots
(for subplan output tuples). Especially if you consider what we did
in 86dc90056df that was committed into v14. In that commit, we
changed the way a subplan under ModifyTable produces its output for an
UPDATE statement. Previously, it would produce a tuple matching the
target table's TupleDesc exactly (plus any junk columns), but now it
produces only a partial tuple containing the values for the changed
columns.
So it's better to revert to using planSlot->tts_tupleDescriptor for
the slots in ri_PlanSlots. Attached a patch to do so.
--
Amit Langote
EDB: http://www.enterprisedb.com
Attachments:
b676ac443b6-thinko-fix.patchapplication/octet-stream; name=b676ac443b6-thinko-fix.patchDownload
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index c24684aa6f..d328856ae5 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -709,17 +709,19 @@ ExecInsert(ModifyTableState *mtstate,
* keep them across batches. To mitigate an inefficiency in how
* resource owner handles objects with many references (as with
* many slots all referencing the same tuple descriptor) we copy
- * the tuple descriptor for each slot.
+ * the appropriate tuple descriptor for each slot.
*/
if (resultRelInfo->ri_NumSlots >= resultRelInfo->ri_NumSlotsInitialized)
{
TupleDesc tdesc = CreateTupleDescCopy(slot->tts_tupleDescriptor);
+ TupleDesc plan_tdesc =
+ CreateTupleDescCopy(planSlot->tts_tupleDescriptor);
resultRelInfo->ri_Slots[resultRelInfo->ri_NumSlots] =
MakeSingleTupleTableSlot(tdesc, slot->tts_ops);
resultRelInfo->ri_PlanSlots[resultRelInfo->ri_NumSlots] =
- MakeSingleTupleTableSlot(tdesc, planSlot->tts_ops);
+ MakeSingleTupleTableSlot(plan_tdesc, planSlot->tts_ops);
/* remember how many batch slots we initialized */
resultRelInfo->ri_NumSlotsInitialized++;
On 7/27/21 4:28 AM, Amit Langote wrote:
Hi,
I noticed $subject while rebasing my patch at [1] to enable batching
for the inserts used in cross-partition UPDATEs.b676ac443b6 did this:
- resultRelInfo->ri_PlanSlots[resultRelInfo->ri_NumSlots] = - MakeSingleTupleTableSlot(planSlot->tts_tupleDescriptor, - planSlot->tts_ops); ... + { + TupleDesc tdesc = CreateTupleDescCopy(slot->tts_tupleDescriptor); + + resultRelInfo->ri_Slots[resultRelInfo->ri_NumSlots] = + MakeSingleTupleTableSlot(tdesc, slot->tts_ops); ... + resultRelInfo->ri_PlanSlots[resultRelInfo->ri_NumSlots] = + MakeSingleTupleTableSlot(tdesc, planSlot->tts_ops);I think it can be incorrect to use the same TupleDesc for both the
slots in ri_Slots (for ready-to-be-inserted tuples) and ri_PlanSlots
(for subplan output tuples). Especially if you consider what we did
in 86dc90056df that was committed into v14. In that commit, we
changed the way a subplan under ModifyTable produces its output for an
UPDATE statement. Previously, it would produce a tuple matching the
target table's TupleDesc exactly (plus any junk columns), but now it
produces only a partial tuple containing the values for the changed
columns.So it's better to revert to using planSlot->tts_tupleDescriptor for
the slots in ri_PlanSlots. Attached a patch to do so.
Yeah, this seems like a clear mistake - thanks for noticing it! Clearly
no regression test triggered the issue, so I wonder what's the best way
to test it - any idea what would the test need to do?
I did some quick experiments with batched INSERTs with RETURNING clauses
and/or subplans, but I haven't succeeded in triggering the issue :-(
regards
--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Wed, Jul 28, 2021 at 1:07 AM Tomas Vondra
<tomas.vondra@enterprisedb.com> wrote:
On 7/27/21 4:28 AM, Amit Langote wrote:
I think it can be incorrect to use the same TupleDesc for both the
slots in ri_Slots (for ready-to-be-inserted tuples) and ri_PlanSlots
(for subplan output tuples). Especially if you consider what we did
in 86dc90056df that was committed into v14. In that commit, we
changed the way a subplan under ModifyTable produces its output for an
UPDATE statement. Previously, it would produce a tuple matching the
target table's TupleDesc exactly (plus any junk columns), but now it
produces only a partial tuple containing the values for the changed
columns.So it's better to revert to using planSlot->tts_tupleDescriptor for
the slots in ri_PlanSlots. Attached a patch to do so.Yeah, this seems like a clear mistake - thanks for noticing it! Clearly
no regression test triggered the issue, so I wonder what's the best way
to test it - any idea what would the test need to do?
Ah, I should've mentioned that this is only a problem if the original
query is an UPDATE. With v14, only INSERTs can use batching and the
subplan does output a tuple matching the target table's TupleDesc in
their case, so the code seems to work fine.
As I said, I noticed a problem when rebasing my patch to allow
cross-partition UPDATEs to use batching for the inserts that are
performed internally to implement such UPDATEs. The exact problem I
noticed is that the following Assert tts_virtual_copyslot() (via
ExecCopySlot called with an ri_PlanSlots[] entry) failed:
Assert(srcdesc->natts <= dstslot->tts_tupleDescriptor->natts);
srcdesc in this case is a slot in ri_PlanSlots[] initialized with the
target table's TupleDesc (the "thinko") and dstslot is the slot that
holds subplan's output tuple ('planSlot' passed to ExecInsert). As I
described in my previous email, dstslot's TupleDesc can be narrower
than the target table's TupleDesc in the case of an UPDATE, so the
Assert can fail in theory.
I did some quick experiments with batched INSERTs with RETURNING clauses
and/or subplans, but I haven't succeeded in triggering the issue :-(
Yeah, no way to trigger this except UPDATEs. It still seems like a
good idea to fix this in v14.
--
Amit Langote
EDB: http://www.enterprisedb.com
On 7/28/21 3:15 AM, Amit Langote wrote:
On Wed, Jul 28, 2021 at 1:07 AM Tomas Vondra
<tomas.vondra@enterprisedb.com> wrote:On 7/27/21 4:28 AM, Amit Langote wrote:
I think it can be incorrect to use the same TupleDesc for both the
slots in ri_Slots (for ready-to-be-inserted tuples) and ri_PlanSlots
(for subplan output tuples). Especially if you consider what we did
in 86dc90056df that was committed into v14. In that commit, we
changed the way a subplan under ModifyTable produces its output for an
UPDATE statement. Previously, it would produce a tuple matching the
target table's TupleDesc exactly (plus any junk columns), but now it
produces only a partial tuple containing the values for the changed
columns.So it's better to revert to using planSlot->tts_tupleDescriptor for
the slots in ri_PlanSlots. Attached a patch to do so.Yeah, this seems like a clear mistake - thanks for noticing it! Clearly
no regression test triggered the issue, so I wonder what's the best way
to test it - any idea what would the test need to do?Ah, I should've mentioned that this is only a problem if the original
query is an UPDATE. With v14, only INSERTs can use batching and the
subplan does output a tuple matching the target table's TupleDesc in
their case, so the code seems to work fine.As I said, I noticed a problem when rebasing my patch to allow
cross-partition UPDATEs to use batching for the inserts that are
performed internally to implement such UPDATEs. The exact problem I
noticed is that the following Assert tts_virtual_copyslot() (via
ExecCopySlot called with an ri_PlanSlots[] entry) failed:Assert(srcdesc->natts <= dstslot->tts_tupleDescriptor->natts);
srcdesc in this case is a slot in ri_PlanSlots[] initialized with the
target table's TupleDesc (the "thinko") and dstslot is the slot that
holds subplan's output tuple ('planSlot' passed to ExecInsert). As I
described in my previous email, dstslot's TupleDesc can be narrower
than the target table's TupleDesc in the case of an UPDATE, so the
Assert can fail in theory.I did some quick experiments with batched INSERTs with RETURNING clauses
and/or subplans, but I haven't succeeded in triggering the issue :-(Yeah, no way to trigger this except UPDATEs. It still seems like a
good idea to fix this in v14.
OK, thanks for the explanation. So it's benign in v14, but I agree it's
better to fix it there too. I'll get this sorted/pushed.
regards
--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company