printtup.c 12.5 KB
Newer Older
1 2
/*-------------------------------------------------------------------------
 *
3
 * printtup.c
4 5 6
 *	  Routines to print out tuples to the destination (both frontend
 *	  clients and interactive backends are supported here).
 *
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
B
Bruce Momjian 已提交
12
 *	  $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.62 2002/06/20 20:29:24 momjian Exp $
13 14 15
 *
 *-------------------------------------------------------------------------
 */
16 17 18 19 20
#include "postgres.h"

#include "access/heapam.h"
#include "access/printtup.h"
#include "catalog/pg_type.h"
21
#include "libpq/libpq.h"
22 23
#include "libpq/pqformat.h"
#include "utils/syscache.h"
M
Marc G. Fournier 已提交
24

25 26
static void printtup_setup(DestReceiver *self, int operation,
						   const char *portalName, TupleDesc typeinfo);
27
static void printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self);
28
static void printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self);
29
static void printtup_cleanup(DestReceiver *self);
30

31
/* ----------------------------------------------------------------
32
 *		printtup / debugtup support
33 34 35 36
 * ----------------------------------------------------------------
 */

/* ----------------
37
 *		getTypeOutputInfo -- get info needed for printing values of a type
38 39
 * ----------------
 */
40 41 42
bool
getTypeOutputInfo(Oid type, Oid *typOutput, Oid *typElem,
				  bool *typIsVarlena)
43
{
44
	HeapTuple	typeTuple;
45
	Form_pg_type pt;
46

47 48 49 50
	typeTuple = SearchSysCache(TYPEOID,
							   ObjectIdGetDatum(type),
							   0, 0, 0);
	if (!HeapTupleIsValid(typeTuple))
51
		elog(ERROR, "getTypeOutputInfo: Cache lookup of type %u failed", type);
52
	pt = (Form_pg_type) GETSTRUCT(typeTuple);
53

54 55
	*typOutput = pt->typoutput;
	*typElem = pt->typelem;
B
Bruce Momjian 已提交
56
	*typIsVarlena = (!pt->typbyval) && (pt->typlen == -1);
57 58
	ReleaseSysCache(typeTuple);
	return OidIsValid(*typOutput);
59 60
}

61 62 63 64
/* ----------------
 *		Private state for a printtup destination object
 * ----------------
 */
B
Bruce Momjian 已提交
65 66
typedef struct
{								/* Per-attribute information */
67 68
	Oid			typoutput;		/* Oid for the attribute's type output fn */
	Oid			typelem;		/* typelem value to pass to the output fn */
69
	bool		typisvarlena;	/* is it varlena (ie possibly toastable)? */
70
	FmgrInfo	finfo;			/* Precomputed call info for typoutput */
71
} PrinttupAttrInfo;
72

B
Bruce Momjian 已提交
73 74 75 76 77 78
typedef struct
{
	DestReceiver pub;			/* publicly-known function pointers */
	TupleDesc	attrinfo;		/* The attr info we are set up for */
	int			nattrs;
	PrinttupAttrInfo *myinfo;	/* Cached info about each attr */
79
} DR_printtup;
80 81 82 83 84

/* ----------------
 *		Initialize: create a DestReceiver for printtup
 * ----------------
 */
B
Bruce Momjian 已提交
85
DestReceiver *
86
printtup_create_DR(bool isBinary)
87
{
B
Bruce Momjian 已提交
88
	DR_printtup *self = (DR_printtup *) palloc(sizeof(DR_printtup));
89

90
	self->pub.receiveTuple = isBinary ? printtup_internal : printtup;
91 92 93 94 95 96 97
	self->pub.setup = printtup_setup;
	self->pub.cleanup = printtup_cleanup;

	self->attrinfo = NULL;
	self->nattrs = 0;
	self->myinfo = NULL;

B
Bruce Momjian 已提交
98
	return (DestReceiver *) self;
99 100 101
}

static void
102 103
printtup_setup(DestReceiver *self, int operation,
			   const char *portalName, TupleDesc typeinfo)
