LCOV - code coverage report
Current view: top level - src/backend/executor - tstoreReceiver.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 99.0 % 96 95
Test Date: 2026-07-26 06:15:43 Functions: 100.0 % 8 8
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 94.1 % 34 32

             Branch data     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                 :       33393 : tstoreStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo)
      58                 :             : {
      59                 :       33393 :     TStoreState *myState = (TStoreState *) self;
      60                 :       33393 :     bool        needtoast = false;
      61                 :       33393 :     int         natts = typeinfo->natts;
      62                 :             :     int         i;
      63                 :             : 
      64                 :             :     /* Check if any columns require detoast work */
      65         [ +  + ]:       33393 :     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         [ +  + ]:       33393 :     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                 :       31574 :         myState->tupmap = NULL;
      88                 :             : 
      89                 :             :     /* Set up appropriate callback */
      90         [ +  + ]:       33392 :     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         [ +  + ]:       33379 :     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                 :       33354 :         myState->pub.receiveSlot = tstoreReceiveSlot_notoast;
     112                 :       33354 :         myState->outvalues = NULL;
     113                 :       33354 :         myState->tofree = NULL;
     114                 :       33354 :         myState->mapslot = NULL;
     115                 :             :     }
     116                 :       33392 : }
     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                 :      320766 : tstoreReceiveSlot_notoast(TupleTableSlot *slot, DestReceiver *self)
     124                 :             : {
     125                 :      320766 :     TStoreState *myState = (TStoreState *) self;
     126                 :             : 
     127                 :      320766 :     tuplestore_puttupleslot(myState->tstore, slot);
     128                 :             : 
     129                 :      320766 :     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                 :             :                                                             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                 :       33268 : tstoreShutdownReceiver(DestReceiver *self)
     208                 :             : {
     209                 :       33268 :     TStoreState *myState = (TStoreState *) self;
     210                 :             : 
     211                 :             :     /* Release workspace if any */
     212         [ +  + ]:       33268 :     if (myState->outvalues)
     213                 :          13 :         pfree(myState->outvalues);
     214                 :       33268 :     myState->outvalues = NULL;
     215         [ +  + ]:       33268 :     if (myState->tofree)
     216                 :          13 :         pfree(myState->tofree);
     217                 :       33268 :     myState->tofree = NULL;
     218         [ +  + ]:       33268 :     if (myState->tupmap)
     219                 :          25 :         free_conversion_map(myState->tupmap);
     220                 :       33268 :     myState->tupmap = NULL;
     221         [ +  + ]:       33268 :     if (myState->mapslot)
     222                 :          25 :         ExecDropSingleTupleTableSlot(myState->mapslot);
     223                 :       33268 :     myState->mapslot = NULL;
     224                 :       33268 : }
     225                 :             : 
     226                 :             : /*
     227                 :             :  * Destroy receiver when done with it
     228                 :             :  */
     229                 :             : static void
     230                 :       33256 : tstoreDestroyReceiver(DestReceiver *self)
     231                 :             : {
     232                 :       33256 :     pfree(self);
     233                 :       33256 : }
     234                 :             : 
     235                 :             : /*
     236                 :             :  * Initially create a DestReceiver object.
     237                 :             :  */
     238                 :             : DestReceiver *
     239                 :       33621 : CreateTuplestoreDestReceiver(void)
     240                 :             : {
     241                 :       33621 :     TStoreState *self = palloc0_object(TStoreState);
     242                 :             : 
     243                 :       33621 :     self->pub.receiveSlot = tstoreReceiveSlot_notoast;   /* might change */
     244                 :       33621 :     self->pub.rStartup = tstoreStartupReceiver;
     245                 :       33621 :     self->pub.rShutdown = tstoreShutdownReceiver;
     246                 :       33621 :     self->pub.rDestroy = tstoreDestroyReceiver;
     247                 :       33621 :     self->pub.mydest = DestTuplestore;
     248                 :             : 
     249                 :             :     /* private fields will be set by SetTuplestoreDestReceiverParams */
     250                 :             : 
     251                 :       33621 :     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                 :       33621 : SetTuplestoreDestReceiverParams(DestReceiver *self,
     268                 :             :                                 Tuplestorestate *tStore,
     269                 :             :                                 MemoryContext tContext,
     270                 :             :                                 bool detoast,
     271                 :             :                                 TupleDesc target_tupdesc,
     272                 :             :                                 const char *map_failure_msg)
     273                 :             : {
     274                 :       33621 :     TStoreState *myState = (TStoreState *) self;
     275                 :             : 
     276                 :             :     Assert(!(detoast && target_tupdesc));
     277                 :             : 
     278                 :             :     Assert(myState->pub.mydest == DestTuplestore);
     279                 :       33621 :     myState->tstore = tStore;
     280                 :       33621 :     myState->cxt = tContext;
     281                 :       33621 :     myState->detoast = detoast;
     282                 :       33621 :     myState->target_tupdesc = target_tupdesc;
     283                 :       33621 :     myState->map_failure_msg = map_failure_msg;
     284                 :       33621 : }
        

Generated by: LCOV version 2.0-1