Coverage Report

Created: 2025-06-15 06:31

/src/postgres/src/backend/access/common/printtup.c
Line
Count
Source (jump to first uncovered line)
1
/*-------------------------------------------------------------------------
2
 *
3
 * printtup.c
4
 *    Routines to print out tuples to the destination (both frontend
5
 *    clients and standalone backends are supported here).
6
 *
7
 *
8
 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
9
 * Portions Copyright (c) 1994, Regents of the University of California
10
 *
11
 * IDENTIFICATION
12
 *    src/backend/access/common/printtup.c
13
 *
14
 *-------------------------------------------------------------------------
15
 */
16
#include "postgres.h"
17
18
#include "access/printtup.h"
19
#include "libpq/pqformat.h"
20
#include "libpq/protocol.h"
21
#include "tcop/pquery.h"
22
#include "utils/lsyscache.h"
23
#include "utils/memdebug.h"
24
#include "utils/memutils.h"
25
26
27
static void printtup_startup(DestReceiver *self, int operation,
28
               TupleDesc typeinfo);
29
static bool printtup(TupleTableSlot *slot, DestReceiver *self);
30
static void printtup_shutdown(DestReceiver *self);
31
static void printtup_destroy(DestReceiver *self);
32
33
/* ----------------------------------------------------------------
34
 *    printtup / debugtup support
35
 * ----------------------------------------------------------------
36
 */
37
38
/* ----------------
39
 *    Private state for a printtup destination object
40
 *
41
 * NOTE: finfo is the lookup info for either typoutput or typsend, whichever
42
 * we are using for this column.
43
 * ----------------
44
 */
45
typedef struct
46
{               /* Per-attribute information */
47
  Oid     typoutput;    /* Oid for the type's text output fn */
48
  Oid     typsend;    /* Oid for the type's binary output fn */
49
  bool    typisvarlena; /* is it varlena (ie possibly toastable)? */
50
  int16   format;     /* format code for this column */
51
  FmgrInfo  finfo;      /* Precomputed call info for output fn */
52
} PrinttupAttrInfo;
53
54
typedef struct
55
{
56
  DestReceiver pub;     /* publicly-known function pointers */
57
  Portal    portal;     /* the Portal we are printing from */
58
  bool    sendDescrip;  /* send RowDescription at startup? */
59
  TupleDesc attrinfo;   /* The attr info we are set up for */
60
  int     nattrs;
61
  PrinttupAttrInfo *myinfo; /* Cached info about each attr */
62
  StringInfoData buf;     /* output buffer (*not* in tmpcontext) */
63
  MemoryContext tmpcontext; /* Memory context for per-row workspace */
64
} DR_printtup;
65
66
/* ----------------
67
 *    Initialize: create a DestReceiver for printtup
68
 * ----------------
69
 */
70
DestReceiver *
71
printtup_create_DR(CommandDest dest)
72
0
{
73
0
  DR_printtup *self = (DR_printtup *) palloc0(sizeof(DR_printtup));
74
75
0
  self->pub.receiveSlot = printtup; /* might get changed later */
76
0
  self->pub.rStartup = printtup_startup;
77
0
  self->pub.rShutdown = printtup_shutdown;
78
0
  self->pub.rDestroy = printtup_destroy;
79
0
  self->pub.mydest = dest;
80
81
  /*
82
   * Send T message automatically if DestRemote, but not if
83
   * DestRemoteExecute
84
   */
85
0
  self->sendDescrip = (dest == DestRemote);
86
87
0
  self->attrinfo = NULL;
88
0
  self->nattrs = 0;
89
0
  self->myinfo = NULL;
90
0
  self->buf.data = NULL;
91
0
  self->tmpcontext = NULL;
92
93
0
  return (DestReceiver *) self;
94
0
}
95
96
/*
97
 * Set parameters for a DestRemote (or DestRemoteExecute) receiver
98
 */
