printtup.c 10.5 KB
Newer Older
1 2
/*-------------------------------------------------------------------------
 *
3
 * printtup.c
4 5
 *	  Routines to print out tuples to the destination (binary or non-binary
 *	  portals, frontend/interactive backend, etc.).
6 7 8 9 10
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
11
 *	  $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.43 1999/04/25 03:19:23 tgl Exp $
12 13 14 15
 *
 *-------------------------------------------------------------------------
 */

16
#include <string.h>
17

18 19 20 21 22 23 24 25 26
#include "postgres.h"

#include "fmgr.h"
#include "access/heapam.h"
#include "access/printtup.h"
#include "catalog/pg_type.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "utils/syscache.h"
M
Marc G. Fournier 已提交
27

28
#ifdef MULTIBYTE
29
#include "mb/pg_wchar.h"
30 31
#endif

32 33 34 35
static void printtup_setup(DestReceiver* self, TupleDesc typeinfo);
static void printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self);
static void printtup_cleanup(DestReceiver* self);

36
/* ----------------------------------------------------------------
37
 *		printtup / debugtup support
38 39 40 41
 * ----------------------------------------------------------------
 */

/* ----------------
42 43 44 45 46
 *		getTypeOutAndElem -- get both typoutput and typelem for a type
 *
 * We used to fetch these with two separate function calls,
 * typtoout() and gettypelem(), which each called SearchSysCacheTuple.
 * This way takes half the time.
47 48
 * ----------------
 */
49 50
int
getTypeOutAndElem(Oid type, Oid* typOutput, Oid* typElem)
51
{
52
	HeapTuple	typeTuple;
53 54 55 56 57 58

	typeTuple = SearchSysCacheTuple(TYPOID,
									ObjectIdGetDatum(type),
									0, 0, 0);

	if (HeapTupleIsValid(typeTuple))
59 60 61 62 63 64
	{
		Form_pg_type pt = (Form_pg_type) GETSTRUCT(typeTuple);
		*typOutput = (Oid) pt->typoutput;
		*typElem = (Oid) pt->typelem;
		return OidIsValid(*typOutput);
	}
65

66
	elog(ERROR, "getTypeOutAndElem: Cache lookup of type %d failed", type);
67

68 69 70
	*typOutput = InvalidOid;
	*typElem = InvalidOid;
	return 0;
71 72
}

73 74 75 76 77 78 79
/* ----------------
 *		Private state for a printtup destination object
 * ----------------
 */
typedef struct {				/* Per-attribute information */
	Oid			typoutput;		/* Oid for the attribute's type output fn */
	Oid			typelem;		/* typelem value to pass to the output fn */
80
	FmgrInfo	finfo;			/* Precomputed call info for typoutput */
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 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
} PrinttupAttrInfo;

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 */
} DR_printtup;

/* ----------------
 *		Initialize: create a DestReceiver for printtup
 * ----------------
 */
DestReceiver*
printtup_create_DR()
{
	DR_printtup* self = (DR_printtup*) palloc(sizeof(DR_printtup));

	self->pub.receiveTuple = printtup;
	self->pub.setup = printtup_setup;
	self->pub.cleanup = printtup_cleanup;

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

	return (DestReceiver*) self;
}

static void
printtup_setup(DestReceiver* self, TupleDesc typeinfo)
{
	/* ----------------
	 * We could set up the derived attr info at this time, but we postpone it
	 * until the first call of printtup, for 3 reasons:
	 * 1. We don't waste time (compared to the old way) if there are no
	 *    tuples at all to output.
	 * 2. Checking in printtup allows us to handle the case that the tuples
	 *    change type midway through (although this probably can't happen in
	 *    the current executor).
	 * 3. Right now, ExecutorRun passes a NULL for typeinfo anyway :-(
	 * ----------------
	 */
}

static void
printtup_prepare_info(DR_printtup* myState, TupleDesc typeinfo, int numAttrs)
{
	int i;

	if (myState->myinfo)
		pfree(myState->myinfo);	/* get rid of any old data */
	myState->myinfo = NULL;
	myState->attrinfo = typeinfo;
	myState->nattrs = numAttrs;
	if (numAttrs <= 0)
		return;
	myState->myinfo = (PrinttupAttrInfo*)
		palloc(numAttrs * sizeof(PrinttupAttrInfo));
	for (i = 0; i < numAttrs; i++)
	{
		PrinttupAttrInfo* thisState = myState->myinfo + i;
143 144 145
		if (getTypeOutAndElem((Oid) typeinfo->attrs[i]->atttypid,
							  &thisState->typoutput, &thisState->typelem))
			fmgr_info(thisState->typoutput, &thisState->finfo);
146 147 148
	}
}

149
/* ----------------
150
 *		printtup
151 152
 * ----------------
 */
