1 /*-------------------------------------------------------------------------
4 * Routines to print out tuples to the destination (binary or non-binary
5 * portals, frontend/interactive backend, etc.).
7 * Copyright (c) 1994, Regents of the University of California
11 * $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.44 1999/04/25 19:27:43 tgl Exp $
13 *-------------------------------------------------------------------------
21 #include "access/heapam.h"
22 #include "access/printtup.h"
23 #include "catalog/pg_type.h"
24 #include "libpq/libpq.h"
25 #include "libpq/pqformat.h"
26 #include "utils/syscache.h"
28 static void printtup_setup(DestReceiver* self, TupleDesc typeinfo);
29 static void printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self);
30 static void printtup_cleanup(DestReceiver* self);
32 /* ----------------------------------------------------------------
33 * printtup / debugtup support
34 * ----------------------------------------------------------------
38 * getTypeOutAndElem -- get both typoutput and typelem for a type
40 * We used to fetch these with two separate function calls,
41 * typtoout() and gettypelem(), which each called SearchSysCacheTuple.
42 * This way takes half the time.
46 getTypeOutAndElem(Oid type, Oid* typOutput, Oid* typElem)
50 typeTuple = SearchSysCacheTuple(TYPOID,
51 ObjectIdGetDatum(type),
54 if (HeapTupleIsValid(typeTuple))
56 Form_pg_type pt = (Form_pg_type) GETSTRUCT(typeTuple);
57 *typOutput = (Oid) pt->typoutput;
58 *typElem = (Oid) pt->typelem;
59 return OidIsValid(*typOutput);
62 elog(ERROR, "getTypeOutAndElem: Cache lookup of type %d failed", type);
64 *typOutput = InvalidOid;
65 *typElem = InvalidOid;
70 * Private state for a printtup destination object
73 typedef struct { /* Per-attribute information */
74 Oid typoutput; /* Oid for the attribute's type output fn */
75 Oid typelem; /* typelem value to pass to the output fn */
76 FmgrInfo finfo; /* Precomputed call info for typoutput */
80 DestReceiver pub; /* publicly-known function pointers */
81 TupleDesc attrinfo; /* The attr info we are set up for */
83 PrinttupAttrInfo *myinfo; /* Cached info about each attr */
87 * Initialize: create a DestReceiver for printtup
93 DR_printtup* self = (DR_printtup*) palloc(sizeof(DR_printtup));
95 self->pub.receiveTuple = printtup;
96 self->pub.setup = printtup_setup;
97 self->pub.cleanup = printtup_cleanup;
99 self->attrinfo = NULL;
103 return (DestReceiver*) self;
107 printtup_setup(DestReceiver* self, TupleDesc typeinfo)
110 * We could set up the derived attr info at this time, but we postpone it
111 * until the first call of printtup, for 3 reasons:
112 * 1. We don't waste time (compared to the old way) if there are no
113 * tuples at all to output.
114 * 2. Checking in printtup allows us to handle the case that the tuples
115 * change type midway through (although this probably can't happen in
116 * the current executor).
117 * 3. Right now, ExecutorRun passes a NULL for typeinfo anyway :-(
123 printtup_prepare_info(DR_printtup* myState, TupleDesc typeinfo, int numAttrs)
128 pfree(myState->myinfo); /* get rid of any old data */
129 myState->myinfo = NULL;
130 myState->attrinfo = typeinfo;
131 myState->nattrs = numAttrs;
134 myState->myinfo = (PrinttupAttrInfo*)
135 palloc(numAttrs * sizeof(PrinttupAttrInfo));
136 for (i = 0; i < numAttrs; i++)
138 PrinttupAttrInfo* thisState = myState->myinfo + i;
139 if (getTypeOutAndElem((Oid) typeinfo->attrs[i]->atttypid,
140 &thisState->typoutput, &thisState->typelem))
141 fmgr_info(thisState->typoutput, &thisState->finfo);
150 printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self)
152 DR_printtup *myState = (DR_printtup*) self;
161 /* Set or update my derived attribute info, if needed */
162 if (myState->attrinfo != typeinfo ||
163 myState->nattrs != tuple->t_data->t_natts)
164 printtup_prepare_info(myState, typeinfo, tuple->t_data->t_natts);
167 * tell the frontend to expect new tuple data (in ASCII style)
170 pq_beginmessage(&buf);
171 pq_sendbyte(&buf, 'D');
174 * send a bitmap of which attributes are not null
179 for (i = 0; i < tuple->t_data->t_natts; ++i)
181 if (! heap_attisnull(tuple, i + 1))
182 j |= k; /* set bit if not null */
184 if (k == 0) /* end of byte? */
186 pq_sendint(&buf, j, 1);
191 if (k != (1 << 7)) /* flush last partial byte */
192 pq_sendint(&buf, j, 1);
195 * send the attributes of this tuple
198 for (i = 0; i < tuple->t_data->t_natts; ++i)
200 PrinttupAttrInfo* thisState = myState->myinfo + i;
201 attr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
204 if (OidIsValid(thisState->typoutput))
206 outputstr = (char *) (*fmgr_faddr(&thisState->finfo))
207 (attr, thisState->typelem, typeinfo->attrs[i]->atttypmod);
208 pq_sendcountedtext(&buf, outputstr, strlen(outputstr));
213 outputstr = "<unprintable>";
214 pq_sendcountedtext(&buf, outputstr, strlen(outputstr));
226 printtup_cleanup(DestReceiver* self)
228 DR_printtup* myState = (DR_printtup*) self;
230 pfree(myState->myinfo);
239 printatt(unsigned attributeId,
240 Form_pg_attribute attributeP,
243 printf("\t%2d: %s%s%s%s\t(typeid = %u, len = %d, typmod = %d, byval = %c)\n",
245 attributeP->attname.data,
246 value != NULL ? " = \"" : "",
247 value != NULL ? value : "",
248 value != NULL ? "\"" : "",
249 (unsigned int) (attributeP->atttypid),
251 attributeP->atttypmod,
252 attributeP->attbyval ? 't' : 'f');
260 showatts(char *name, TupleDesc tupleDesc)
263 int natts = tupleDesc->natts;
264 Form_pg_attribute *attinfo = tupleDesc->attrs;
267 for (i = 0; i < natts; ++i)
268 printatt((unsigned) i + 1, attinfo[i], (char *) NULL);
277 debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self)
286 for (i = 0; i < tuple->t_data->t_natts; ++i)
288 attr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
291 if (getTypeOutAndElem((Oid) typeinfo->attrs[i]->atttypid,
292 &typoutput, &typelem))
294 value = fmgr(typoutput, attr, typelem,
295 typeinfo->attrs[i]->atttypmod);
296 printatt((unsigned) i + 1, typeinfo->attrs[i], value);
305 * We use a different data prefix, e.g. 'B' instead of 'D' to
306 * indicate a tuple in internal (binary) form.
308 * This is same as printtup, except we don't use the typout func,
309 * and therefore have no need for persistent state.
313 printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self)
323 * tell the frontend to expect new tuple data (in binary style)
326 pq_beginmessage(&buf);
327 pq_sendbyte(&buf, 'B');
330 * send a bitmap of which attributes are not null
335 for (i = 0; i < tuple->t_data->t_natts; ++i)
337 if (! heap_attisnull(tuple, i + 1))
338 j |= k; /* set bit if not null */
340 if (k == 0) /* end of byte? */
342 pq_sendint(&buf, j, 1);
347 if (k != (1 << 7)) /* flush last partial byte */
348 pq_sendint(&buf, j, 1);
351 * send the attributes of this tuple
355 fprintf(stderr, "sending tuple with %d atts\n", tuple->t_data->t_natts);
357 for (i = 0; i < tuple->t_data->t_natts; ++i)
359 int32 len = typeinfo->attrs[i]->attlen;
361 attr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
364 /* # of bytes, and opaque data */
367 /* variable length, assume a varlena structure */
368 len = VARSIZE(attr) - VARHDRSZ;
370 pq_sendint(&buf, len, VARHDRSZ);
371 pq_sendbytes(&buf, VARDATA(attr), len);
375 char *d = VARDATA(attr);
377 fprintf(stderr, "length %d data %x%x%x%x\n",
378 len, *d, *(d + 1), *(d + 2), *(d + 3));
385 if (typeinfo->attrs[i]->attbyval)
391 pq_sendint(&buf, len, sizeof(int32));
395 i8 = DatumGetChar(attr);
396 pq_sendbytes(&buf, (char *) &i8, len);
399 i16 = DatumGetInt16(attr);
400 pq_sendbytes(&buf, (char *) &i16, len);
403 i32 = DatumGetInt32(attr);
404 pq_sendbytes(&buf, (char *) &i32, len);
408 fprintf(stderr, "byval length %d data %d\n", len, attr);
413 pq_sendint(&buf, len, sizeof(int32));
414 pq_sendbytes(&buf, DatumGetPointer(attr), len);
416 fprintf(stderr, "byref length %d data %x\n", len,
417 DatumGetPointer(attr));