99
void
100
SetRemoteDestReceiverParams(DestReceiver *self, Portal portal)
101
0
{
102
0
  DR_printtup *myState = (DR_printtup *) self;
103
104
0
  Assert(myState->pub.mydest == DestRemote ||
105
0
       myState->pub.mydest == DestRemoteExecute);
106
107
0
  myState->portal = portal;
108
0
}
109
110
static void
111
printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
112
0
{
113
0
  DR_printtup *myState = (DR_printtup *) self;
114
0
  Portal    portal = myState->portal;
115
116
  /*
117
   * Create I/O buffer to be used for all messages.  This cannot be inside
118
   * tmpcontext, since we want to re-use it across rows.
119
   */
120
0
  initStringInfo(&myState->buf);
121
122
  /*
123
   * Create a temporary memory context that we can reset once per row to
124
   * recover palloc'd memory.  This avoids any problems with leaks inside
125
   * datatype output routines, and should be faster than retail pfree's
126
   * anyway.
127
   */
128
0
  myState->tmpcontext = AllocSetContextCreate(CurrentMemoryContext,
129
0
                        "printtup",
130
0
                        ALLOCSET_DEFAULT_SIZES);
131
132
  /*
133
   * If we are supposed to emit row descriptions, then send the tuple
134
   * descriptor of the tuples.
135
   */
136
0
  if (myState->sendDescrip)
137
0
    SendRowDescriptionMessage(&myState->buf,
138
0
                  typeinfo,
139
0
                  FetchPortalTargetList(portal),
140
0
                  portal->formats);
141
142
  /* ----------------
143
   * We could set up the derived attr info at this time, but we postpone it
144
   * until the first call of printtup, for 2 reasons:
145
   * 1. We don't waste time (compared to the old way) if there are no
146
   *    tuples at all to output.
147
   * 2. Checking in printtup allows us to handle the case that the tuples
148
   *    change type midway through (although this probably can't happen in
149
   *    the current executor).
150
   * ----------------
151
   */
152
0
}
153
154
/*
155
 * SendRowDescriptionMessage --- send a RowDescription message to the frontend
156
 *
157
 * Notes: the TupleDesc has typically been manufactured by ExecTypeFromTL()
158
 * or some similar function; it does not contain a full set of fields.
159
 * The targetlist will be NIL when executing a utility function that does
160
 * not have a plan.  If the targetlist isn't NIL then it is a Query node's
161
 * targetlist; it is up to us to ignore resjunk columns in it.  The formats[]
162
 * array pointer might be NULL (if we are doing Describe on a prepared stmt);
163
 * send zeroes for the format codes in that case.
164
 */
165
void
166
SendRowDescriptionMessage(StringInfo buf, TupleDesc typeinfo,
167
              List *targetlist, int16 *formats)
168
0
{
169
0
  int     natts = typeinfo->natts;
170
0
  int     i;
171
0
  ListCell   *tlist_item = list_head(targetlist);
172
173
  /* tuple descriptor message type */
174
0
  pq_beginmessage_reuse(buf, PqMsg_RowDescription);
175
  /* # of attrs in tuples */
176
0
  pq_sendint16(buf, natts);
177
178
  /*
179
   * Preallocate memory for the entire message to be sent. That allows to
180
   * use the significantly faster inline pqformat.h functions and to avoid
181
   * reallocations.
182
   *
183
   * Have to overestimate the size of the column-names, to account for
184
   * character set overhead.
185
   */
186
0
  enlargeStringInfo(buf, (NAMEDATALEN * MAX_CONVERSION_GROWTH /* attname */
187
0
              + sizeof(Oid) /* resorigtbl */
188
0
              + sizeof(AttrNumber)  /* resorigcol */
189
0
              + sizeof(Oid) /* atttypid */
190
0
              + sizeof(int16) /* attlen */
191
0
              + sizeof(int32) /* attypmod */
192
0
              + sizeof(int16) /* format */
193
0
              ) * natts);
194
195
0
  for (i = 0; i < natts; ++i)
196
0
  {
197
0
    Form_pg_attribute att = TupleDescAttr(typeinfo, i);
198
0
    Oid     atttypid = att->atttypid;
199
0
    int32   atttypmod = att->atttypmod;
200
0
    Oid     resorigtbl;
201
0
    AttrNumber  resorigcol;
202
0
    int16   format;
203
204
    /*
205
     * If column is a domain, send the base type and typmod instead.
206
     * Lookup before sending any ints, for efficiency.
207
     */
208
0
    atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod);
209
210
    /* Do we have a non-resjunk tlist item? */
211
0
    while (tlist_item &&
212
0
         ((TargetEntry *) lfirst(tlist_item))->resjunk)
213
0
      tlist_item = lnext(targetlist, tlist_item);
214
0
    if (tlist_item)
215
0
    {
216
0
      TargetEntry *tle = (TargetEntry *) lfirst(tlist_item);
217
218
0
      resorigtbl = tle->resorigtbl;
219
0
      resorigcol = tle->resorigcol;
220
0
      tlist_item = lnext(targetlist, tlist_item);
221
0
    }
222
0
    else
223
0
    {
224
      /* No info available, so send zeroes */
225
0
      resorigtbl = 0;
226
0
      resorigcol = 0;
227
0
    }
228
229
0
    if (formats)
230
0
      format = formats[i];
231
0
    else
232
0
      format = 0;
233
234
0
    pq_writestring(buf, NameStr(att->attname));
235
0
    pq_writeint32(buf, resorigtbl);
236
0
    pq_writeint16(buf, resorigcol);
237
0
    pq_writeint32(buf, atttypid);
238
0
    pq_writeint16(buf, att->attlen);
239
0
    pq_writeint32(buf, atttypmod);
240
0
    pq_writeint16(buf, format);
241
0
  }