153 154
static void
printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self)
155
{
156
	DR_printtup *myState = (DR_printtup*) self;
157
	StringInfoData buf;
158 159
	int			i,
				j,
160 161
				k,
				outputlen;
162 163
	char	   *outputstr;
	Datum		attr;
164
	bool		isnull;
165
#ifdef MULTIBYTE
166 167
	unsigned char *p;
#endif
168

169 170 171 172 173
	/* Set or update my derived attribute info, if needed */
	if (myState->attrinfo != typeinfo ||
		myState->nattrs != tuple->t_data->t_natts)
		printtup_prepare_info(myState, typeinfo, tuple->t_data->t_natts);

174
	/* ----------------
175
	 *	tell the frontend to expect new tuple data (in ASCII style)
176 177
	 * ----------------
	 */
178 179
	pq_beginmessage(&buf);
	pq_sendbyte(&buf, 'D');
180 181

	/* ----------------
182
	 *	send a bitmap of which attributes are not null
183 184 185 186
	 * ----------------
	 */
	j = 0;
	k = 1 << 7;
187
	for (i = 0; i < tuple->t_data->t_natts; ++i)
188
	{
189 190
		if (! heap_attisnull(tuple, i + 1))
			j |= k;				/* set bit if not null */
191
		k >>= 1;
192
		if (k == 0)				/* end of byte? */
193
		{
194
			pq_sendint(&buf, j, 1);
195 196 197
			j = 0;
			k = 1 << 7;
		}
198
	}
199
	if (k != (1 << 7))			/* flush last partial byte */
200
		pq_sendint(&buf, j, 1);
201 202 203 204 205

	/* ----------------
	 *	send the attributes of this tuple
	 * ----------------
	 */
206
	for (i = 0; i < tuple->t_data->t_natts; ++i)
207
	{
208
		PrinttupAttrInfo* thisState = myState->myinfo + i;
209
		attr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
M
 
Marc G. Fournier 已提交
210 211
		if (isnull)
			continue;
212
		if (OidIsValid(thisState->typoutput))
213
		{
214 215
			outputstr = (char *) (*fmgr_faddr(&thisState->finfo))
				(attr, thisState->typelem, typeinfo->attrs[i]->atttypmod);
216
#ifdef MULTIBYTE
217
			p = pg_server_to_client(outputstr, strlen(outputstr));
218
			outputlen = strlen(p);
219 220
			pq_sendint(&buf, outputlen + VARHDRSZ, VARHDRSZ);
			pq_sendbytes(&buf, p, outputlen);
221
#else
222
			outputlen = strlen(outputstr);
223 224
			pq_sendint(&buf, outputlen + VARHDRSZ, VARHDRSZ);
			pq_sendbytes(&buf, outputstr, outputlen);
225
#endif
226 227
			pfree(outputstr);
		}
M
 
Marc G. Fournier 已提交
228 229 230
		else
		{
			outputstr = "<unprintable>";
231
			outputlen = strlen(outputstr);
232 233
			pq_sendint(&buf, outputlen + VARHDRSZ, VARHDRSZ);
			pq_sendbytes(&buf, outputstr, outputlen);
234
		}
235
	}
236 237

	pq_endmessage(&buf);
238 239
}

240 241 242 243 244 245 246 247 248 249 250 251 252
/* ----------------
 *		printtup_cleanup
 * ----------------
 */
static void
printtup_cleanup(DestReceiver* self)
{
	DR_printtup* myState = (DR_printtup*) self;
	if (myState->myinfo)
		pfree(myState->myinfo);
	pfree(myState);
}

253
/* ----------------
254
 *		printatt
255 256 257 258
 * ----------------
 */
static void
printatt(unsigned attributeId,
259
		 Form_pg_attribute attributeP,
260
		 char *value)
261
{
B
Bruce Momjian 已提交
262
	printf("\t%2d: %s%s%s%s\t(typeid = %u, len = %d, typmod = %d, byval = %c)\n",
263 264 265 266 267 268 269
		   attributeId,
		   attributeP->attname.data,
		   value != NULL ? " = \"" : "",
		   value != NULL ? value : "",
		   value != NULL ? "\"" : "",
		   (unsigned int) (attributeP->atttypid),
		   attributeP->attlen,
B
Bruce Momjian 已提交
270
		   attributeP->atttypmod,
271
		   attributeP->attbyval ? 't' : 'f');
272 273 274
}

/* ----------------
275
 *		showatts
276 277 278 279 280
 * ----------------
 */
void
showatts(char *name, TupleDesc tupleDesc)
{
281 282
	int			i;
	int			natts = tupleDesc->natts;
283
	Form_pg_attribute *attinfo = tupleDesc->attrs;
284

285 286 287 288
	puts(name);
	for (i = 0; i < natts; ++i)
		printatt((unsigned) i + 1, attinfo[i], (char *) NULL);
	printf("\t----\n");
289 290 291
}

