LCOV - code coverage report
Current view: top level - src/backend/executor - tstoreReceiver.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 99.1 % 117 116
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 8 8
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 84.8 % 46 39

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

Generated by: LCOV version 2.3.2-1