Allow buffer tuple table slots to materialize after ExecStoreVirtualTuple().
authorAndres Freund <[email protected]>
Thu, 28 Feb 2019 20:27:58 +0000 (12:27 -0800)
committerAndres Freund <[email protected]>
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[email protected]
    https://postgr.es/m/20180703070645[email protected]

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);