/* ----------------
292
 *		debugtup
293 294 295
 * ----------------
 */
void
296
debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self)
297
{
298 299
	int			i;
	Datum		attr;
300
	char	   *value;
301
	bool		isnull;
302 303
	Oid			typoutput,
				typelem;
304

305
	for (i = 0; i < tuple->t_data->t_natts; ++i)
306
	{
307
		attr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
308 309 310 311
		if (isnull)
			continue;
		if (getTypeOutAndElem((Oid) typeinfo->attrs[i]->atttypid,
							  &typoutput, &typelem))
312
		{
313
			value = fmgr(typoutput, attr, typelem,
314
						 typeinfo->attrs[i]->atttypmod);
315 316 317
			printatt((unsigned) i + 1, typeinfo->attrs[i], value);
			pfree(value);
		}
318
	}
319
	printf("\t----\n");
320 321 322
}

/* ----------------
323 324 325
 *		printtup_internal
 *		We use a different data prefix, e.g. 'B' instead of 'D' to
 *		indicate a tuple in internal (binary) form.
326
 *
327 328
 *		This is same as printtup, except we don't use the typout func,
 *		and therefore have no need for persistent state.
329 330 331
 * ----------------
 */
void
332
printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self)
333
{
334
	StringInfoData buf;
335 336 337
	int			i,
				j,
				k;
338
	Datum		attr;
339
	bool		isnull;
340 341

	/* ----------------
342
	 *	tell the frontend to expect new tuple data (in binary style)
343 344
	 * ----------------
	 */
345 346
	pq_beginmessage(&buf);
	pq_sendbyte(&buf, 'B');
347 348

	/* ----------------
349
	 *	send a bitmap of which attributes are not null
350 351 352 353
	 * ----------------
	 */
	j = 0;
	k = 1 << 7;
354
	for (i = 0; i < tuple->t_data->t_natts; ++i)
355
	{
356 357
		if (! heap_attisnull(tuple, i + 1))
			j |= k;				/* set bit if not null */
358
		k >>= 1;
359
		if (k == 0)				/* end of byte? */
360
		{
361
			pq_sendint(&buf, j, 1);
362 363 364
			j = 0;
			k = 1 << 7;
		}
365
	}
366
	if (k != (1 << 7))			/* flush last partial byte */
367
		pq_sendint(&buf, j, 1);
368 369 370 371 372

	/* ----------------
	 *	send the attributes of this tuple
	 * ----------------
	 */
373
#ifdef IPORTAL_DEBUG
374
	fprintf(stderr, "sending tuple with %d atts\n", tuple->t_data->t_natts);
375
#endif
376
	for (i = 0; i < tuple->t_data->t_natts; ++i)
377
	{
378
		int32		len = typeinfo->attrs[i]->attlen;
379

380
		attr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
381
		if (!isnull)
382
		{
383 384 385 386 387 388
			/* # of bytes, and opaque data */
			if (len == -1)
			{
				/* variable length, assume a varlena structure */
				len = VARSIZE(attr) - VARHDRSZ;

389 390
				pq_sendint(&buf, len, VARHDRSZ);
				pq_sendbytes(&buf, VARDATA(attr), len);
391

392 393
#ifdef IPORTAL_DEBUG
				{
394
					char	   *d = VARDATA(attr);
395 396 397 398

					fprintf(stderr, "length %d data %x%x%x%x\n",
							len, *d, *(d + 1), *(d + 2), *(d + 3));
				}
399
#endif
400 401 402 403 404 405
			}
			else
			{
				/* fixed size */
				if (typeinfo->attrs[i]->attbyval)
				{
406 407 408
					int8		i8;
					int16		i16;
					int32		i32;
409

410
					pq_sendint(&buf, len, sizeof(int32));
411 412
					switch (len)
					{
413 414
						case sizeof(int8):
							i8 = DatumGetChar(attr);
415
							pq_sendbytes(&buf, (char *) &i8, len);
416 417 418
							break;
						case sizeof(int16):
							i16 = DatumGetInt16(attr);
419
							pq_sendbytes(&buf, (char *) &i16, len);
420 421 422
							break;
						case sizeof(int32):
							i32 = DatumGetInt32(attr);
423
							pq_sendbytes(&buf, (char *) &i32, len);
424
							break;
425
					}
426
#ifdef IPORTAL_DEBUG
427
					fprintf(stderr, "byval length %d data %d\n", len, attr);
428
#endif
429 430 431
				}
				else
				{
432 433
					pq_sendint(&buf, len, sizeof(int32));
					pq_sendbytes(&buf, DatumGetPointer(attr), len);
434
#ifdef IPORTAL_DEBUG
435
					fprintf(stderr, "byref length %d data %x\n", len,
436
							DatumGetPointer(attr));
437
#endif
438 439
				}
			}
440 441
		}
	}
442 443

	pq_endmessage(&buf);
444
}