printtup.c 16.7 KB
Newer Older
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
 *
B
Bruce Momjian 已提交
8
 * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
B
Add:  
Bruce Momjian 已提交
9
 * Portions Copyright (c) 1994, Regents of the University of California
10 11
 *
 * IDENTIFICATION
12
 *	  $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.73 2003/05/13 18:39:50 tgl Exp $
13 14 15
 *
 *-------------------------------------------------------------------------
 */
16 17 18 19
#include "postgres.h"

#include "access/heapam.h"
#include "access/printtup.h"
20
#include "libpq/libpq.h"
21
#include "libpq/pqformat.h"
22
#include "utils/lsyscache.h"
23
#include "utils/portal.h"
24

M
Marc G. Fournier 已提交
25

26
static void printtup_startup(DestReceiver *self, int operation,
27 28 29 30 31 32 33
							 TupleDesc typeinfo);
static void printtup(HeapTuple tuple, TupleDesc typeinfo,
					 DestReceiver *self);
static void printtup_20(HeapTuple tuple, TupleDesc typeinfo,
						DestReceiver *self);
static void printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo,
								 DestReceiver *self);
34 35 36
static void printtup_shutdown(DestReceiver *self);
static void printtup_destroy(DestReceiver *self);

37

38
/* ----------------------------------------------------------------
39
 *		printtup / debugtup support
40 41 42
 * ----------------------------------------------------------------
 */

43 44
/* ----------------
 *		Private state for a printtup destination object
45 46 47
 *
 * NOTE: finfo is the lookup info for either typoutput or typsend, whichever
 * we are using for this column.
48 49
 * ----------------
 */
B
Bruce Momjian 已提交
50 51
typedef struct
{								/* Per-attribute information */
52 53
	Oid			typoutput;		/* Oid for the type's text output fn */
	Oid			typsend;		/* Oid for the type's binary output fn */
54
	Oid			typelem;		/* typelem value to pass to the output fn */
55
	bool		typisvarlena;	/* is it varlena (ie possibly toastable)? */
56 57
	int16		format;			/* format code for this column */
	FmgrInfo	finfo;			/* Precomputed call info for output fn */
58
} PrinttupAttrInfo;
59

B
Bruce Momjian 已提交
60 61 62
typedef struct
{
	DestReceiver pub;			/* publicly-known function pointers */
63
	Portal		portal;			/* the Portal we are printing from */
64
	bool		sendDescrip;	/* send RowDescription at startup? */
B
Bruce Momjian 已提交
65 66 67
	TupleDesc	attrinfo;		/* The attr info we are set up for */
	int			nattrs;
	PrinttupAttrInfo *myinfo;	/* Cached info about each attr */
68
} DR_printtup;
69 70 71 72 73

/* ----------------
 *		Initialize: create a DestReceiver for printtup
 * ----------------
 */
B
Bruce Momjian 已提交
74
DestReceiver *
75
printtup_create_DR(CommandDest dest, Portal portal)
76
{
B
Bruce Momjian 已提交
77
	DR_printtup *self = (DR_printtup *) palloc(sizeof(DR_printtup));
78

79 80 81
	if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
		self->pub.receiveTuple = printtup;
	else
82
	{
83 84 85 86 87 88 89 90 91
		/*
		 * In protocol 2.0 the Bind message does not exist, so there is
		 * no way for the columns to have different print formats; it's
		 * sufficient to look at the first one.
		 */
		if (portal->formats && portal->formats[0] != 0)
			self->pub.receiveTuple = printtup_internal_20;
		else
			self->pub.receiveTuple = printtup_20;
92 93 94 95 96
	}
	self->pub.startup = printtup_startup;
	self->pub.shutdown = printtup_shutdown;
	self->pub.destroy = printtup_destroy;
	self->pub.mydest = dest;
97

98 99 100 101
	self->portal = portal;

	/* Send T message automatically if Remote, but not if RemoteExecute */
	self->sendDescrip = (dest == Remote);
102

103 104 105 106
	self->attrinfo = NULL;
	self->nattrs = 0;
	self->myinfo = NULL;

B
Bruce Momjian 已提交
107
	return (DestReceiver *) self;
108 109 110
}