104
{
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
	/*
	 * Send portal name to frontend.
	 *
	 * If portal name not specified, use "blank" portal.
	 */
	if (portalName == NULL)
		portalName = "blank";

	pq_puttextmessage('P', portalName);

	/*
	 * if this is a retrieve, then we send back the tuple
	 * descriptor of the tuples.
	 */
	if (operation == CMD_SELECT)
	{
		Form_pg_attribute *attrs = typeinfo->attrs;
		int			natts = typeinfo->natts;
		int			i;
		StringInfoData buf;

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

		for (i = 0; i < natts; ++i)
		{
			pq_sendstring(&buf, NameStr(attrs[i]->attname));
			pq_sendint(&buf, (int) attrs[i]->atttypid,
					   sizeof(attrs[i]->atttypid));
			pq_sendint(&buf, attrs[i]->attlen,
					   sizeof(attrs[i]->attlen));
			if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
				pq_sendint(&buf, attrs[i]->atttypmod,
						   sizeof(attrs[i]->atttypmod));
		}
		pq_endmessage(&buf);
	}

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

static void
157
printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
158
{
B
Bruce Momjian 已提交
159
	int			i;
160 161

	if (myState->myinfo)
B
Bruce Momjian 已提交
162
		pfree(myState->myinfo); /* get rid of any old data */
163 164 165 166 167
	myState->myinfo = NULL;
	myState->attrinfo = typeinfo;
	myState->nattrs = numAttrs;
	if (numAttrs <= 0)
		return;
B
Bruce Momjian 已提交
168
	myState->myinfo = (PrinttupAttrInfo *)
169 170 171
		palloc(numAttrs * sizeof(PrinttupAttrInfo));
	for (i = 0; i < numAttrs; i++)
	{
B
Bruce Momjian 已提交
172 173
		PrinttupAttrInfo *thisState = myState->myinfo + i;

174 175 176
		if (getTypeOutputInfo(typeinfo->attrs[i]->atttypid,
							  &thisState->typoutput, &thisState->typelem,
							  &thisState->typisvarlena))
177
			fmgr_info(thisState->typoutput, &thisState->finfo);
178 179 180
	}
}

181
/* ----------------
182
 *		printtup
183 184
 * ----------------
 */
185
static void
186
printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
187
{
B
Bruce Momjian 已提交
188
	DR_printtup *myState = (DR_printtup *) self;
189
	StringInfoData buf;
190
	int			natts = tuple->t_data->t_natts;
191 192
	int			i,
				j,
193
				k;
194

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

199 200
	/*
	 * tell the frontend to expect new tuple data (in ASCII style)
201
	 */
202 203
	pq_beginmessage(&buf);
	pq_sendbyte(&buf, 'D');
204

205 206
	/*
	 * send a bitmap of which attributes are not null
207 208 209
	 */
	j = 0;
	k = 1 << 7;
210
	for (i = 0; i < natts; ++i)
211
	{
B
Bruce Momjian 已提交
212
		if (!heap_attisnull(tuple, i + 1))
213
			j |= k;				/* set bit if not null */
214
		k >>= 1;
215
		if (k == 0)				/* end of byte? */
216
		{
217
			pq_sendint(&buf, j, 1);
218 219 220
			j = 0;
			k = 1 << 7;
		}
221
	}
222
	if (k != (1 << 7))			/* flush last partial byte */
223
		pq_sendint(&buf, j, 1);
224

225 226
	/*
	 * send the attributes of this tuple
227
	 */
228
	for (i = 0; i < natts; ++i)
229
	{
B
Bruce Momjian 已提交
230
		PrinttupAttrInfo *thisState = myState->myinfo + i;
231 232 233 234
		Datum		origattr,
					attr;
		bool		isnull;
		char	   *outputstr;
B
Bruce Momjian 已提交
235

236
		origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
M
 
Marc G. Fournier 已提交
237 238
		if (isnull)
			continue;
239
		if (OidIsValid(thisState->typoutput))
240
		{
241
			/*
B
Bruce Momjian 已提交
242 243
			 * If we have a toasted datum, forcibly detoast it here to
			 * avoid memory leakage inside the type's output routine.
244 245 246 247 248 249
			 */
			if (thisState->typisvarlena)
				attr = PointerGetDatum(PG_DETOAST_DATUM(origattr));
			else
				attr = origattr;

250
			outputstr = DatumGetCString(FunctionCall3(&thisState->finfo,
B
Bruce Momjian 已提交
251 252 253
													  attr,
									ObjectIdGetDatum(thisState->typelem),
						  Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
254

255
			pq_sendcountedtext(&buf, outputstr, strlen(outputstr));
256 257 258 259

			/* Clean up detoasted copy, if any */
			if (attr != origattr)
				pfree(DatumGetPointer(attr));
260 261
			pfree(outputstr);
		}
M
 
Marc G. Fournier 已提交
262 263 264
		else
		{
			outputstr = "<unprintable>";
265
			pq_sendcountedtext(&buf, outputstr, strlen(outputstr));
266
		}
267
	}
268 269

	pq_endmessage(&buf);
270 271
}

272 273 274 275 276
/* ----------------
 *		printtup_cleanup
 * ----------------
 */
static void
277
printtup_cleanup(DestReceiver *self)
278
{
B
Bruce Momjian 已提交
279 280
	DR_printtup *myState = (DR_printtup *) self;

281 282 283 284 285
	if (myState->myinfo)
		pfree(myState->myinfo);
	pfree(myState);
}

286
/* ----------------
287
 *		printatt
288 289 290 291
 * ----------------
 */
static void
printatt(unsigned attributeId,
292
		 Form_pg_attribute attributeP,
293
		 char *value)
294
{
B
Bruce Momjian 已提交
295
	printf("\t%2d: %s%s%s%s\t(typeid = %u, len = %d, typmod = %d, byval = %c)\n",
296
		   attributeId,
297
		   NameStr(attributeP->attname),
298 299 300 301 302
		   value != NULL ? " = \"" : "",
		   value != NULL ? value : "",
		   value != NULL ? "\"" : "",
		   (unsigned int) (attributeP->atttypid),
		   attributeP->attlen,
B
Bruce Momjian 已提交
303
		   attributeP->atttypmod,
304
		   attributeP->attbyval ? 't' : 'f');
305 306 307
}

/* ----------------
308
 *		showatts
309 310
 * ----------------
 */
311 312
static void
showatts(const char *name, TupleDesc tupleDesc)
313
{
314
	int			natts = tupleDesc->natts;
315
	Form_pg_attribute *attinfo = tupleDesc->attrs;
316
	int			i;
317

318 319 320 321
	puts(name);
	for (i = 0; i < natts; ++i)
		printatt((unsigned) i + 1, attinfo[i], (char *) NULL);
	printf("\t----\n");
322 323 324
}

/* ----------------
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
 *		debugSetup - prepare to print tuples for an interactive backend
 * ----------------
 */
void
debugSetup(DestReceiver *self, int operation,
		   const char *portalName, TupleDesc typeinfo)
{
	/*
	 * show the return type of the tuples
	 */
	if (portalName == NULL)
		portalName = "blank";

	showatts(portalName, typeinfo);
}

/* ----------------
 *		debugtup - print one tuple for an interactive backend
343 344 345
 * ----------------
 */
void
346
debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
347
{
348
	int			natts = tuple->t_data->t_natts;
349
	int			i;
350 351
	Datum		origattr,
				attr;
352
	char	   *value;
353
	bool		isnull;
354 355
	Oid			typoutput,
				typelem;
356
	bool		typisvarlena;
357

358
	for (i = 0; i < natts; ++i)
359
	{
360
		origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
361 362
		if (isnull)
			continue;
363 364
		if (getTypeOutputInfo(typeinfo->attrs[i]->atttypid,
							  &typoutput, &typelem, &typisvarlena))
365
		{
366
			/*
B
Bruce Momjian 已提交
367 368
			 * If we have a toasted datum, forcibly detoast it here to
			 * avoid memory leakage inside the type's output routine.
369 370 371 372 373 374
			 */
			if (typisvarlena)
				attr = PointerGetDatum(PG_DETOAST_DATUM(origattr));
			else
				attr = origattr;

375
			value = DatumGetCString(OidFunctionCall3(typoutput,
B
Bruce Momjian 已提交
376 377 378
													 attr,
											   ObjectIdGetDatum(typelem),
						  Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
379

380
			printatt((unsigned) i + 1, typeinfo->attrs[i], value);
381 382 383 384

			/* Clean up detoasted copy, if any */
			if (attr != origattr)
				pfree(DatumGetPointer(attr));
385 386
			pfree(value);
		}
387
	}
388
	printf("\t----\n");
389 390 391
}

/* ----------------
392 393 394
 *		printtup_internal
 *		We use a different data prefix, e.g. 'B' instead of 'D' to
 *		indicate a tuple in internal (binary) form.
395
 *
396
 *		This is largely same as printtup, except we don't use the typout func.
397 398
 * ----------------
 */
399
static void
400
printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
401
{
402
	DR_printtup *myState = (DR_printtup *) self;
403
	StringInfoData buf;
404
	int			natts = tuple->t_data->t_natts;
405 406 407
	int			i,
				j,
				k;
408 409 410 411

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

413 414
	/*
	 * tell the frontend to expect new tuple data (in binary style)
415
	 */
416 417
	pq_beginmessage(&buf);
	pq_sendbyte(&buf, 'B');
418

419 420
	/*
	 * send a bitmap of which attributes are not null
421 422 423
	 */
	j = 0;
	k = 1 << 7;
424
	for (i = 0; i < natts; ++i)
425
	{
B
Bruce Momjian 已提交
426
		if (!heap_attisnull(tuple, i + 1))
427
			j |= k;				/* set bit if not null */
428
		k >>= 1;
429
		if (k == 0)				/* end of byte? */
430
		{
431
			pq_sendint(&buf, j, 1);
432 433 434
			j = 0;
			k = 1 << 7;
		}
435
	}
436
	if (k != (1 << 7))			/* flush last partial byte */
437
		pq_sendint(&buf, j, 1);
438

439 440
	/*
	 * send the attributes of this tuple
441
	 */
442
#ifdef IPORTAL_DEBUG
443
	fprintf(stderr, "sending tuple with %d atts\n", natts);
444
#endif
445 446

	for (i = 0; i < natts; ++i)
447
	{
448 449 450 451 452
		PrinttupAttrInfo *thisState = myState->myinfo + i;
		Datum		origattr,
					attr;
		bool		isnull;
		int32		len;
453

454 455 456 457 458
		origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
		if (isnull)
			continue;
		/* send # of bytes, and opaque data */
		if (thisState->typisvarlena)
459
		{
460 461 462 463 464 465
			/*
			 * If we have a toasted datum, must detoast before sending.
			 */
			attr = PointerGetDatum(PG_DETOAST_DATUM(origattr));

			len = VARSIZE(attr) - VARHDRSZ;
466

467 468
			pq_sendint(&buf, len, VARHDRSZ);
			pq_sendbytes(&buf, VARDATA(attr), len);
469

470
#ifdef IPORTAL_DEBUG
471 472
			{
				char	   *d = VARDATA(attr);
473

474 475
				fprintf(stderr, "length %d data %x %x %x %x\n",
						len, *d, *(d + 1), *(d + 2), *(d + 3));
476
			}
477 478 479 480 481 482 483 484 485 486 487 488 489
#endif

			/* Clean up detoasted copy, if any */
			if (attr != origattr)
				pfree(DatumGetPointer(attr));
		}
		else
		{
			/* fixed size */
			attr = origattr;
			len = typeinfo->attrs[i]->attlen;
			pq_sendint(&buf, len, sizeof(int32));
			if (typeinfo->attrs[i]->attbyval)
490
			{
491 492 493 494 495 496 497 498
				Datum		datumBuf;

				/*
				 * We need this horsing around because we don't know how
				 * shorter data values are aligned within a Datum.
				 */
				store_att_byval(&datumBuf, attr, len);
				pq_sendbytes(&buf, (char *) &datumBuf, len);
499
#ifdef IPORTAL_DEBUG
500 501
				fprintf(stderr, "byval length %d data %ld\n", len,
						(long) attr);
502
#endif
503 504 505 506
			}
			else
			{
				pq_sendbytes(&buf, DatumGetPointer(attr), len);
507
#ifdef IPORTAL_DEBUG
508 509
				fprintf(stderr, "byref length %d data %p\n", len,
						DatumGetPointer(attr));
510
#endif
511
			}
512 513
		}
	}
514 515

	pq_endmessage(&buf);
516
}