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

Generated by: LCOV version 1.14