static void
111
printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
112
{
113
	DR_printtup *myState = (DR_printtup *) self;
114
	Portal	portal = myState->portal;
115

116 117 118 119 120 121 122
	if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
	{
		/*
		 * Send portal name to frontend (obsolete cruft, gone in proto 3.0)
		 *
		 * If portal name not specified, use "blank" portal.
		 */
123 124 125
		const char *portalName = portal->name;

		if (portalName == NULL || portalName[0] == '\0')
126 127 128 129
			portalName = "blank";

		pq_puttextmessage('P', portalName);
	}
130 131

	/*
132 133
	 * If this is a retrieve, and we are supposed to emit row descriptions,
	 * then we send back the tuple descriptor of the tuples.  
134
	 */
135
	if (operation == CMD_SELECT && myState->sendDescrip)
136 137 138 139 140 141 142 143 144 145
	{
		List	   *targetlist;

		if (portal->strategy == PORTAL_ONE_SELECT)
			targetlist = ((Query *) lfirst(portal->parseTrees))->targetList;
		else
			targetlist = NIL;

		SendRowDescriptionMessage(typeinfo, targetlist, portal->formats);
	}
146

147 148
	/* ----------------
	 * We could set up the derived attr info at this time, but we postpone it
149
	 * until the first call of printtup, for 2 reasons:
150
	 * 1. We don't waste time (compared to the old way) if there are no
B
Bruce Momjian 已提交
151
	 *	  tuples at all to output.
152
	 * 2. Checking in printtup allows us to handle the case that the tuples
B
Bruce Momjian 已提交
153 154
	 *	  change type midway through (although this probably can't happen in
	 *	  the current executor).
155 156 157 158
	 * ----------------
	 */
}

159 160
/*
 * SendRowDescriptionMessage --- send a RowDescription message to the frontend
161 162 163 164
 *
 * Notes: the TupleDesc has typically been manufactured by ExecTypeFromTL()
 * or some similar function; it does not contain a full set of fields.
 * The targetlist will be NIL when executing a utility function that does
165 166 167 168
 * not have a plan.  If the targetlist isn't NIL then it is a Query node's
 * targetlist; it is up to us to ignore resjunk columns in it.  The formats[]
 * array pointer might be NULL (if we are doing Describe on a prepared stmt);
 * send zeroes for the format codes in that case.
169 170
 */
void
171
SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, int16 *formats)
172 173 174 175 176 177 178 179 180 181 182 183
{
	Form_pg_attribute *attrs = typeinfo->attrs;
	int			natts = typeinfo->natts;
	int			proto = PG_PROTOCOL_MAJOR(FrontendProtocol);
	int			i;
	StringInfoData buf;

	pq_beginmessage(&buf, 'T');		/* tuple descriptor message type */
	pq_sendint(&buf, natts, 2);		/* # of attrs in tuples */

	for (i = 0; i < natts; ++i)
	{
184 185 186 187
		Oid		atttypid = attrs[i]->atttypid;
		int32	atttypmod = attrs[i]->atttypmod;
		Oid		basetype;

188 189 190 191
		pq_sendstring(&buf, NameStr(attrs[i]->attname));
		/* column ID info appears in protocol 3.0 and up */
		if (proto >= 3)
		{
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
			/* Do we have a non-resjunk tlist item? */
			while (targetlist &&
				   ((TargetEntry *) lfirst(targetlist))->resdom->resjunk)
				targetlist = lnext(targetlist);
			if (targetlist)
			{
				Resdom	   *res = ((TargetEntry *) lfirst(targetlist))->resdom;

				pq_sendint(&buf, res->resorigtbl, 4);
				pq_sendint(&buf, res->resorigcol, 2);
				targetlist = lnext(targetlist);
			}
			else
			{
				/* No info available, so send zeroes */
				pq_sendint(&buf, 0, 4);
				pq_sendint(&buf, 0, 2);
			}
210
		}
211 212 213 214 215 216 217 218 219
		/* If column is a domain, send the base type and typmod instead */
		basetype = getBaseType(atttypid);
		if (basetype != atttypid)
		{
			atttypmod = get_typtypmod(atttypid);
			atttypid = basetype;
		}
		pq_sendint(&buf, (int) atttypid, sizeof(atttypid));
		pq_sendint(&buf, attrs[i]->attlen, sizeof(attrs[i]->attlen));
220 221
		/* typmod appears in protocol 2.0 and up */
		if (proto >= 2)
222
			pq_sendint(&buf, atttypmod, sizeof(atttypmod));
223 224 225 226 227 228 229 230
		/* format info appears in protocol 3.0 and up */
		if (proto >= 3)
		{
			if (formats)
				pq_sendint(&buf, formats[i], 2);
			else
				pq_sendint(&buf, 0, 2);
		}
231 232 233 234
	}
	pq_endmessage(&buf);
}

235 236 237
/*
 * Get the lookup info that printtup() needs
 */
