Coverage Report

Created: 2025-08-12 06:43

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