Age Owner Branch data TLA Line data Source code
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 *
6124 tgl@sss.pgh.pa.us 71 :CBC 308924 : printtup_create_DR(CommandDest dest)
72 : : {
73 : 308924 : DR_printtup *self = (DR_printtup *) palloc0(sizeof(DR_printtup));
74 : :
5931 bruce@momjian.us 75 : 308924 : self->pub.receiveSlot = printtup; /* might get changed later */
8067 tgl@sss.pgh.pa.us 76 : 308924 : self->pub.rStartup = printtup_startup;
77 : 308924 : self->pub.rShutdown = printtup_shutdown;
78 : 308924 : self->pub.rDestroy = printtup_destroy;
8159 79 : 308924 : self->pub.mydest = dest;
80 : :
81 : : /*
82 : : * Send T message automatically if DestRemote, but not if
83 : : * DestRemoteExecute
84 : : */
7247 alvherre@alvh.no-ip. 85 : 308924 : self->sendDescrip = (dest == DestRemote);
86 : :
9719 tgl@sss.pgh.pa.us 87 : 308924 : self->attrinfo = NULL;
88 : 308924 : self->nattrs = 0;
89 : 308924 : self->myinfo = NULL;
2364 90 : 308924 : self->buf.data = NULL;
4325 91 : 308924 : self->tmpcontext = NULL;
92 : :
9601 bruce@momjian.us 93 : 308924 : return (DestReceiver *) self;
94 : : }
95 : :
96 : : /*
97 : : * Set parameters for a DestRemote (or DestRemoteExecute) receiver
98 : : */
99 : : void
6124 tgl@sss.pgh.pa.us 100 : 308924 : SetRemoteDestReceiverParams(DestReceiver *self, Portal portal)
101 : : {
102 : 308924 : DR_printtup *myState = (DR_printtup *) self;
103 : :
104 [ + + - + ]: 308924 : Assert(myState->pub.mydest == DestRemote ||
105 : : myState->pub.mydest == DestRemoteExecute);
106 : :
107 : 308924 : myState->portal = portal;
108 : 308924 : }
109 : :
110 : : static void
8157 111 : 152827 : printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
112 : : {
8160 113 : 152827 : DR_printtup *myState = (DR_printtup *) self;
8069 bruce@momjian.us 114 : 152827 : 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 : : */
2887 andres@anarazel.de 120 : 152827 : 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 : : */
4325 tgl@sss.pgh.pa.us 128 : 152827 : myState->tmpcontext = AllocSetContextCreate(CurrentMemoryContext,
129 : : "printtup",
130 : : ALLOCSET_DEFAULT_SIZES);
131 : :
132 : : /*
133 : : * If we are supposed to emit row descriptions, then send the tuple
134 : : * descriptor of the tuples.
135 : : */
6965 136 [ + + ]: 152827 : if (myState->sendDescrip)
2887 andres@anarazel.de 137 : 148162 : SendRowDescriptionMessage(&myState->buf,
138 : : typeinfo,
139 : : FetchPortalTargetList(portal),
140 : : 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 : : */
9719 tgl@sss.pgh.pa.us 152 : 152827 : }
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
2887 andres@anarazel.de 166 : 152873 : SendRowDescriptionMessage(StringInfo buf, TupleDesc typeinfo,
167 : : List *targetlist, int16 *formats)
168 : : {
8160 tgl@sss.pgh.pa.us 169 : 152873 : int natts = typeinfo->natts;
170 : : int i;
1647 heikki.linnakangas@i 171 : 152873 : ListCell *tlist_item = list_head(targetlist);
172 : :
173 : : /* tuple descriptor message type */
416 nathan@postgresql.or 174 : 152873 : pq_beginmessage_reuse(buf, PqMsg_RowDescription);
175 : : /* # of attrs in tuples */
2887 andres@anarazel.de 176 : 152873 : 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 : 152873 : enlargeStringInfo(buf, (NAMEDATALEN * MAX_CONVERSION_GROWTH /* attname */
187 : : + sizeof(Oid) /* resorigtbl */
188 : : + sizeof(AttrNumber) /* resorigcol */
189 : : + sizeof(Oid) /* atttypid */
190 : : + sizeof(int16) /* attlen */
191 : : + sizeof(int32) /* attypmod */
192 : : + sizeof(int16) /* format */
193 : : ) * natts);
194 : :
8160 tgl@sss.pgh.pa.us 195 [ + + ]: 656386 : for (i = 0; i < natts; ++i)
196 : : {
2939 andres@anarazel.de 197 : 503513 : Form_pg_attribute att = TupleDescAttr(typeinfo, i);
198 : 503513 : Oid atttypid = att->atttypid;
199 : 503513 : int32 atttypmod = att->atttypmod;
200 : : Oid resorigtbl;
201 : : AttrNumber resorigcol;
202 : : 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 : : */
2887 208 : 503513 : atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod);
209 : :
210 : : /* Do we have a non-resjunk tlist item? */
211 [ + + ]: 503513 : while (tlist_item &&
212 [ - + ]: 495085 : ((TargetEntry *) lfirst(tlist_item))->resjunk)
2245 tgl@sss.pgh.pa.us 213 :UBC 0 : tlist_item = lnext(targetlist, tlist_item);
2887 andres@anarazel.de 214 [ + + ]:CBC 503513 : if (tlist_item)
215 : : {
216 : 495085 : TargetEntry *tle = (TargetEntry *) lfirst(tlist_item);
217 : :
218 : 495085 : resorigtbl = tle->resorigtbl;
219 : 495085 : resorigcol = tle->resorigcol;
2245 tgl@sss.pgh.pa.us 220 : 495085 : tlist_item = lnext(targetlist, tlist_item);
221 : : }
222 : : else
223 : : {
224 : : /* No info available, so send zeroes */
2887 andres@anarazel.de 225 : 8428 : resorigtbl = 0;
226 : 8428 : resorigcol = 0;
227 : : }
228 : :
229 [ + + ]: 503513 : if (formats)
230 : 503342 : format = formats[i];
231 : : else
232 : 171 : format = 0;
233 : :
234 : 503513 : pq_writestring(buf, NameStr(att->attname));
235 : 503513 : pq_writeint32(buf, resorigtbl);
236 : 503513 : pq_writeint16(buf, resorigcol);
237 : 503513 : pq_writeint32(buf, atttypid);
238 : 503513 : pq_writeint16(buf, att->attlen);
239 : 503513 : pq_writeint32(buf, atttypmod);
240 : 503513 : pq_writeint16(buf, format);
241 : : }
242 : :
1647 heikki.linnakangas@i 243 : 152873 : pq_endmessage_reuse(buf);
8160 tgl@sss.pgh.pa.us 244 : 152873 : }
245 : :
246 : : /*
247 : : * Get the lookup info that printtup() needs
248 : : */
249 : : static void
9601 bruce@momjian.us 250 : 126484 : printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
251 : : {
8156 tgl@sss.pgh.pa.us 252 : 126484 : int16 *formats = myState->portal->formats;
253 : : int i;
254 : :
255 : : /* get rid of any old data */
9719 256 [ + + ]: 126484 : if (myState->myinfo)
7764 257 : 812 : pfree(myState->myinfo);
9719 258 : 126484 : myState->myinfo = NULL;
259 : :
260 : 126484 : myState->attrinfo = typeinfo;
261 : 126484 : myState->nattrs = numAttrs;
262 [ + + ]: 126484 : if (numAttrs <= 0)
263 : 125 : return;
264 : :
9601 bruce@momjian.us 265 : 126359 : myState->myinfo = (PrinttupAttrInfo *)
8156 tgl@sss.pgh.pa.us 266 : 126359 : palloc0(numAttrs * sizeof(PrinttupAttrInfo));
267 : :
9719 268 [ + + ]: 523103 : for (i = 0; i < numAttrs; i++)
269 : : {
9601 bruce@momjian.us 270 : 396744 : PrinttupAttrInfo *thisState = myState->myinfo + i;
8156 tgl@sss.pgh.pa.us 271 [ + + ]: 396744 : int16 format = (formats ? formats[i] : 0);
2939 andres@anarazel.de 272 : 396744 : Form_pg_attribute attr = TupleDescAttr(typeinfo, i);
273 : :
8156 tgl@sss.pgh.pa.us 274 : 396744 : thisState->format = format;
275 [ + + ]: 396744 : if (format == 0)
276 : : {
2939 andres@anarazel.de 277 : 396705 : getTypeOutputInfo(attr->atttypid,
278 : : &thisState->typoutput,
279 : : &thisState->typisvarlena);
9719 tgl@sss.pgh.pa.us 280 : 396705 : fmgr_info(thisState->typoutput, &thisState->finfo);
281 : : }
8156 282 [ + - ]: 39 : else if (format == 1)
283 : : {
2939 andres@anarazel.de 284 : 39 : getTypeBinaryOutputInfo(attr->atttypid,
285 : : &thisState->typsend,
286 : : &thisState->typisvarlena);
8156 tgl@sss.pgh.pa.us 287 : 39 : fmgr_info(thisState->typsend, &thisState->finfo);
288 : : }
289 : : else
8083 tgl@sss.pgh.pa.us 290 [ # # ]:UBC 0 : ereport(ERROR,
291 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
292 : : errmsg("unsupported format code: %d", format)));
293 : : }
294 : : }
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
7479 tgl@sss.pgh.pa.us 304 :CBC 3691945 : printtup(TupleTableSlot *slot, DestReceiver *self)
305 : : {
7266 bruce@momjian.us 306 : 3691945 : TupleDesc typeinfo = slot->tts_tupleDescriptor;
8157 tgl@sss.pgh.pa.us 307 : 3691945 : DR_printtup *myState = (DR_printtup *) self;
308 : : MemoryContext oldcontext;
2887 andres@anarazel.de 309 : 3691945 : StringInfo buf = &myState->buf;
8139 tgl@sss.pgh.pa.us 310 : 3691945 : int natts = typeinfo->natts;
311 : : int i;
312 : :
313 : : /* Set or update my derived attribute info, if needed */
8157 314 [ + + - + ]: 3691945 : if (myState->attrinfo != typeinfo || myState->nattrs != natts)
315 : 126484 : printtup_prepare_info(myState, typeinfo, natts);
316 : :
317 : : /* Make sure the tuple is fully deconstructed */
7479 318 : 3691945 : slot_getallattrs(slot);
319 : :
320 : : /* Switch into per-row context so we can recover memory below */
4325 321 : 3691945 : oldcontext = MemoryContextSwitchTo(myState->tmpcontext);
322 : :
323 : : /*
324 : : * Prepare a DataRow message (note buffer is in per-query context)
325 : : */
416 nathan@postgresql.or 326 : 3691945 : pq_beginmessage_reuse(buf, PqMsg_DataRow);
327 : :
2887 andres@anarazel.de 328 : 3691945 : pq_sendint16(buf, natts);
329 : :
330 : : /*
331 : : * send the attributes of this tuple
332 : : */
8157 tgl@sss.pgh.pa.us 333 [ + + ]: 21865035 : for (i = 0; i < natts; ++i)
334 : : {
335 : 18173090 : PrinttupAttrInfo *thisState = myState->myinfo + i;
4325 336 : 18173090 : Datum attr = slot->tts_values[i];
337 : :
7479 338 [ + + ]: 18173090 : if (slot->tts_isnull[i])
339 : : {
2887 andres@anarazel.de 340 : 1003314 : pq_sendint32(buf, -1);
8157 tgl@sss.pgh.pa.us 341 : 1003314 : continue;
342 : : }
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 : : */
8156 351 : 17169776 : if (thisState->typisvarlena)
352 : : VALGRIND_CHECK_MEM_IS_DEFINED(DatumGetPointer(attr),
353 : : VARSIZE_ANY(DatumGetPointer(attr)));
354 : :
355 [ + + ]: 17169776 : if (thisState->format == 0)
356 : : {
357 : : /* Text output */
358 : : char *outputstr;
359 : :
7095 360 : 17162468 : outputstr = OutputFunctionCall(&thisState->finfo, attr);
551 heikki.linnakangas@i 361 : 17162468 : pq_sendcountedtext(buf, outputstr, strlen(outputstr));
362 : : }
363 : : else
364 : : {
365 : : /* Binary output */
366 : : bytea *outputbytes;
367 : :
7095 tgl@sss.pgh.pa.us 368 : 7308 : outputbytes = SendFunctionCall(&thisState->finfo, attr);
2887 andres@anarazel.de 369 : 7308 : pq_sendint32(buf, VARSIZE(outputbytes) - VARHDRSZ);
370 : 7308 : pq_sendbytes(buf, VARDATA(outputbytes),
8156 tgl@sss.pgh.pa.us 371 : 7308 : VARSIZE(outputbytes) - VARHDRSZ);
372 : : }
373 : : }
374 : :
2887 andres@anarazel.de 375 : 3691945 : pq_endmessage_reuse(buf);
376 : :
377 : : /* Return to caller's context, and flush row's temporary memory */
4325 tgl@sss.pgh.pa.us 378 : 3691945 : MemoryContextSwitchTo(oldcontext);
379 : 3691945 : MemoryContextReset(myState->tmpcontext);
380 : :
3379 rhaas@postgresql.org 381 : 3691945 : return true;
382 : : }
383 : :
384 : : /* ----------------
385 : : * printtup_shutdown
386 : : * ----------------
387 : : */
388 : : static void
8159 tgl@sss.pgh.pa.us 389 : 149240 : printtup_shutdown(DestReceiver *self)
390 : : {
9601 bruce@momjian.us 391 : 149240 : DR_printtup *myState = (DR_printtup *) self;
392 : :
9719 tgl@sss.pgh.pa.us 393 [ + + ]: 149240 : if (myState->myinfo)
394 : 125449 : pfree(myState->myinfo);
8159 395 : 149240 : myState->myinfo = NULL;
396 : :
397 : 149240 : myState->attrinfo = NULL;
398 : :
2364 399 [ + - ]: 149240 : if (myState->buf.data)
400 : 149240 : pfree(myState->buf.data);
401 : 149240 : myState->buf.data = NULL;
402 : :
4325 403 [ + - ]: 149240 : if (myState->tmpcontext)
404 : 149240 : MemoryContextDelete(myState->tmpcontext);
405 : 149240 : myState->tmpcontext = NULL;
8159 406 : 149240 : }
407 : :
408 : : /* ----------------
409 : : * printtup_destroy
410 : : * ----------------
411 : : */
412 : : static void
413 : 294862 : printtup_destroy(DestReceiver *self)
414 : : {
415 : 294862 : pfree(self);
9719 416 : 294862 : }
417 : :
418 : : /* ----------------
419 : : * printatt
420 : : * ----------------
421 : : */
422 : : static void
10651 scrappy@hub.org 423 : 214 : printatt(unsigned attributeId,
424 : : Form_pg_attribute attributeP,
425 : : char *value)
426 : : {
9977 bruce@momjian.us 427 [ + + + + : 214 : printf("\t%2d: %s%s%s%s\t(typeid = %u, len = %d, typmod = %d, byval = %c)\n",
+ + + + ]
428 : : attributeId,
429 : : NameStr(attributeP->attname),
430 : : value != NULL ? " = \"" : "",
431 : : value != NULL ? value : "",
432 : : value != NULL ? "\"" : "",
433 : : (unsigned int) (attributeP->atttypid),
434 : : attributeP->attlen,
435 : : attributeP->atttypmod,
436 : : attributeP->attbyval ? 't' : 'f');
10651 scrappy@hub.org 437 : 214 : }
438 : :
439 : : /* ----------------
440 : : * debugStartup - prepare to print tuples for an interactive backend
441 : : * ----------------
442 : : */
443 : : void
8157 tgl@sss.pgh.pa.us 444 : 106 : debugStartup(DestReceiver *self, int operation, TupleDesc typeinfo)
445 : : {
446 : 106 : int natts = typeinfo->natts;
447 : : int i;
448 : :
449 : : /*
450 : : * show the return type of the tuples
451 : : */
452 [ + + ]: 212 : for (i = 0; i < natts; ++i)
2939 andres@anarazel.de 453 : 106 : printatt((unsigned) i + 1, TupleDescAttr(typeinfo, i), NULL);
8157 tgl@sss.pgh.pa.us 454 : 106 : printf("\t----\n");
8592 455 : 106 : }
456 : :
457 : : /* ----------------
458 : : * debugtup - print one tuple for an interactive backend
459 : : * ----------------
460 : : */
461 : : bool
7479 462 : 108 : debugtup(TupleTableSlot *slot, DestReceiver *self)
463 : : {
464 : 108 : TupleDesc typeinfo = slot->tts_tupleDescriptor;
8139 465 : 108 : int natts = typeinfo->natts;
466 : : int i;
467 : : Datum attr;
468 : : char *value;
469 : : bool isnull;
470 : : Oid typoutput;
471 : : bool typisvarlena;
472 : :
9045 473 [ + + ]: 216 : for (i = 0; i < natts; ++i)
474 : : {
4325 475 : 108 : attr = slot_getattr(slot, i + 1, &isnull);
9722 476 [ - + ]: 108 : if (isnull)
9722 tgl@sss.pgh.pa.us 477 :UBC 0 : continue;
2939 andres@anarazel.de 478 :CBC 108 : getTypeOutputInfo(TupleDescAttr(typeinfo, i)->atttypid,
479 : : &typoutput, &typisvarlena);
480 : :
7095 tgl@sss.pgh.pa.us 481 : 108 : value = OidOutputFunctionCall(typoutput, attr);
482 : :
2939 andres@anarazel.de 483 : 108 : printatt((unsigned) i + 1, TupleDescAttr(typeinfo, i), value);
484 : : }
10226 bruce@momjian.us 485 : 108 : printf("\t----\n");
486 : :
3379 rhaas@postgresql.org 487 : 108 : return true;
488 : : }
|