238
static void
239
printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
240
{
241
	int16	   *formats = myState->portal->formats;
B
Bruce Momjian 已提交
242
	int			i;
243 244

	if (myState->myinfo)
B
Bruce Momjian 已提交
245
		pfree(myState->myinfo); /* get rid of any old data */
246 247 248 249 250
	myState->myinfo = NULL;
	myState->attrinfo = typeinfo;
	myState->nattrs = numAttrs;
	if (numAttrs <= 0)
		return;
B
Bruce Momjian 已提交
251
	myState->myinfo = (PrinttupAttrInfo *)
252
		palloc0(numAttrs * sizeof(PrinttupAttrInfo));
253 254
	for (i = 0; i < numAttrs; i++)
	{
B
Bruce Momjian 已提交
255
		PrinttupAttrInfo *thisState = myState->myinfo + i;
256
		int16		format = (formats ? formats[i] : 0);
B
Bruce Momjian 已提交
257

258 259 260 261 262 263 264
		thisState->format = format;
		if (format == 0)
		{
			getTypeOutputInfo(typeinfo->attrs[i]->atttypid,
							  &thisState->typoutput,
							  &thisState->typelem,
							  &thisState->typisvarlena);
265
			fmgr_info(thisState->typoutput, &thisState->finfo);
266 267 268 269 270 271 272 273 274 275 276
		}
		else if (format == 1)
		{
			getTypeBinaryOutputInfo(typeinfo->attrs[i]->atttypid,
									&thisState->typsend,
									&thisState->typelem,
									&thisState->typisvarlena);
			fmgr_info(thisState->typsend, &thisState->finfo);
		}
		else
			elog(ERROR, "Unsupported format code %d", format);
277 278 279
	}
}

280
/* ----------------
281
 *		printtup --- print a tuple in protocol 3.0
282 283
 * ----------------
 */
