]> granicus.if.org Git - postgresql/commitdiff
Allow buffer tuple table slots to materialize after ExecStoreVirtualTuple().
authorAndres Freund <andres@anarazel.de>
Thu, 28 Feb 2019 20:27:58 +0000 (12:27 -0800)
committerAndres Freund <andres@anarazel.de>
Thu, 28 Feb 2019 20:28:03 +0000 (12:28 -0800)
While not common, it can be useful to store a virtual tuple into a
buffer tuple table slot, and then materialize that slot. So far we've
asserted out, which surprisingly wasn't a problem for anything in
core. But that seems fragile, and it also breaks redis_fdw after
ff11e7f4b9.

Thus, allow materializing a virtual tuple stored in a buffer tuple
table slot.

Author: Andres Freund
Discussion:
    https://postgr.es/m/20190227181621.xholonj7ff7ohxsg@alap3.anarazel.de
    https://postgr.es/m/20180703070645.wchpu5muyto5n647@alap3.anarazel.de

src/backend/executor/execTuples.c

index 3a1425182144191763935c53a84682fac96a8415..121649f34351c6c2a460f2626135f3bccb259a8e 100644 (file)
@@ -700,25 +700,36 @@ tts_buffer_heap_materialize(TupleTableSlot *slot)
 
        slot->tts_flags |= TTS_FLAG_SHOULDFREE;
 
-       /*
-        * A heap tuple stored in a BufferHeapTupleTableSlot should have a buffer
-        * associated with it, unless it's materialized (which would've returned
-        * above).
-        */
-       Assert(bslot->base.tuple);
-
        oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
-       bslot->base.tuple = heap_copytuple(bslot->base.tuple);
-       MemoryContextSwitchTo(oldContext);
 
-       /*
-        * A heap tuple stored in a BufferHeapTupleTableSlot should have a buffer
-        * associated with it, unless it's materialized.
-        */
-       Assert(BufferIsValid(bslot->buffer));
-       if (likely(BufferIsValid(bslot->buffer)))
-               ReleaseBuffer(bslot->buffer);
-       bslot->buffer = InvalidBuffer;
+       if (!bslot->base.tuple)
+       {
+               /*
+                * Normally BufferHeapTupleTableSlot should have a tuple + buffer
+                * associated with it, unless it's materialized (which would've
+                * returned above). But when it's useful to allow storing virtual
+                * tuples in a buffer slot, which then also needs to be
+                * materializable.
+                */
+               bslot->base.tuple = heap_form_tuple(slot->tts_tupleDescriptor,
+                                                                                       slot->tts_values,
+                                                                                       slot->tts_isnull);
+
+       }
+       else
+       {
+               bslot->base.tuple = heap_copytuple(bslot->base.tuple);
+
+               /*
+                * A heap tuple stored in a BufferHeapTupleTableSlot should have a
+                * buffer associated with it, unless it's materialized or virtual.
+                */
+               Assert(BufferIsValid(bslot->buffer));
+               if (likely(BufferIsValid(bslot->buffer)))
+                       ReleaseBuffer(bslot->buffer);
+               bslot->buffer = InvalidBuffer;
+       }
+       MemoryContextSwitchTo(oldContext);
 
        /*
         * Have to deform from scratch, otherwise tts_values[] entries could point
@@ -752,6 +763,9 @@ tts_buffer_heap_copyslot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
        }
        else
        {
+               if (!bsrcslot->base.tuple)
+                       tts_buffer_heap_materialize(srcslot);
+
                tts_buffer_heap_store_tuple(dstslot, bsrcslot->base.tuple,
                                                                        bsrcslot->buffer, false);