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

Generated by: LCOV version 2.0-1