284
static void
285
printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
{
	DR_printtup *myState = (DR_printtup *) self;
	StringInfoData buf;
	int			natts = tuple->t_data->t_natts;
	int			i;

	/* Set or update my derived attribute info, if needed */
	if (myState->attrinfo != typeinfo || myState->nattrs != natts)
		printtup_prepare_info(myState, typeinfo, natts);

	/*
	 * Prepare a DataRow message
	 */
	pq_beginmessage(&buf, 'D');

	pq_sendint(&buf, natts, 2);

	/*
	 * send the attributes of this tuple
	 */
	for (i = 0; i < natts; ++i)
	{
		PrinttupAttrInfo *thisState = myState->myinfo + i;
		Datum		origattr,
					attr;
		bool		isnull;

		origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
		if (isnull)
		{
			pq_sendint(&buf, -1, 4);
			continue;
		}

320 321 322 323 324 325 326 327
		/*
		 * If we have a toasted datum, forcibly detoast it here to
		 * avoid memory leakage inside the type's output routine.
		 */
		if (thisState->typisvarlena)
			attr = PointerGetDatum(PG_DETOAST_DATUM(origattr));
		else
			attr = origattr;
328

329
		if (thisState->format == 0)
330
		{
331 332 333 334 335 336 337 338 339
			/* Text output */
			char	   *outputstr;

			outputstr = DatumGetCString(FunctionCall3(&thisState->finfo,
													  attr,
									ObjectIdGetDatum(thisState->typelem),
						  Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
			pq_sendcountedtext(&buf, outputstr, strlen(outputstr), false);
			pfree(outputstr);
340 341 342
		}
		else
		{
343 344 345 346 347 348 349 350 351 352 353
			/* Binary output */
			bytea	   *outputbytes;

			outputbytes = DatumGetByteaP(FunctionCall2(&thisState->finfo,
													   attr,
									ObjectIdGetDatum(thisState->typelem)));
			/* We assume the result will not have been toasted */
			pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
			pq_sendbytes(&buf, VARDATA(outputbytes),
						 VARSIZE(outputbytes) - VARHDRSZ);
			pfree(outputbytes);
354
		}
355 356 357 358

		/* Clean up detoasted copy, if any */
		if (attr != origattr)
			pfree(DatumGetPointer(attr));
359 360 361 362 363 364 365 366 367 368 369
	}

	pq_endmessage(&buf);
}

/* ----------------
 *		printtup_20 --- print a tuple in protocol 2.0
 * ----------------
 */
static void
printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
370
{
B
Bruce Momjian 已提交
371
	DR_printtup *myState = (DR_printtup *) self;
372
	StringInfoData buf;
373
	int			natts = tuple->t_data->t_natts;
374 375
	int			i,
				j,
376
				k;
377

378
	/* Set or update my derived attribute info, if needed */
379 380
	if (myState->attrinfo != typeinfo || myState->nattrs != natts)
		printtup_prepare_info(myState, typeinfo, natts);
381

382 383
	/*
	 * tell the frontend to expect new tuple data (in ASCII style)
384
	 */
385
	pq_beginmessage(&buf, 'D');
386

387 388
	/*
	 * send a bitmap of which attributes are not null
389 390 391
	 */
	j = 0;
	k = 1 << 7;
392
	for (i = 0; i < natts; ++i)
393
	{
B
Bruce Momjian 已提交
394
		if (!heap_attisnull(tuple, i + 1))
395
			j |= k;				/* set bit if not null */
396
		k >>= 1;
397
		if (k == 0)				/* end of byte? */
398
		{
399
			pq_sendint(&buf, j, 1);
400 401 402
			j = 0;
			k = 1 << 7;
		}
403
	}
404
	if (k != (1 << 7))			/* flush last partial byte */
405
		pq_sendint(&buf, j, 1);
406

407 408
	/*
	 * send the attributes of this tuple
409
	 */
410
	for (i = 0; i < natts; ++i)
411
	{
B
Bruce Momjian 已提交
412
		PrinttupAttrInfo *thisState = myState->myinfo + i;
413 414 415 416
		Datum		origattr,
					attr;
		bool		isnull;
		char	   *outputstr;
B
Bruce Momjian 已提交
417

418
		origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
M
 
Marc G. Fournier 已提交
419 420
		if (isnull)
			continue;
421

422 423 424 425 426 427 428 429 430 431 432 433 434
		Assert(thisState->format == 0);

		/*
		 * If we have a toasted datum, forcibly detoast it here to
		 * avoid memory leakage inside the type's output routine.
		 */
		if (thisState->typisvarlena)
			attr = PointerGetDatum(PG_DETOAST_DATUM(origattr));
		else
			attr = origattr;

		outputstr = DatumGetCString(FunctionCall3(&thisState->finfo,
												  attr,
B
Bruce Momjian 已提交
435 436
									ObjectIdGetDatum(thisState->typelem),
						  Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
437 438
		pq_sendcountedtext(&buf, outputstr, strlen(outputstr), true);
		pfree(outputstr);
439

440 441 442
		/* Clean up detoasted copy, if any */
		if (attr != origattr)
			pfree(DatumGetPointer(attr));
443
	}
444 445

	pq_endmessage(&buf);
446 447
}

448
/* ----------------
449
 *		printtup_shutdown
450 451 452
 * ----------------
 */
static void
453
printtup_shutdown(DestReceiver *self)
454
{
B
Bruce Momjian 已提交
455 456
	DR_printtup *myState = (DR_printtup *) self;

457 458
	if (myState->myinfo)
		pfree(myState->myinfo);
459 460 461 462 463 464 465 466 467 468 469 470
	myState->myinfo = NULL;
	myState->attrinfo = NULL;
}

/* ----------------
 *		printtup_destroy
 * ----------------
 */
static void
printtup_destroy(DestReceiver *self)
{
	pfree(self);
471 472
}

473
/* ----------------
474
 *		printatt
475 476 477 478
 * ----------------
 */
static void
printatt(unsigned attributeId,
479
		 Form_pg_attribute attributeP,
480
		 char *value)
481
{
B
Bruce Momjian 已提交
482
	printf("\t%2d: %s%s%s%s\t(typeid = %u, len = %d, typmod = %d, byval = %c)\n",
483
		   attributeId,
484
		   NameStr(attributeP->attname),
485 486 487 488 489
		   value != NULL ? " = \"" : "",
		   value != NULL ? value : "",
		   value != NULL ? "\"" : "",
		   (unsigned int) (attributeP->atttypid),
		   attributeP->attlen,
B
Bruce Momjian 已提交
490
		   attributeP->atttypmod,
491
		   attributeP->attbyval ? 't' : 'f');
492 493 494
}

/* ----------------
495
 *		debugStartup - prepare to print tuples for an interactive backend
496 497 498
 * ----------------
 */
void
499
debugStartup(DestReceiver *self, int operation, TupleDesc typeinfo)
500
{
501 502 503 504
	int			natts = typeinfo->natts;
	Form_pg_attribute *attinfo = typeinfo->attrs;
	int			i;

505 506 507
	/*
	 * show the return type of the tuples
	 */
508 509 510
	for (i = 0; i < natts; ++i)
		printatt((unsigned) i + 1, attinfo[i], (char *) NULL);
	printf("\t----\n");
511 512 513 514
}

/* ----------------
 *		debugtup - print one tuple for an interactive backend
515 516 517
 * ----------------
 */
void
518
debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
519
{
520
	int			natts = tuple->t_data->t_natts;
521
	int			i;
522 523
	Datum		origattr,
				attr;
524
	char	   *value;
525
	bool		isnull;
526 527
	Oid			typoutput,
				typelem;
528
	bool		typisvarlena;
529

530
	for (i = 0; i < natts; ++i)
531
	{
532
		origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
533 534
		if (isnull)
			continue;
535 536 537 538 539 540 541 542 543 544
		getTypeOutputInfo(typeinfo->attrs[i]->atttypid,
						  &typoutput, &typelem, &typisvarlena);
		/*
		 * If we have a toasted datum, forcibly detoast it here to
		 * avoid memory leakage inside the type's output routine.
		 */
		if (typisvarlena)
			attr = PointerGetDatum(PG_DETOAST_DATUM(origattr));
		else
			attr = origattr;
545

546 547 548
		value = DatumGetCString(OidFunctionCall3(typoutput,
												 attr,
												 ObjectIdGetDatum(typelem),
B
Bruce Momjian 已提交
549
						  Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
550

551
		printatt((unsigned) i + 1, typeinfo->attrs[i], value);
552

553 554 555 556 557
		pfree(value);

		/* Clean up detoasted copy, if any */
		if (attr != origattr)
			pfree(DatumGetPointer(attr));
558
	}
559
	printf("\t----\n");
560 561 562
}

/* ----------------
563 564 565 566
 *		printtup_internal_20 --- print a binary tuple in protocol 2.0
 *
 * We use a different message type, i.e. 'B' instead of 'D' to
 * indicate a tuple in internal (binary) form.
567
 *
568
 * This is largely same as printtup_20, except we use binary formatting.
569 570
 * ----------------
 */
571
static void
572
printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
573
{
574
	DR_printtup *myState = (DR_printtup *) self;
575
	StringInfoData buf;
576
	int			natts = tuple->t_data->t_natts;
577 578 579
	int			i,
				j,
				k;
580 581 582 583

	/* Set or update my derived attribute info, if needed */
	if (myState->attrinfo != typeinfo || myState->nattrs != natts)
		printtup_prepare_info(myState, typeinfo, natts);
584

585 586
	/*
	 * tell the frontend to expect new tuple data (in binary style)
587
	 */
588
	pq_beginmessage(&buf, 'B');
589

590 591
	/*
	 * send a bitmap of which attributes are not null
592 593 594
	 */
	j = 0;
	k = 1 << 7;
595
	for (i = 0; i < natts; ++i)
596
	{
B
Bruce Momjian 已提交
597
		if (!heap_attisnull(tuple, i + 1))
598
			j |= k;				/* set bit if not null */
599
		k >>= 1;
600
		if (k == 0)				/* end of byte? */
601
		{
602
			pq_sendint(&buf, j, 1);
603 604 605
			j = 0;
			k = 1 << 7;
		}
606
	}
607
	if (k != (1 << 7))			/* flush last partial byte */
608
		pq_sendint(&buf, j, 1);
609

610 611
	/*
	 * send the attributes of this tuple
612
	 */
613
	for (i = 0; i < natts; ++i)
614
	{
615 616 617 618
		PrinttupAttrInfo *thisState = myState->myinfo + i;
		Datum		origattr,
					attr;
		bool		isnull;
619
		bytea	   *outputbytes;
620

621 622 623
		origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
		if (isnull)
			continue;
624

625
		Assert(thisState->format == 1);
626

627 628 629 630 631 632
		/*
		 * If we have a toasted datum, forcibly detoast it here to
		 * avoid memory leakage inside the type's output routine.
		 */
		if (thisState->typisvarlena)
			attr = PointerGetDatum(PG_DETOAST_DATUM(origattr));
633 634
		else
			attr = origattr;
635 636 637 638 639 640 641 642 643 644 645 646 647

		outputbytes = DatumGetByteaP(FunctionCall2(&thisState->finfo,
												   attr,
									ObjectIdGetDatum(thisState->typelem)));
		/* We assume the result will not have been toasted */
		pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
		pq_sendbytes(&buf, VARDATA(outputbytes),
					 VARSIZE(outputbytes) - VARHDRSZ);
		pfree(outputbytes);

		/* Clean up detoasted copy, if any */
		if (attr != origattr)
			pfree(DatumGetPointer(attr));
648
	}
649 650

	pq_endmessage(&buf);
651
}