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