LCOV - code coverage report
Current view: top level - src/backend/executor - tstoreReceiver.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 99.0 % 97 96
Test Date: 2026-03-14 09:15:06 Functions: 100.0 % 8 8
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-2026, 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        27458 : tstoreStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo)
      58              : {
      59        27458 :     TStoreState *myState = (TStoreState *) self;
      60        27458 :     bool        needtoast = false;
      61        27458 :     int         natts = typeinfo->natts;
      62              :     int         i;
      63              : 
      64              :     /* Check if any columns require detoast work */
      65        27458 :     if (myState->detoast)
      66              :     {
      67          173 :         for (i = 0; i < natts; i++)
      68              :         {
      69          141 :             CompactAttribute *attr = TupleDescCompactAttr(typeinfo, i);
      70              : 
      71          141 :             if (attr->attisdropped)
      72            0 :                 continue;
      73          141 :             if (attr->attlen == -1)
      74              :             {
      75           10 :                 needtoast = true;
      76           10 :                 break;
      77              :             }
      78              :         }
      79              :     }
      80              : 
      81              :     /* Check if tuple conversion is needed */
      82        27458 :     if (myState->target_tupdesc)
      83         1341 :         myState->tupmap = convert_tuples_by_position(typeinfo,
      84              :                                                      myState->target_tupdesc,
      85              :                                                      myState->map_failure_msg);
      86              :     else
      87        26117 :         myState->tupmap = NULL;
      88              : 
      89              :     /* Set up appropriate callback */
      90        27457 :     if (needtoast)
      91              :     {
      92              :         Assert(!myState->tupmap);
      93           10 :         myState->pub.receiveSlot = tstoreReceiveSlot_detoast;
      94              :         /* Create workspace */
      95           10 :         myState->outvalues = (Datum *)
      96           10 :             MemoryContextAlloc(myState->cxt, natts * sizeof(Datum));
      97           10 :         myState->tofree = (Datum *)
      98           10 :             MemoryContextAlloc(myState->cxt, natts * sizeof(Datum));
      99           10 :         myState->mapslot = NULL;
     100              :     }
     101        27447 :     else if (myState->tupmap)
     102              :     {
     103           19 :         myState->pub.receiveSlot = tstoreReceiveSlot_tupmap;
     104           19 :         myState->outvalues = NULL;
     105           19 :         myState->tofree = NULL;
     106           19 :         myState->mapslot = MakeSingleTupleTableSlot(myState->target_tupdesc,
     107              :                                                     &TTSOpsVirtual);
     108              :     }
     109              :     else
     110              :     {
     111        27428 :         myState->pub.receiveSlot = tstoreReceiveSlot_notoast;
     112        27428 :         myState->outvalues = NULL;
     113        27428 :         myState->tofree = NULL;
     114        27428 :         myState->mapslot = NULL;
     115              :     }
     116        27457 : }
     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       270551 : tstoreReceiveSlot_notoast(TupleTableSlot *slot, DestReceiver *self)
     124              : {
     125       270551 :     TStoreState *myState = (TStoreState *) self;
     126              : 
     127       270551 :     tuplestore_puttupleslot(myState->tstore, slot);
     128              : 
     129       270551 :     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           23 : tstoreReceiveSlot_detoast(TupleTableSlot *slot, DestReceiver *self)
     138              : {
     139           23 :     TStoreState *myState = (TStoreState *) self;
     140           23 :     TupleDesc   typeinfo = slot->tts_tupleDescriptor;
     141           23 :     int         natts = typeinfo->natts;
     142              :     int         nfree;
     143              :     int         i;
     144              :     MemoryContext oldcxt;
     145              : 
     146              :     /* Make sure the tuple is fully deconstructed */
     147           23 :     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           23 :     nfree = 0;
     155           64 :     for (i = 0; i < natts; i++)
     156              :     {
     157           41 :         Datum       val = slot->tts_values[i];
     158           41 :         CompactAttribute *attr = TupleDescCompactAttr(typeinfo, i);
     159              : 
     160           41 :         if (!attr->attisdropped && attr->attlen == -1 && !slot->tts_isnull[i])
     161              :         {
     162           20 :             if (VARATT_IS_EXTERNAL(DatumGetPointer(val)))
     163              :             {
     164            5 :                 val = PointerGetDatum(detoast_external_attr((varlena *)
     165            5 :                                                             DatumGetPointer(val)));
     166            5 :                 myState->tofree[nfree++] = val;
     167              :             }
     168              :         }
     169              : 
     170           41 :         myState->outvalues[i] = val;
     171              :     }
     172              : 
     173              :     /*
     174              :      * Push the modified tuple into the tuplestore.
     175              :      */
     176           23 :     oldcxt = MemoryContextSwitchTo(myState->cxt);
     177           23 :     tuplestore_putvalues(myState->tstore, typeinfo,
     178           23 :                          myState->outvalues, slot->tts_isnull);
     179           23 :     MemoryContextSwitchTo(oldcxt);
     180              : 
     181              :     /* And release any temporary detoasted values */
     182           28 :     for (i = 0; i < nfree; i++)
     183            5 :         pfree(DatumGetPointer(myState->tofree[i]));
     184              : 
     185           23 :     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           37 : tstoreReceiveSlot_tupmap(TupleTableSlot *slot, DestReceiver *self)
     194              : {
     195           37 :     TStoreState *myState = (TStoreState *) self;
     196              : 
     197           37 :     execute_attr_map_slot(myState->tupmap->attrMap, slot, myState->mapslot);
     198           37 :     tuplestore_puttupleslot(myState->tstore, myState->mapslot);
     199              : 
     200           37 :     return true;
     201              : }
     202              : 
     203              : /*
     204              :  * Clean up at end of an executor run
     205              :  */
     206              : static void
     207        27368 : tstoreShutdownReceiver(DestReceiver *self)
     208              : {
     209        27368 :     TStoreState *myState = (TStoreState *) self;
     210              : 
     211              :     /* Release workspace if any */
     212        27368 :     if (myState->outvalues)
     213           10 :         pfree(myState->outvalues);
     214        27368 :     myState->outvalues = NULL;
     215        27368 :     if (myState->tofree)
     216           10 :         pfree(myState->tofree);
     217        27368 :     myState->tofree = NULL;
     218        27368 :     if (myState->tupmap)
     219           19 :         free_conversion_map(myState->tupmap);
     220        27368 :     myState->tupmap = NULL;
     221        27368 :     if (myState->mapslot)
     222           19 :         ExecDropSingleTupleTableSlot(myState->mapslot);
     223        27368 :     myState->mapslot = NULL;
     224        27368 : }
     225              : 
     226              : /*
     227              :  * Destroy receiver when done with it
     228              :  */
     229              : static void
     230        27359 : tstoreDestroyReceiver(DestReceiver *self)
     231              : {
     232        27359 :     pfree(self);
     233        27359 : }
     234              : 
     235              : /*
     236              :  * Initially create a DestReceiver object.
     237              :  */
     238              : DestReceiver *
     239        27616 : CreateTuplestoreDestReceiver(void)
     240              : {
     241        27616 :     TStoreState *self = palloc0_object(TStoreState);
     242              : 
     243        27616 :     self->pub.receiveSlot = tstoreReceiveSlot_notoast;   /* might change */
     244        27616 :     self->pub.rStartup = tstoreStartupReceiver;
     245        27616 :     self->pub.rShutdown = tstoreShutdownReceiver;
     246        27616 :     self->pub.rDestroy = tstoreDestroyReceiver;
     247        27616 :     self->pub.mydest = DestTuplestore;
     248              : 
     249              :     /* private fields will be set by SetTuplestoreDestReceiverParams */
     250              : 
     251        27616 :     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        27616 : SetTuplestoreDestReceiverParams(DestReceiver *self,
     268              :                                 Tuplestorestate *tStore,
     269              :                                 MemoryContext tContext,
     270              :                                 bool detoast,
     271              :                                 TupleDesc target_tupdesc,
     272              :                                 const char *map_failure_msg)
     273              : {
     274        27616 :     TStoreState *myState = (TStoreState *) self;
     275              : 
     276              :     Assert(!(detoast && target_tupdesc));
     277              : 
     278              :     Assert(myState->pub.mydest == DestTuplestore);
     279        27616 :     myState->tstore = tStore;
     280        27616 :     myState->cxt = tContext;
     281        27616 :     myState->detoast = detoast;
     282        27616 :     myState->target_tupdesc = target_tupdesc;
     283        27616 :     myState->map_failure_msg = map_failure_msg;
     284        27616 : }
        

Generated by: LCOV version 2.0-1