242
243
0
  pq_endmessage_reuse(buf);
244
0
}
245
246
/*
247
 * Get the lookup info that printtup() needs
248
 */
249
static void
250
printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
251
0
{
252
0
  int16    *formats = myState->portal->formats;
253
0
  int     i;
254
255
  /* get rid of any old data */
256
0
  if (myState->myinfo)
257
0
    pfree(myState->myinfo);
258
0
  myState->myinfo = NULL;
259
260
0
  myState->attrinfo = typeinfo;
261
0
  myState->nattrs = numAttrs;
262
0
  if (numAttrs <= 0)
263
0
    return;
264
265
0
  myState->myinfo = (PrinttupAttrInfo *)
266
0
    palloc0(numAttrs * sizeof(PrinttupAttrInfo));
267
268
0
  for (i = 0; i < numAttrs; i++)
269
0
  {
270
0
    PrinttupAttrInfo *thisState = myState->myinfo + i;
271
0
    int16   format = (formats ? formats[i] : 0);
272
0
    Form_pg_attribute attr = TupleDescAttr(typeinfo, i);
273
274
0
    thisState->format = format;
275
0
    if (format == 0)
276
0
    {
277
0
      getTypeOutputInfo(attr->atttypid,
278
0
                &thisState->typoutput,
279
0
                &thisState->typisvarlena);
280
0
      fmgr_info(thisState->typoutput, &thisState->finfo);
281
0
    }
282
0
    else if (format == 1)
283
0
    {
284
0
      getTypeBinaryOutputInfo(attr->atttypid,
285
0
                  &thisState->typsend,
286
0
                  &thisState->typisvarlena);
287
0
      fmgr_info(thisState->typsend, &thisState->finfo);
288
0
    }
289
0
    else
290
0
      ereport(ERROR,
291
0
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
292
0
           errmsg("unsupported format code: %d", format)));
293
0
  }
294
0
}
295
296
/* ----------------
297
 *    printtup --- send a tuple to the client
298
 *
299
 * Note: if you change this function, see also serializeAnalyzeReceive
300
 * in explain.c, which is meant to replicate the computations done here.
301
 * ----------------
302
 */
303
static bool
304
printtup(TupleTableSlot *slot, DestReceiver *self)
305
0
{
306
0
  TupleDesc typeinfo = slot->tts_tupleDescriptor;
307
0
  DR_printtup *myState = (DR_printtup *) self;
308
0
  MemoryContext oldcontext;
309
0
  StringInfo  buf = &myState->buf;
310
0
  int     natts = typeinfo->natts;
311
0
  int     i;
312
313
  /* Set or update my derived attribute info, if needed */
314
0
  if (myState->attrinfo != typeinfo || myState->nattrs != natts)
315
0
    printtup_prepare_info(myState, typeinfo, natts);
316
317
  /* Make sure the tuple is fully deconstructed */
318
0
  slot_getallattrs(slot);
319
320
  /* Switch into per-row context so we can recover memory below */
321
0
  oldcontext = MemoryContextSwitchTo(myState->tmpcontext);
322
323
  /*
324
   * Prepare a DataRow message (note buffer is in per-query context)
325
   */
326
0
  pq_beginmessage_reuse(buf, PqMsg_DataRow);
327
328
0
  pq_sendint16(buf, natts);
329
330
  /*
331
   * send the attributes of this tuple
332
   */
333
0
  for (i = 0; i < natts; ++i)
334
0
  {
335
0
    PrinttupAttrInfo *thisState = myState->myinfo + i;
336
0
    Datum   attr = slot->tts_values[i];
337
338
0
    if (slot->tts_isnull[i])
339
0
    {
340
0
      pq_sendint32(buf, -1);
341
0
      continue;
342
0
    }
343
344
    /*
345
     * Here we catch undefined bytes in datums that are returned to the
346
     * client without hitting disk; see comments at the related check in
347
     * PageAddItem().  This test is most useful for uncompressed,
348
     * non-external datums, but we're quite likely to see such here when
349
     * testing new C functions.
350
     */
351
0
    if (thisState->typisvarlena)
352
0
      VALGRIND_CHECK_MEM_IS_DEFINED(DatumGetPointer(attr),
353
0
                      VARSIZE_ANY(attr));
354
355
0
    if (thisState->format == 0)
356
0
    {
357
      /* Text output */
358
0
      char     *outputstr;
359
360
0
      outputstr = OutputFunctionCall(&thisState->finfo, attr);
361
0
      pq_sendcountedtext(buf, outputstr, strlen(outputstr));
362
0
    }
363
0
    else
364
0
    {
365
      /* Binary output */
366
0
      bytea    *outputbytes;
367
368
0
      outputbytes = SendFunctionCall(&thisState->finfo, attr);
369
0
      pq_sendint32(buf, VARSIZE(outputbytes) - VARHDRSZ);
370
0
      pq_sendbytes(buf, VARDATA(outputbytes),
371
0
             VARSIZE(outputbytes) - VARHDRSZ);
372
0
    }
373
0
  }
374
375
0
  pq_endmessage_reuse(buf);
376
377
  /* Return to caller's context, and flush row's temporary memory */
378
0
  MemoryContextSwitchTo(oldcontext);
379
0
  MemoryContextReset(myState->tmpcontext);
380
381
0
  return true;
382
0
}
383
384
/* ----------------
385
 *    printtup_shutdown
386
 * ----------------
387
 */
