]> granicus.if.org Git - postgresql/blob - src/backend/access/common/printtup.c
My first cut at libpq revision didn't handle MULTIBYTE correctly,
[postgresql] / src / backend / access / common / printtup.c
1 /*-------------------------------------------------------------------------
2  *
3  * printtup.c
4  *        Routines to print out tuples to the destination (binary or non-binary
5  *        portals, frontend/interactive backend, etc.).
6  *
7  * Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.44 1999/04/25 19:27:43 tgl Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15
16 #include <string.h>
17
18 #include "postgres.h"
19
20 #include "fmgr.h"
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"
27
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);
31
32 /* ----------------------------------------------------------------
33  *              printtup / debugtup support
34  * ----------------------------------------------------------------
35  */
36
37 /* ----------------
38  *              getTypeOutAndElem -- get both typoutput and typelem for a type
39  *
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.
43  * ----------------
44  */
45 int
46 getTypeOutAndElem(Oid type, Oid* typOutput, Oid* typElem)
47 {
48         HeapTuple       typeTuple;
49
50         typeTuple = SearchSysCacheTuple(TYPOID,
51                                                                         ObjectIdGetDatum(type),
52                                                                         0, 0, 0);
53
54         if (HeapTupleIsValid(typeTuple))
55         {
56                 Form_pg_type pt = (Form_pg_type) GETSTRUCT(typeTuple);
57                 *typOutput = (Oid) pt->typoutput;
58                 *typElem = (Oid) pt->typelem;
59                 return OidIsValid(*typOutput);
60         }
61
62         elog(ERROR, "getTypeOutAndElem: Cache lookup of type %d failed", type);
63
64         *typOutput = InvalidOid;
65         *typElem = InvalidOid;
66         return 0;
67 }
68
69 /* ----------------
70  *              Private state for a printtup destination object
71  * ----------------
72  */
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 */
77 } PrinttupAttrInfo;
78
79 typedef struct {
80         DestReceiver            pub;            /* publicly-known function pointers */
81         TupleDesc                       attrinfo;       /* The attr info we are set up for */
82         int                                     nattrs;
83         PrinttupAttrInfo   *myinfo;             /* Cached info about each attr */
84 } DR_printtup;
85
86 /* ----------------
87  *              Initialize: create a DestReceiver for printtup
88  * ----------------
89  */
90 DestReceiver*
91 printtup_create_DR()
92 {
93         DR_printtup* self = (DR_printtup*) palloc(sizeof(DR_printtup));
94
95         self->pub.receiveTuple = printtup;
96         self->pub.setup = printtup_setup;
97         self->pub.cleanup = printtup_cleanup;
98
99         self->attrinfo = NULL;
100         self->nattrs = 0;
101         self->myinfo = NULL;
102
103         return (DestReceiver*) self;
104 }
105
106 static void
107 printtup_setup(DestReceiver* self, TupleDesc typeinfo)
108 {
109         /* ----------------
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 :-(
118          * ----------------
119          */
120 }
121
122 static void
123 printtup_prepare_info(DR_printtup* myState, TupleDesc typeinfo, int numAttrs)
124 {
125         int i;
126
127         if (myState->myinfo)
128                 pfree(myState->myinfo); /* get rid of any old data */
129         myState->myinfo = NULL;
130         myState->attrinfo = typeinfo;
131         myState->nattrs = numAttrs;
132         if (numAttrs <= 0)
133                 return;
134         myState->myinfo = (PrinttupAttrInfo*)
135                 palloc(numAttrs * sizeof(PrinttupAttrInfo));
136         for (i = 0; i < numAttrs; i++)
137         {
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);
142         }
143 }
144
145 /* ----------------
146  *              printtup
147  * ----------------
148  */
149 static void
150 printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self)
151 {
152         DR_printtup *myState = (DR_printtup*) self;
153         StringInfoData buf;
154         int                     i,
155                                 j,
156                                 k;
157         char       *outputstr;
158         Datum           attr;
159         bool            isnull;
160
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);
165
166         /* ----------------
167          *      tell the frontend to expect new tuple data (in ASCII style)
168          * ----------------
169          */
170         pq_beginmessage(&buf);
171         pq_sendbyte(&buf, 'D');
172
173         /* ----------------
174          *      send a bitmap of which attributes are not null
175          * ----------------
176          */
177         j = 0;
178         k = 1 << 7;
179         for (i = 0; i < tuple->t_data->t_natts; ++i)
180         {
181                 if (! heap_attisnull(tuple, i + 1))
182                         j |= k;                         /* set bit if not null */
183                 k >>= 1;
184                 if (k == 0)                             /* end of byte? */
185                 {
186                         pq_sendint(&buf, j, 1);
187                         j = 0;
188                         k = 1 << 7;
189                 }
190         }
191         if (k != (1 << 7))                      /* flush last partial byte */
192                 pq_sendint(&buf, j, 1);
193
194         /* ----------------
195          *      send the attributes of this tuple
196          * ----------------
197          */
198         for (i = 0; i < tuple->t_data->t_natts; ++i)
199         {
200                 PrinttupAttrInfo* thisState = myState->myinfo + i;
201                 attr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
202                 if (isnull)
203                         continue;
204                 if (OidIsValid(thisState->typoutput))
205                 {
206                         outputstr = (char *) (*fmgr_faddr(&thisState->finfo))
207                                 (attr, thisState->typelem, typeinfo->attrs[i]->atttypmod);
208                         pq_sendcountedtext(&buf, outputstr, strlen(outputstr));
209                         pfree(outputstr);
210                 }
211                 else
212                 {
213                         outputstr = "<unprintable>";
214                         pq_sendcountedtext(&buf, outputstr, strlen(outputstr));
215                 }
216         }
217
218         pq_endmessage(&buf);
219 }
220
221 /* ----------------
222  *              printtup_cleanup
223  * ----------------
224  */
225 static void
226 printtup_cleanup(DestReceiver* self)
227 {
228         DR_printtup* myState = (DR_printtup*) self;
229         if (myState->myinfo)
230                 pfree(myState->myinfo);
231         pfree(myState);
232 }
233
234 /* ----------------
235  *              printatt
236  * ----------------
237  */
238 static void
239 printatt(unsigned attributeId,
240                  Form_pg_attribute attributeP,
241                  char *value)
242 {
243         printf("\t%2d: %s%s%s%s\t(typeid = %u, len = %d, typmod = %d, byval = %c)\n",
244                    attributeId,
245                    attributeP->attname.data,
246                    value != NULL ? " = \"" : "",
247                    value != NULL ? value : "",
248                    value != NULL ? "\"" : "",
249                    (unsigned int) (attributeP->atttypid),
250                    attributeP->attlen,
251                    attributeP->atttypmod,
252                    attributeP->attbyval ? 't' : 'f');
253 }
254
255 /* ----------------
256  *              showatts
257  * ----------------
258  */
259 void
260 showatts(char *name, TupleDesc tupleDesc)
261 {
262         int                     i;
263         int                     natts = tupleDesc->natts;
264         Form_pg_attribute *attinfo = tupleDesc->attrs;
265
266         puts(name);
267         for (i = 0; i < natts; ++i)
268                 printatt((unsigned) i + 1, attinfo[i], (char *) NULL);
269         printf("\t----\n");
270 }
271
272 /* ----------------
273  *              debugtup
274  * ----------------
275  */
276 void
277 debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self)
278 {
279         int                     i;
280         Datum           attr;
281         char       *value;
282         bool            isnull;
283         Oid                     typoutput,
284                                 typelem;
285
286         for (i = 0; i < tuple->t_data->t_natts; ++i)
287         {
288                 attr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
289                 if (isnull)
290                         continue;
291                 if (getTypeOutAndElem((Oid) typeinfo->attrs[i]->atttypid,
292                                                           &typoutput, &typelem))
293                 {
294                         value = fmgr(typoutput, attr, typelem,
295                                                  typeinfo->attrs[i]->atttypmod);
296                         printatt((unsigned) i + 1, typeinfo->attrs[i], value);
297                         pfree(value);
298                 }
299         }
300         printf("\t----\n");
301 }
302
303 /* ----------------
304  *              printtup_internal
305  *              We use a different data prefix, e.g. 'B' instead of 'D' to
306  *              indicate a tuple in internal (binary) form.
307  *
308  *              This is same as printtup, except we don't use the typout func,
309  *              and therefore have no need for persistent state.
310  * ----------------
311  */
312 void
313 printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self)
314 {
315         StringInfoData buf;
316         int                     i,
317                                 j,
318                                 k;
319         Datum           attr;
320         bool            isnull;
321
322         /* ----------------
323          *      tell the frontend to expect new tuple data (in binary style)
324          * ----------------
325          */
326         pq_beginmessage(&buf);
327         pq_sendbyte(&buf, 'B');
328
329         /* ----------------
330          *      send a bitmap of which attributes are not null
331          * ----------------
332          */
333         j = 0;
334         k = 1 << 7;
335         for (i = 0; i < tuple->t_data->t_natts; ++i)
336         {
337                 if (! heap_attisnull(tuple, i + 1))
338                         j |= k;                         /* set bit if not null */
339                 k >>= 1;
340                 if (k == 0)                             /* end of byte? */
341                 {
342                         pq_sendint(&buf, j, 1);
343                         j = 0;
344                         k = 1 << 7;
345                 }
346         }
347         if (k != (1 << 7))                      /* flush last partial byte */
348                 pq_sendint(&buf, j, 1);
349
350         /* ----------------
351          *      send the attributes of this tuple
352          * ----------------
353          */
354 #ifdef IPORTAL_DEBUG
355         fprintf(stderr, "sending tuple with %d atts\n", tuple->t_data->t_natts);
356 #endif
357         for (i = 0; i < tuple->t_data->t_natts; ++i)
358         {
359                 int32           len = typeinfo->attrs[i]->attlen;
360
361                 attr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
362                 if (!isnull)
363                 {
364                         /* # of bytes, and opaque data */
365                         if (len == -1)
366                         {
367                                 /* variable length, assume a varlena structure */
368                                 len = VARSIZE(attr) - VARHDRSZ;
369
370                                 pq_sendint(&buf, len, VARHDRSZ);
371                                 pq_sendbytes(&buf, VARDATA(attr), len);
372
373 #ifdef IPORTAL_DEBUG
374                                 {
375                                         char       *d = VARDATA(attr);
376
377                                         fprintf(stderr, "length %d data %x%x%x%x\n",
378                                                         len, *d, *(d + 1), *(d + 2), *(d + 3));
379                                 }
380 #endif
381                         }
382                         else
383                         {
384                                 /* fixed size */
385                                 if (typeinfo->attrs[i]->attbyval)
386                                 {
387                                         int8            i8;
388                                         int16           i16;
389                                         int32           i32;
390
391                                         pq_sendint(&buf, len, sizeof(int32));
392                                         switch (len)
393                                         {
394                                                 case sizeof(int8):
395                                                         i8 = DatumGetChar(attr);
396                                                         pq_sendbytes(&buf, (char *) &i8, len);
397                                                         break;
398                                                 case sizeof(int16):
399                                                         i16 = DatumGetInt16(attr);
400                                                         pq_sendbytes(&buf, (char *) &i16, len);
401                                                         break;
402                                                 case sizeof(int32):
403                                                         i32 = DatumGetInt32(attr);
404                                                         pq_sendbytes(&buf, (char *) &i32, len);
405                                                         break;
406                                         }
407 #ifdef IPORTAL_DEBUG
408                                         fprintf(stderr, "byval length %d data %d\n", len, attr);
409 #endif
410                                 }
411                                 else
412                                 {
413                                         pq_sendint(&buf, len, sizeof(int32));
414                                         pq_sendbytes(&buf, DatumGetPointer(attr), len);
415 #ifdef IPORTAL_DEBUG
416                                         fprintf(stderr, "byref length %d data %x\n", len,
417                                                         DatumGetPointer(attr));
418 #endif
419                                 }
420                         }
421                 }
422         }
423
424         pq_endmessage(&buf);
425 }