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-2019, 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/libpq.h"
20#include "libpq/pqformat.h"
21#include "tcop/pquery.h"
22#include "utils/lsyscache.h"
23#include "utils/memdebug.h"
24#include "utils/memutils.h"
25
26
27static void printtup_startup(DestReceiver *self, int operation,
28 TupleDesc typeinfo);
29static bool printtup(TupleTableSlot *slot, DestReceiver *self);
30static bool printtup_20(TupleTableSlot *slot, DestReceiver *self);
31static bool printtup_internal_20(TupleTableSlot *slot, DestReceiver *self);
32static void printtup_shutdown(DestReceiver *self);
33static void printtup_destroy(DestReceiver *self);
34
35static void SendRowDescriptionCols_2(StringInfo buf, TupleDesc typeinfo,
36 List *targetlist, int16 *formats);
37static void SendRowDescriptionCols_3(StringInfo buf, TupleDesc typeinfo,
38 List *targetlist, int16 *formats);
39
40/* ----------------------------------------------------------------
41 * printtup / debugtup support
42 * ----------------------------------------------------------------
43 */
44
45/* ----------------
46 * Private state for a printtup destination object
47 *
48 * NOTE: finfo is the lookup info for either typoutput or typsend, whichever
49 * we are using for this column.
50 * ----------------
51 */
52typedef struct
53{ /* Per-attribute information */
54 Oid typoutput; /* Oid for the type's text output fn */
55 Oid typsend; /* Oid for the type's binary output fn */
56 bool typisvarlena; /* is it varlena (ie possibly toastable)? */
57 int16 format; /* format code for this column */
58 FmgrInfo finfo; /* Precomputed call info for output fn */
59} PrinttupAttrInfo;
60
61typedef struct
62{
63 DestReceiver pub; /* publicly-known function pointers */
64 Portal portal; /* the Portal we are printing from */
65 bool sendDescrip; /* send RowDescription at startup? */
66 TupleDesc attrinfo; /* The attr info we are set up for */
67 int nattrs;
68 PrinttupAttrInfo *myinfo; /* Cached info about each attr */
69 StringInfoData buf; /* output buffer (*not* in tmpcontext) */
70 MemoryContext tmpcontext; /* Memory context for per-row workspace */
71} DR_printtup;
72
73/* ----------------
74 * Initialize: create a DestReceiver for printtup
75 * ----------------
76 */
77DestReceiver *
78printtup_create_DR(CommandDest dest)
79{
80 DR_printtup *self = (DR_printtup *) palloc0(sizeof(DR_printtup));
81
82 self->pub.receiveSlot = printtup; /* might get changed later */
83 self->pub.rStartup = printtup_startup;
84 self->pub.rShutdown = printtup_shutdown;
85 self->pub.rDestroy = printtup_destroy;
86 self->pub.mydest = dest;
87
88 /*
89 * Send T message automatically if DestRemote, but not if
90 * DestRemoteExecute
91 */
92 self->sendDescrip = (dest == DestRemote);
93
94 self->attrinfo = NULL;
95 self->nattrs = 0;
96 self->myinfo = NULL;
97 self->buf.data = NULL;
98 self->tmpcontext = NULL;
99
100 return (DestReceiver *) self;
101}
102
103/*
104 * Set parameters for a DestRemote (or DestRemoteExecute) receiver
105 */
106void
107SetRemoteDestReceiverParams(DestReceiver *self, Portal portal)
108{
109 DR_printtup *myState = (DR_printtup *) self;
110
111 Assert(myState->pub.mydest == DestRemote ||
112 myState->pub.mydest == DestRemoteExecute);
113
114 myState->portal = portal;
115
116 if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
117 {
118 /*
119 * In protocol 2.0 the Bind message does not exist, so there is no way
120 * for the columns to have different print formats; it's sufficient to
121 * look at the first one.
122 */
123 if (portal->formats && portal->formats[0] != 0)
124 myState->pub.receiveSlot = printtup_internal_20;
125 else
126 myState->pub.receiveSlot = printtup_20;
127 }
128}
129
130static void
131printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
132{
133 DR_printtup *myState = (DR_printtup *) self;
134 Portal portal = myState->portal;
135
136 /*
137 * Create I/O buffer to be used for all messages. This cannot be inside
138 * tmpcontext, since we want to re-use it across rows.
139 */
140 initStringInfo(&myState->buf);
141
142 /*
143 * Create a temporary memory context that we can reset once per row to
144 * recover palloc'd memory. This avoids any problems with leaks inside
145 * datatype output routines, and should be faster than retail pfree's
146 * anyway.
147 */
148 myState->tmpcontext = AllocSetContextCreate(CurrentMemoryContext,
149 "printtup",
150 ALLOCSET_DEFAULT_SIZES);
151
152 if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
153 {
154 /*
155 * Send portal name to frontend (obsolete cruft, gone in proto 3.0)
156 *
157 * If portal name not specified, use "blank" portal.
158 */
159 const char *portalName = portal->name;
160
161 if (portalName == NULL || portalName[0] == '\0')
162 portalName = "blank";
163
164 pq_puttextmessage('P', portalName);
165 }
166
167 /*
168 * If we are supposed to emit row descriptions, then send the tuple
169 * descriptor of the tuples.
170 */
171 if (myState->sendDescrip)
172 SendRowDescriptionMessage(&myState->buf,
173 typeinfo,
174 FetchPortalTargetList(portal),
175 portal->formats);
176
177 /* ----------------
178 * We could set up the derived attr info at this time, but we postpone it
179 * until the first call of printtup, for 2 reasons:
180 * 1. We don't waste time (compared to the old way) if there are no
181 * tuples at all to output.
182 * 2. Checking in printtup allows us to handle the case that the tuples
183 * change type midway through (although this probably can't happen in
184 * the current executor).
185 * ----------------
186 */
187}
188
189/*
190 * SendRowDescriptionMessage --- send a RowDescription message to the frontend
191 *
192 * Notes: the TupleDesc has typically been manufactured by ExecTypeFromTL()
193 * or some similar function; it does not contain a full set of fields.
194 * The targetlist will be NIL when executing a utility function that does
195 * not have a plan. If the targetlist isn't NIL then it is a Query node's
196 * targetlist; it is up to us to ignore resjunk columns in it. The formats[]
197 * array pointer might be NULL (if we are doing Describe on a prepared stmt);
198 * send zeroes for the format codes in that case.
199 */
200void
201SendRowDescriptionMessage(StringInfo buf, TupleDesc typeinfo,
202 List *targetlist, int16 *formats)
203{
204 int natts = typeinfo->natts;
205 int proto = PG_PROTOCOL_MAJOR(FrontendProtocol);
206
207 /* tuple descriptor message type */
208 pq_beginmessage_reuse(buf, 'T');
209 /* # of attrs in tuples */
210 pq_sendint16(buf, natts);
211
212 if (proto >= 3)
213 SendRowDescriptionCols_3(buf, typeinfo, targetlist, formats);
214 else
215 SendRowDescriptionCols_2(buf, typeinfo, targetlist, formats);
216
217 pq_endmessage_reuse(buf);
218}
219
220/*
221 * Send description for each column when using v3+ protocol
222 */
223static void
224SendRowDescriptionCols_3(StringInfo buf, TupleDesc typeinfo, List *targetlist, int16 *formats)
225{
226 int natts = typeinfo->natts;
227 int i;
228 ListCell *tlist_item = list_head(targetlist);
229
230 /*
231 * Preallocate memory for the entire message to be sent. That allows to
232 * use the significantly faster inline pqformat.h functions and to avoid
233 * reallocations.
234 *
235 * Have to overestimate the size of the column-names, to account for
236 * character set overhead.
237 */
238 enlargeStringInfo(buf, (NAMEDATALEN * MAX_CONVERSION_GROWTH /* attname */
239 + sizeof(Oid) /* resorigtbl */
240 + sizeof(AttrNumber) /* resorigcol */
241 + sizeof(Oid) /* atttypid */
242 + sizeof(int16) /* attlen */
243 + sizeof(int32) /* attypmod */
244 + sizeof(int16) /* format */
245 ) * natts);
246
247 for (i = 0; i < natts; ++i)
248 {
249 Form_pg_attribute att = TupleDescAttr(typeinfo, i);
250 Oid atttypid = att->atttypid;
251 int32 atttypmod = att->atttypmod;
252 Oid resorigtbl;
253 AttrNumber resorigcol;
254 int16 format;
255
256 /*
257 * If column is a domain, send the base type and typmod instead.
258 * Lookup before sending any ints, for efficiency.
259 */
260 atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod);
261
262 /* Do we have a non-resjunk tlist item? */
263 while (tlist_item &&
264 ((TargetEntry *) lfirst(tlist_item))->resjunk)
265 tlist_item = lnext(tlist_item);
266 if (tlist_item)
267 {
268 TargetEntry *tle = (TargetEntry *) lfirst(tlist_item);
269
270 resorigtbl = tle->resorigtbl;
271 resorigcol = tle->resorigcol;
272 tlist_item = lnext(tlist_item);
273 }
274 else
275 {
276 /* No info available, so send zeroes */
277 resorigtbl = 0;
278 resorigcol = 0;
279 }
280
281 if (formats)
282 format = formats[i];
283 else
284 format = 0;
285
286 pq_writestring(buf, NameStr(att->attname));
287 pq_writeint32(buf, resorigtbl);
288 pq_writeint16(buf, resorigcol);
289 pq_writeint32(buf, atttypid);
290 pq_writeint16(buf, att->attlen);
291 pq_writeint32(buf, atttypmod);
292 pq_writeint16(buf, format);
293 }
294}
295
296/*
297 * Send description for each column when using v2 protocol
298 */
299static void
300SendRowDescriptionCols_2(StringInfo buf, TupleDesc typeinfo, List *targetlist, int16 *formats)
301{
302 int natts = typeinfo->natts;
303 int i;
304
305 for (i = 0; i < natts; ++i)
306 {
307 Form_pg_attribute att = TupleDescAttr(typeinfo, i);
308 Oid atttypid = att->atttypid;
309 int32 atttypmod = att->atttypmod;
310
311 /* If column is a domain, send the base type and typmod instead */
312 atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod);
313
314 pq_sendstring(buf, NameStr(att->attname));
315 /* column ID only info appears in protocol 3.0 and up */
316 pq_sendint32(buf, atttypid);
317 pq_sendint16(buf, att->attlen);
318 pq_sendint32(buf, atttypmod);
319 /* format info only appears in protocol 3.0 and up */
320 }
321}
322
323/*
324 * Get the lookup info that printtup() needs
325 */
326static void
327printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
328{
329 int16 *formats = myState->portal->formats;
330 int i;
331
332 /* get rid of any old data */
333 if (myState->myinfo)
334 pfree(myState->myinfo);
335 myState->myinfo = NULL;
336
337 myState->attrinfo = typeinfo;
338 myState->nattrs = numAttrs;
339 if (numAttrs <= 0)
340 return;
341
342 myState->myinfo = (PrinttupAttrInfo *)
343 palloc0(numAttrs * sizeof(PrinttupAttrInfo));
344
345 for (i = 0; i < numAttrs; i++)
346 {
347 PrinttupAttrInfo *thisState = myState->myinfo + i;
348 int16 format = (formats ? formats[i] : 0);
349 Form_pg_attribute attr = TupleDescAttr(typeinfo, i);
350
351 thisState->format = format;
352 if (format == 0)
353 {
354 getTypeOutputInfo(attr->atttypid,
355 &thisState->typoutput,
356 &thisState->typisvarlena);
357 fmgr_info(thisState->typoutput, &thisState->finfo);
358 }
359 else if (format == 1)
360 {
361 getTypeBinaryOutputInfo(attr->atttypid,
362 &thisState->typsend,
363 &thisState->typisvarlena);
364 fmgr_info(thisState->typsend, &thisState->finfo);
365 }
366 else
367 ereport(ERROR,
368 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
369 errmsg("unsupported format code: %d", format)));
370 }
371}
372
373/* ----------------
374 * printtup --- print a tuple in protocol 3.0
375 * ----------------
376 */
377static bool
378printtup(TupleTableSlot *slot, DestReceiver *self)
379{
380 TupleDesc typeinfo = slot->tts_tupleDescriptor;
381 DR_printtup *myState = (DR_printtup *) self;
382 MemoryContext oldcontext;
383 StringInfo buf = &myState->buf;
384 int natts = typeinfo->natts;
385 int i;
386
387 /* Set or update my derived attribute info, if needed */
388 if (myState->attrinfo != typeinfo || myState->nattrs != natts)
389 printtup_prepare_info(myState, typeinfo, natts);
390
391 /* Make sure the tuple is fully deconstructed */
392 slot_getallattrs(slot);
393
394 /* Switch into per-row context so we can recover memory below */
395 oldcontext = MemoryContextSwitchTo(myState->tmpcontext);
396
397 /*
398 * Prepare a DataRow message (note buffer is in per-row context)
399 */
400 pq_beginmessage_reuse(buf, 'D');
401
402 pq_sendint16(buf, natts);
403
404 /*
405 * send the attributes of this tuple
406 */
407 for (i = 0; i < natts; ++i)
408 {
409 PrinttupAttrInfo *thisState = myState->myinfo + i;
410 Datum attr = slot->tts_values[i];
411
412 if (slot->tts_isnull[i])
413 {
414 pq_sendint32(buf, -1);
415 continue;
416 }
417
418 /*
419 * Here we catch undefined bytes in datums that are returned to the
420 * client without hitting disk; see comments at the related check in
421 * PageAddItem(). This test is most useful for uncompressed,
422 * non-external datums, but we're quite likely to see such here when
423 * testing new C functions.
424 */
425 if (thisState->typisvarlena)
426 VALGRIND_CHECK_MEM_IS_DEFINED(DatumGetPointer(attr),
427 VARSIZE_ANY(attr));
428
429 if (thisState->format == 0)
430 {
431 /* Text output */
432 char *outputstr;
433
434 outputstr = OutputFunctionCall(&thisState->finfo, attr);
435 pq_sendcountedtext(buf, outputstr, strlen(outputstr), false);
436 }
437 else
438 {
439 /* Binary output */
440 bytea *outputbytes;
441
442 outputbytes = SendFunctionCall(&thisState->finfo, attr);
443 pq_sendint32(buf, VARSIZE(outputbytes) - VARHDRSZ);
444 pq_sendbytes(buf, VARDATA(outputbytes),
445 VARSIZE(outputbytes) - VARHDRSZ);
446 }
447 }
448
449 pq_endmessage_reuse(buf);
450
451 /* Return to caller's context, and flush row's temporary memory */
452 MemoryContextSwitchTo(oldcontext);
453 MemoryContextReset(myState->tmpcontext);
454
455 return true;
456}
457
458/* ----------------
459 * printtup_20 --- print a tuple in protocol 2.0
460 * ----------------
461 */
462static bool
463printtup_20(TupleTableSlot *slot, DestReceiver *self)
464{
465 TupleDesc typeinfo = slot->tts_tupleDescriptor;
466 DR_printtup *myState = (DR_printtup *) self;
467 MemoryContext oldcontext;
468 StringInfo buf = &myState->buf;
469 int natts = typeinfo->natts;
470 int i,
471 j,
472 k;
473
474 /* Set or update my derived attribute info, if needed */
475 if (myState->attrinfo != typeinfo || myState->nattrs != natts)
476 printtup_prepare_info(myState, typeinfo, natts);
477
478 /* Make sure the tuple is fully deconstructed */
479 slot_getallattrs(slot);
480
481 /* Switch into per-row context so we can recover memory below */
482 oldcontext = MemoryContextSwitchTo(myState->tmpcontext);
483
484 /*
485 * tell the frontend to expect new tuple data (in ASCII style)
486 */
487 pq_beginmessage_reuse(buf, 'D');
488
489 /*
490 * send a bitmap of which attributes are not null
491 */
492 j = 0;
493 k = 1 << 7;
494 for (i = 0; i < natts; ++i)
495 {
496 if (!slot->tts_isnull[i])
497 j |= k; /* set bit if not null */
498 k >>= 1;
499 if (k == 0) /* end of byte? */
500 {
501 pq_sendint8(buf, j);
502 j = 0;
503 k = 1 << 7;
504 }
505 }
506 if (k != (1 << 7)) /* flush last partial byte */
507 pq_sendint8(buf, j);
508
509 /*
510 * send the attributes of this tuple
511 */
512 for (i = 0; i < natts; ++i)
513 {
514 PrinttupAttrInfo *thisState = myState->myinfo + i;
515 Datum attr = slot->tts_values[i];
516 char *outputstr;
517
518 if (slot->tts_isnull[i])
519 continue;
520
521 Assert(thisState->format == 0);
522
523 outputstr = OutputFunctionCall(&thisState->finfo, attr);
524 pq_sendcountedtext(buf, outputstr, strlen(outputstr), true);
525 }
526
527 pq_endmessage_reuse(buf);
528
529 /* Return to caller's context, and flush row's temporary memory */
530 MemoryContextSwitchTo(oldcontext);
531 MemoryContextReset(myState->tmpcontext);
532
533 return true;
534}
535
536/* ----------------
537 * printtup_shutdown
538 * ----------------
539 */
540static void
541printtup_shutdown(DestReceiver *self)
542{
543 DR_printtup *myState = (DR_printtup *) self;
544
545 if (myState->myinfo)
546 pfree(myState->myinfo);
547 myState->myinfo = NULL;
548
549 myState->attrinfo = NULL;
550
551 if (myState->buf.data)
552 pfree(myState->buf.data);
553 myState->buf.data = NULL;
554
555 if (myState->tmpcontext)
556 MemoryContextDelete(myState->tmpcontext);
557 myState->tmpcontext = NULL;
558}
559
560/* ----------------
561 * printtup_destroy
562 * ----------------
563 */
564static void
565printtup_destroy(DestReceiver *self)
566{
567 pfree(self);
568}
569
570/* ----------------
571 * printatt
572 * ----------------
573 */
574static void
575printatt(unsigned attributeId,
576 Form_pg_attribute attributeP,
577 char *value)
578{
579 printf("\t%2d: %s%s%s%s\t(typeid = %u, len = %d, typmod = %d, byval = %c)\n",
580 attributeId,
581 NameStr(attributeP->attname),
582 value != NULL ? " = \"" : "",
583 value != NULL ? value : "",
584 value != NULL ? "\"" : "",
585 (unsigned int) (attributeP->atttypid),
586 attributeP->attlen,
587 attributeP->atttypmod,
588 attributeP->attbyval ? 't' : 'f');
589}
590
591/* ----------------
592 * debugStartup - prepare to print tuples for an interactive backend
593 * ----------------
594 */
595void
596debugStartup(DestReceiver *self, int operation, TupleDesc typeinfo)
597{
598 int natts = typeinfo->natts;
599 int i;
600
601 /*
602 * show the return type of the tuples
603 */
604 for (i = 0; i < natts; ++i)
605 printatt((unsigned) i + 1, TupleDescAttr(typeinfo, i), NULL);
606 printf("\t----\n");
607}
608
609/* ----------------
610 * debugtup - print one tuple for an interactive backend
611 * ----------------
612 */
613bool
614debugtup(TupleTableSlot *slot, DestReceiver *self)
615{
616 TupleDesc typeinfo = slot->tts_tupleDescriptor;
617 int natts = typeinfo->natts;
618 int i;
619 Datum attr;
620 char *value;
621 bool isnull;
622 Oid typoutput;
623 bool typisvarlena;
624
625 for (i = 0; i < natts; ++i)
626 {
627 attr = slot_getattr(slot, i + 1, &isnull);
628 if (isnull)
629 continue;
630 getTypeOutputInfo(TupleDescAttr(typeinfo, i)->atttypid,
631 &typoutput, &typisvarlena);
632
633 value = OidOutputFunctionCall(typoutput, attr);
634
635 printatt((unsigned) i + 1, TupleDescAttr(typeinfo, i), value);
636 }
637 printf("\t----\n");
638
639 return true;
640}
641
642/* ----------------
643 * printtup_internal_20 --- print a binary tuple in protocol 2.0
644 *
645 * We use a different message type, i.e. 'B' instead of 'D' to
646 * indicate a tuple in internal (binary) form.
647 *
648 * This is largely same as printtup_20, except we use binary formatting.
649 * ----------------
650 */
651static bool
652printtup_internal_20(TupleTableSlot *slot, DestReceiver *self)
653{
654 TupleDesc typeinfo = slot->tts_tupleDescriptor;
655 DR_printtup *myState = (DR_printtup *) self;
656 MemoryContext oldcontext;
657 StringInfo buf = &myState->buf;
658 int natts = typeinfo->natts;
659 int i,
660 j,
661 k;
662
663 /* Set or update my derived attribute info, if needed */
664 if (myState->attrinfo != typeinfo || myState->nattrs != natts)
665 printtup_prepare_info(myState, typeinfo, natts);
666
667 /* Make sure the tuple is fully deconstructed */
668 slot_getallattrs(slot);
669
670 /* Switch into per-row context so we can recover memory below */
671 oldcontext = MemoryContextSwitchTo(myState->tmpcontext);
672
673 /*
674 * tell the frontend to expect new tuple data (in binary style)
675 */
676 pq_beginmessage_reuse(buf, 'B');
677
678 /*
679 * send a bitmap of which attributes are not null
680 */
681 j = 0;
682 k = 1 << 7;
683 for (i = 0; i < natts; ++i)
684 {
685 if (!slot->tts_isnull[i])
686 j |= k; /* set bit if not null */
687 k >>= 1;
688 if (k == 0) /* end of byte? */
689 {
690 pq_sendint8(buf, j);
691 j = 0;
692 k = 1 << 7;
693 }
694 }
695 if (k != (1 << 7)) /* flush last partial byte */
696 pq_sendint8(buf, j);
697
698 /*
699 * send the attributes of this tuple
700 */
701 for (i = 0; i < natts; ++i)
702 {
703 PrinttupAttrInfo *thisState = myState->myinfo + i;
704 Datum attr = slot->tts_values[i];
705 bytea *outputbytes;
706
707 if (slot->tts_isnull[i])
708 continue;
709
710 Assert(thisState->format == 1);
711
712 outputbytes = SendFunctionCall(&thisState->finfo, attr);
713 pq_sendint32(buf, VARSIZE(outputbytes) - VARHDRSZ);
714 pq_sendbytes(buf, VARDATA(outputbytes),
715 VARSIZE(outputbytes) - VARHDRSZ);
716 }
717
718 pq_endmessage_reuse(buf);
719
720 /* Return to caller's context, and flush row's temporary memory */
721 MemoryContextSwitchTo(oldcontext);
722 MemoryContextReset(myState->tmpcontext);
723
724 return true;
725}
726