388
static void
389
printtup_shutdown(DestReceiver *self)
390
0
{
391
0
  DR_printtup *myState = (DR_printtup *) self;
392
393
0
  if (myState->myinfo)
394
0
    pfree(myState->myinfo);
395
0
  myState->myinfo = NULL;
396
397
0
  myState->attrinfo = NULL;
398
399
0
  if (myState->buf.data)
400
0
    pfree(myState->buf.data);
401
0
  myState->buf.data = NULL;
402
403
0
  if (myState->tmpcontext)
404
0
    MemoryContextDelete(myState->tmpcontext);
405
0
  myState->tmpcontext = NULL;
406
0
}
407
408
/* ----------------
409
 *    printtup_destroy
410
 * ----------------
411
 */
412
static void
413
printtup_destroy(DestReceiver *self)
414
0
{
415
0
  pfree(self);
416
0
}
417
418
/* ----------------
419
 *    printatt
420
 * ----------------
421
 */
422
static void
423
printatt(unsigned attributeId,
424
     Form_pg_attribute attributeP,
425
     char *value)
426
0
{
427
0
  printf("\t%2d: %s%s%s%s\t(typeid = %u, len = %d, typmod = %d, byval = %c)\n",
428
0
       attributeId,
429
0
       NameStr(attributeP->attname),
430
0
       value != NULL ? " = \"" : "",
431
0
       value != NULL ? value : "",
432
0
       value != NULL ? "\"" : "",
433
0
       (unsigned int) (attributeP->atttypid),
434
0
       attributeP->attlen,
435
0
       attributeP->atttypmod,
436
0
       attributeP->attbyval ? 't' : 'f');
437
0
}
438
439
/* ----------------
440
 *    debugStartup - prepare to print tuples for an interactive backend
441
 * ----------------
442
 */
443
void
444
debugStartup(DestReceiver *self, int operation, TupleDesc typeinfo)
445
0
{
446
0
  int     natts = typeinfo->natts;
447
0
  int     i;
448
449
  /*
450
   * show the return type of the tuples
451
   */
452
0
  for (i = 0; i < natts; ++i)
453
0
    printatt((unsigned) i + 1, TupleDescAttr(typeinfo, i), NULL);
454
0
  printf("\t----\n");
455
0
}
456
457
/* ----------------
458
 *    debugtup - print one tuple for an interactive backend
459
 * ----------------
460
 */
461
bool
462
debugtup(TupleTableSlot *slot, DestReceiver *self)
463
0
{
464
0
  TupleDesc typeinfo = slot->tts_tupleDescriptor;
465
0
  int     natts = typeinfo->natts;
466
0
  int     i;
467
0
  Datum   attr;
468
0
  char     *value;
469
0
  bool    isnull;
470
0
  Oid     typoutput;
471
0
  bool    typisvarlena;
472
473
0
  for (i = 0; i < natts; ++i)
474
0
  {
475
0
    attr = slot_getattr(slot, i + 1, &isnull);
476
0
    if (isnull)
477
0
      continue;
478
0
    getTypeOutputInfo(TupleDescAttr(typeinfo, i)->atttypid,
479
0
              &typoutput, &typisvarlena);
480
481
0
    value = OidOutputFunctionCall(typoutput, attr);
482
483
0
    printatt((unsigned) i + 1, TupleDescAttr(typeinfo, i), value);
484
0
  }
485
0
  printf("\t----\n");
486
487
0
  return true;
488
0
}