LCOV - code coverage report
Current view: top level - src/backend/executor - tstoreReceiver.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 96 97 99.0 %
Date: 2025-10-10 10:17:52 Functions: 8 8 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * tstoreReceiver.c
       4             :  *    An implementation of DestReceiver that stores the result tuples in
       5             :  *    a Tuplestore.
       6             :  *
       7             :  * Optionally, we can force detoasting (but not decompression) of out-of-line
       8             :  * toasted values.  This is to support cursors WITH HOLD, which must retain
       9             :  * data even if the underlying table is dropped.
      10             :  *
      11             :  * Also optionally, we can apply a tuple conversion map before storing.
      12             :  *
      13             :  *
      14             :  * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
      15             :  * Portions Copyright (c) 1994, Regents of the University of California
      16             :  *
      17             :  * IDENTIFICATION
      18             :  *    src/backend/executor/tstoreReceiver.c
      19             :  *
      20             :  *-------------------------------------------------------------------------
      21             :  */
      22             : 
      23             : #include "postgres.h"
      24             : 
      25             : #include "access/detoast.h"
      26             : #include "access/tupconvert.h"
      27             : #include "executor/tstoreReceiver.h"
      28             : #include "varatt.h"
      29             : 
      30             : 
      31             : typedef struct
      32             : {
      33             :     DestReceiver pub;
      34             :     /* parameters: */
      35             :     Tuplestorestate *tstore;    /* where to put the data */
      36             :     MemoryContext cxt;          /* context containing tstore */
      37             :     bool        detoast;        /* were we told to detoast? */
      38             :     TupleDesc   target_tupdesc; /* target tupdesc, or NULL if none */
      39             :     const char *map_failure_msg;    /* tupdesc mapping failure message */
      40             :     /* workspace: */
      41             :     Datum      *outvalues;      /* values array for result tuple */
      42             :     Datum      *tofree;         /* temp values to be pfree'd */
      43             :     TupleConversionMap *tupmap; /* conversion map, if needed */
      44             :     TupleTableSlot *mapslot;    /* slot for mapped tuples */
      45             : } TStoreState;
      46             : 
      47             : 
      48             : static bool tstoreReceiveSlot_notoast(TupleTableSlot *slot, DestReceiver *self);
      49             : static bool tstoreReceiveSlot_detoast(TupleTableSlot *slot, DestReceiver *self);
      50             : static bool tstoreReceiveSlot_tupmap(TupleTableSlot *slot, DestReceiver *self);
      51             : 
      52             : 
      53             : /*
      54             :  * Prepare to receive tuples from executor.
      55             :  */
      56             : static void
      57       52658 : tstoreStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo)
      58             : {
      59       52658 :     TStoreState *myState = (TStoreState *) self;
      60       52658 :     bool        needtoast = false;
      61       52658 :     int         natts = typeinfo->natts;
      62             :     int         i;
      63             : 
      64             :     /* Check if any columns require detoast work */
      65       52658 :     if (myState->detoast)
      66             :     {
      67         342 :         for (i = 0; i < natts; i++)
      68             :         {
      69         280 :             CompactAttribute *attr = TupleDescCompactAttr(typeinfo, i);
      70             : 
      71         280 :             if (attr->attisdropped)
      72           0 :                 continue;
      73         280 :             if (attr->attlen == -1)
      74             :             {
      75          20 :                 needtoast = true;
      76          20 :                 break;
      77             :             }
      78             :         }
      79             :     }
      80             : 
      81             :     /* Check if tuple conversion is needed */
      82       52658 :     if (myState->target_tupdesc)
      83        2682 :         myState->tupmap = convert_tuples_by_position(typeinfo,
      84             :                                                      myState->target_tupdesc,
      85             :                                                      myState->map_failure_msg);
      86             :     else
      87       49976 :         myState->tupmap = NULL;
      88             : 
      89             :     /* Set up appropriate callback */
      90       52656 :     if (needtoast)
      91             :     {
      92             :         Assert(!myState->tupmap);
      93          20 :         myState->pub.receiveSlot = tstoreReceiveSlot_detoast;
      94             :         /* Create workspace */
      95          20 :         myState->outvalues = (Datum *)
      96          20 :             MemoryContextAlloc(myState->cxt, natts * sizeof(Datum));
      97          20 :         myState->tofree = (Datum *)
      98          20 :             MemoryContextAlloc(myState->cxt, natts * sizeof(Datum));
      99          20 :         myState->mapslot = NULL;
     100             :     }
     101       52636 :     else if (myState->tupmap)
     102             :     {
     103          38 :         myState->pub.receiveSlot = tstoreReceiveSlot_tupmap;
     104          38 :         myState->outvalues = NULL;
     105          38 :         myState->tofree = NULL;
     106          38 :         myState->mapslot = MakeSingleTupleTableSlot(myState->target_tupdesc,
     107             :                                                     &TTSOpsVirtual);
     108             :     }
     109             :     else
     110             :     {
     111       52598 :         myState->pub.receiveSlot = tstoreReceiveSlot_notoast;
     112       52598 :         myState->outvalues = NULL;
     113       52598 :         myState->tofree = NULL;
     114       52598 :         myState->mapslot = NULL;
     115             :     }
     116       52656 : }
     117             : 
     118             : /*
     119             :  * Receive a tuple from the executor and store it in the tuplestore.
     120             :  * This is for the easy case where we don't have to detoast nor map anything.
     121             :  */
     122             : static bool
     123      526846 : tstoreReceiveSlot_notoast(TupleTableSlot *slot, DestReceiver *self)
     124             : {
     125      526846 :     TStoreState *myState = (TStoreState *) self;
     126             : 
     127      526846 :     tuplestore_puttupleslot(myState->tstore, slot);
     128             : 
     129      526846 :     return true;
     130             : }
     131             : 
     132             : /*
     133             :  * Receive a tuple from the executor and store it in the tuplestore.
     134             :  * This is for the case where we have to detoast any toasted values.
     135             :  */
     136             : static bool
     137          46 : tstoreReceiveSlot_detoast(TupleTableSlot *slot, DestReceiver *self)
     138             : {
     139          46 :     TStoreState *myState = (TStoreState *) self;
     140          46 :     TupleDesc   typeinfo = slot->tts_tupleDescriptor;
     141          46 :     int         natts = typeinfo->natts;
     142             :     int         nfree;
     143             :     int         i;
     144             :     MemoryContext oldcxt;
     145             : 
     146             :     /* Make sure the tuple is fully deconstructed */
     147          46 :     slot_getallattrs(slot);
     148             : 
     149             :     /*
     150             :      * Fetch back any out-of-line datums.  We build the new datums array in
     151             :      * myState->outvalues[] (but we can re-use the slot's isnull array). Also,
     152             :      * remember the fetched values to free afterwards.
     153             :      */
     154          46 :     nfree = 0;
     155         128 :     for (i = 0; i < natts; i++)
     156             :     {
     157          82 :         Datum       val = slot->tts_values[i];
     158          82 :         CompactAttribute *attr = TupleDescCompactAttr(typeinfo, i);
     159             : 
     160          82 :         if (!attr->attisdropped && attr->attlen == -1 && !slot->tts_isnull[i])
     161             :         {
     162          40 :             if (VARATT_IS_EXTERNAL(DatumGetPointer(val)))
     163             :             {
     164          10 :                 val = PointerGetDatum(detoast_external_attr((struct varlena *)
     165          10 :                                                             DatumGetPointer(val)));
     166          10 :                 myState->tofree[nfree++] = val;
     167             :             }
     168             :         }
     169             : 
     170          82 :         myState->outvalues[i] = val;
     171             :     }
     172             : 
     173             :     /*
     174             :      * Push the modified tuple into the tuplestore.
     175             :      */
     176          46 :     oldcxt = MemoryContextSwitchTo(myState->cxt);
     177          46 :     tuplestore_putvalues(myState->tstore, typeinfo,
     178          46 :                          myState->outvalues, slot->tts_isnull);
     179          46 :     MemoryContextSwitchTo(oldcxt);
     180             : 
     181             :     /* And release any temporary detoasted values */
     182          56 :     for (i = 0; i < nfree; i++)
     183          10 :         pfree(DatumGetPointer(myState->tofree[i]));
     184             : 
     185          46 :     return true;
     186             : }
     187             : 
     188             : /*
     189             :  * Receive a tuple from the executor and store it in the tuplestore.
     190             :  * This is for the case where we must apply a tuple conversion map.
     191             :  */
     192             : static bool
     193          74 : tstoreReceiveSlot_tupmap(TupleTableSlot *slot, DestReceiver *self)
     194             : {
     195          74 :     TStoreState *myState = (TStoreState *) self;
     196             : 
     197          74 :     execute_attr_map_slot(myState->tupmap->attrMap, slot, myState->mapslot);
     198          74 :     tuplestore_puttupleslot(myState->tstore, myState->mapslot);
     199             : 
     200          74 :     return true;
     201             : }
     202             : 
     203             : /*
     204             :  * Clean up at end of an executor run
     205             :  */
     206             : static void
     207       52508 : tstoreShutdownReceiver(DestReceiver *self)
     208             : {
     209       52508 :     TStoreState *myState = (TStoreState *) self;
     210             : 
     211             :     /* Release workspace if any */
     212       52508 :     if (myState->outvalues)
     213          20 :         pfree(myState->outvalues);
     214       52508 :     myState->outvalues = NULL;
     215       52508 :     if (myState->tofree)
     216          20 :         pfree(myState->tofree);
     217       52508 :     myState->tofree = NULL;
     218       52508 :     if (myState->tupmap)
     219          38 :         free_conversion_map(myState->tupmap);
     220       52508 :     myState->tupmap = NULL;
     221       52508 :     if (myState->mapslot)
     222          38 :         ExecDropSingleTupleTableSlot(myState->mapslot);
     223       52508 :     myState->mapslot = NULL;
     224       52508 : }
     225             : 
     226             : /*
     227             :  * Destroy receiver when done with it
     228             :  */
     229             : static void
     230       52490 : tstoreDestroyReceiver(DestReceiver *self)
     231             : {
     232       52490 :     pfree(self);
     233       52490 : }
     234             : 
     235             : /*
     236             :  * Initially create a DestReceiver object.
     237             :  */
     238             : DestReceiver *
     239       52924 : CreateTuplestoreDestReceiver(void)
     240             : {
     241       52924 :     TStoreState *self = (TStoreState *) palloc0(sizeof(TStoreState));
     242             : 
     243       52924 :     self->pub.receiveSlot = tstoreReceiveSlot_notoast;   /* might change */
     244       52924 :     self->pub.rStartup = tstoreStartupReceiver;
     245       52924 :     self->pub.rShutdown = tstoreShutdownReceiver;
     246       52924 :     self->pub.rDestroy = tstoreDestroyReceiver;
     247       52924 :     self->pub.mydest = DestTuplestore;
     248             : 
     249             :     /* private fields will be set by SetTuplestoreDestReceiverParams */
     250             : 
     251       52924 :     return (DestReceiver *) self;
     252             : }
     253             : 
     254             : /*
     255             :  * Set parameters for a TuplestoreDestReceiver
     256             :  *
     257             :  * tStore: where to store the tuples
     258             :  * tContext: memory context containing tStore
     259             :  * detoast: forcibly detoast contained data?
     260             :  * target_tupdesc: if not NULL, forcibly convert tuples to this rowtype
     261             :  * map_failure_msg: error message to use if mapping to target_tupdesc fails
     262             :  *
     263             :  * We don't currently support both detoast and target_tupdesc at the same
     264             :  * time, just because no existing caller needs that combination.
     265             :  */
     266             : void
     267       52924 : SetTuplestoreDestReceiverParams(DestReceiver *self,
     268             :                                 Tuplestorestate *tStore,
     269             :                                 MemoryContext tContext,
     270             :                                 bool detoast,
     271             :                                 TupleDesc target_tupdesc,
     272             :                                 const char *map_failure_msg)
     273             : {
     274       52924 :     TStoreState *myState = (TStoreState *) self;
     275             : 
     276             :     Assert(!(detoast && target_tupdesc));
     277             : 
     278             :     Assert(myState->pub.mydest == DestTuplestore);
     279       52924 :     myState->tstore = tStore;
     280       52924 :     myState->cxt = tContext;
     281       52924 :     myState->detoast = detoast;
     282       52924 :     myState->target_tupdesc = target_tupdesc;
     283       52924 :     myState->map_failure_msg = map_failure_msg;
     284       52924 : }

Generated by: LCOV version 1.16