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-2020, 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 static void SendRowDescriptionCols_2(StringInfo buf, TupleDesc typeinfo,
36 									 List *targetlist, int16 *formats);
37 static 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  */
52 typedef 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 
61 typedef 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  */
77 DestReceiver *
printtup_create_DR(CommandDest dest)78 printtup_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  */
106 void
SetRemoteDestReceiverParams(DestReceiver * self,Portal portal)107 SetRemoteDestReceiverParams(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 
130 static void
printtup_startup(DestReceiver * self,int operation,TupleDesc typeinfo)131 printtup_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  */
200 void
SendRowDescriptionMessage(StringInfo buf,TupleDesc typeinfo,List * targetlist,int16 * formats)201 SendRowDescriptionMessage(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  */
223 static void
SendRowDescriptionCols_3(StringInfo buf,TupleDesc typeinfo,List * targetlist,int16 * formats)224 SendRowDescriptionCols_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(targetlist, 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(targetlist, 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  */
299 static void
SendRowDescriptionCols_2(StringInfo buf,TupleDesc typeinfo,List * targetlist,int16 * formats)300 SendRowDescriptionCols_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  */
326 static void
printtup_prepare_info(DR_printtup * myState,TupleDesc typeinfo,int numAttrs)327 printtup_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  */
377 static bool
printtup(TupleTableSlot * slot,DestReceiver * self)378 printtup(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  */
462 static bool
printtup_20(TupleTableSlot * slot,DestReceiver * self)463 printtup_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  */
540 static void
printtup_shutdown(DestReceiver * self)541 printtup_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  */
564 static void
printtup_destroy(DestReceiver * self)565 printtup_destroy(DestReceiver *self)
566 {
567 	pfree(self);
568 }
569 
570 /* ----------------
571  *		printatt
572  * ----------------
573  */
574 static void
printatt(unsigned attributeId,Form_pg_attribute attributeP,char * value)575 printatt(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  */
595 void
debugStartup(DestReceiver * self,int operation,TupleDesc typeinfo)596 debugStartup(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  */
613 bool
debugtup(TupleTableSlot * slot,DestReceiver * self)614 debugtup(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  */
651 static bool
printtup_internal_20(TupleTableSlot * slot,DestReceiver * self)652 printtup_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