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