]> granicus.if.org Git - postgresql/blob - src/backend/access/common/printtup.c
Extended query protocol: parse, bind, execute, describe FE/BE messages.
[postgresql] / src / backend / access / common / printtup.c
1 /*-------------------------------------------------------------------------
2  *
3  * printtup.c
4  *        Routines to print out tuples to the destination (both frontend
5  *        clients and interactive backends are supported here).
6  *
7  *
8  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
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.68 2003/05/05 00:44:55 tgl Exp $
13  *
14  *-------------------------------------------------------------------------
15  */
16 #include "postgres.h"
17
18 #include "access/heapam.h"
19 #include "access/printtup.h"
20 #include "libpq/libpq.h"
21 #include "libpq/pqformat.h"
22 #include "utils/lsyscache.h"
23
24
25 static void printtup_setup(DestReceiver *self, int operation,
26                            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  *              Private state for a printtup destination object
38  * ----------------
39  */
40 typedef struct
41 {                                                               /* Per-attribute information */
42         Oid                     typoutput;              /* Oid for the attribute's type output fn */
43         Oid                     typelem;                /* typelem value to pass to the output fn */
44         bool            typisvarlena;   /* is it varlena (ie possibly toastable)? */
45         FmgrInfo        finfo;                  /* Precomputed call info for typoutput */
46 } PrinttupAttrInfo;
47
48 typedef struct
49 {
50         DestReceiver pub;                       /* publicly-known function pointers */
51         bool            sendDescrip;    /* send RowDescription at startup? */
52         TupleDesc       attrinfo;               /* The attr info we are set up for */
53         int                     nattrs;
54         PrinttupAttrInfo *myinfo;       /* Cached info about each attr */
55 } DR_printtup;
56
57 /* ----------------
58  *              Initialize: create a DestReceiver for printtup
59  * ----------------
60  */
61 DestReceiver *
62 printtup_create_DR(bool isBinary, bool sendDescrip)
63 {
64         DR_printtup *self = (DR_printtup *) palloc(sizeof(DR_printtup));
65
66         self->pub.receiveTuple = isBinary ? printtup_internal : printtup;
67         self->pub.setup = printtup_setup;
68         self->pub.cleanup = printtup_cleanup;
69
70         self->sendDescrip = sendDescrip;
71
72         self->attrinfo = NULL;
73         self->nattrs = 0;
74         self->myinfo = NULL;
75
76         return (DestReceiver *) self;
77 }
78
79 static void
80 printtup_setup(DestReceiver *self, int operation,
81                            const char *portalName, TupleDesc typeinfo)
82 {
83         DR_printtup *myState = (DR_printtup *) self;
84
85         if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
86         {
87                 /*
88                  * Send portal name to frontend (obsolete cruft, gone in proto 3.0)
89                  *
90                  * If portal name not specified, use "blank" portal.
91                  */
92                 if (portalName == NULL)
93                         portalName = "blank";
94
95                 pq_puttextmessage('P', portalName);
96         }
97
98         /*
99          * If this is a retrieve, and we are supposed to emit row descriptions,
100          * then we send back the tuple descriptor of the tuples.  
101          */
102         if (operation == CMD_SELECT && myState->sendDescrip)
103                 SendRowDescriptionMessage(typeinfo);
104
105         /* ----------------
106          * We could set up the derived attr info at this time, but we postpone it
107          * until the first call of printtup, for 2 reasons:
108          * 1. We don't waste time (compared to the old way) if there are no
109          *        tuples at all to output.
110          * 2. Checking in printtup allows us to handle the case that the tuples
111          *        change type midway through (although this probably can't happen in
112          *        the current executor).
113          * ----------------
114          */
115 }
116
117 /*
118  * SendRowDescriptionMessage --- send a RowDescription message to the frontend
119  */
120 void
121 SendRowDescriptionMessage(TupleDesc typeinfo)
122 {
123         Form_pg_attribute *attrs = typeinfo->attrs;
124         int                     natts = typeinfo->natts;
125         int                     proto = PG_PROTOCOL_MAJOR(FrontendProtocol);
126         int                     i;
127         StringInfoData buf;
128
129         pq_beginmessage(&buf, 'T');             /* tuple descriptor message type */
130         pq_sendint(&buf, natts, 2);             /* # of attrs in tuples */
131
132         for (i = 0; i < natts; ++i)
133         {
134                 pq_sendstring(&buf, NameStr(attrs[i]->attname));
135                 /* column ID info appears in protocol 3.0 and up */
136                 if (proto >= 3)
137                 {
138                         /* XXX not yet implemented, send zeroes */
139                         pq_sendint(&buf, 0, 4);
140                         pq_sendint(&buf, 0, 2);
141                 }
142                 pq_sendint(&buf, (int) attrs[i]->atttypid,
143                                    sizeof(attrs[i]->atttypid));
144                 pq_sendint(&buf, attrs[i]->attlen,
145                                    sizeof(attrs[i]->attlen));
146                 /* typmod appears in protocol 2.0 and up */
147                 if (proto >= 2)
148                         pq_sendint(&buf, attrs[i]->atttypmod,
149                                            sizeof(attrs[i]->atttypmod));
150         }
151         pq_endmessage(&buf);
152 }
153
154 static void
155 printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
156 {
157         int                     i;
158
159         if (myState->myinfo)
160                 pfree(myState->myinfo); /* get rid of any old data */
161         myState->myinfo = NULL;
162         myState->attrinfo = typeinfo;
163         myState->nattrs = numAttrs;
164         if (numAttrs <= 0)
165                 return;
166         myState->myinfo = (PrinttupAttrInfo *)
167                 palloc(numAttrs * sizeof(PrinttupAttrInfo));
168         for (i = 0; i < numAttrs; i++)
169         {
170                 PrinttupAttrInfo *thisState = myState->myinfo + i;
171
172                 if (getTypeOutputInfo(typeinfo->attrs[i]->atttypid,
173                                                           &thisState->typoutput, &thisState->typelem,
174                                                           &thisState->typisvarlena))
175                         fmgr_info(thisState->typoutput, &thisState->finfo);
176         }
177 }
178
179 /* ----------------
180  *              printtup
181  * ----------------
182  */
183 static void
184 printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
185 {
186         DR_printtup *myState = (DR_printtup *) self;
187         StringInfoData buf;
188         int                     natts = tuple->t_data->t_natts;
189         int                     i,
190                                 j,
191                                 k;
192
193         /* Set or update my derived attribute info, if needed */
194         if (myState->attrinfo != typeinfo || myState->nattrs != natts)
195                 printtup_prepare_info(myState, typeinfo, natts);
196
197         /*
198          * tell the frontend to expect new tuple data (in ASCII style)
199          */
200         pq_beginmessage(&buf, 'D');
201
202         /*
203          * send a bitmap of which attributes are not null
204          */
205         j = 0;
206         k = 1 << 7;
207         for (i = 0; i < natts; ++i)
208         {
209                 if (!heap_attisnull(tuple, i + 1))
210                         j |= k;                         /* set bit if not null */
211                 k >>= 1;
212                 if (k == 0)                             /* end of byte? */
213                 {
214                         pq_sendint(&buf, j, 1);
215                         j = 0;
216                         k = 1 << 7;
217                 }
218         }
219         if (k != (1 << 7))                      /* flush last partial byte */
220                 pq_sendint(&buf, j, 1);
221
222         /*
223          * send the attributes of this tuple
224          */
225         for (i = 0; i < natts; ++i)
226         {
227                 PrinttupAttrInfo *thisState = myState->myinfo + i;
228                 Datum           origattr,
229                                         attr;
230                 bool            isnull;
231                 char       *outputstr;
232
233                 origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
234                 if (isnull)
235                         continue;
236                 if (OidIsValid(thisState->typoutput))
237                 {
238                         /*
239                          * If we have a toasted datum, forcibly detoast it here to
240                          * avoid memory leakage inside the type's output routine.
241                          */
242                         if (thisState->typisvarlena)
243                                 attr = PointerGetDatum(PG_DETOAST_DATUM(origattr));
244                         else
245                                 attr = origattr;
246
247                         outputstr = DatumGetCString(FunctionCall3(&thisState->finfo,
248                                                                                                           attr,
249                                                                         ObjectIdGetDatum(thisState->typelem),
250                                                   Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
251
252                         pq_sendcountedtext(&buf, outputstr, strlen(outputstr));
253
254                         /* Clean up detoasted copy, if any */
255                         if (attr != origattr)
256                                 pfree(DatumGetPointer(attr));
257                         pfree(outputstr);
258                 }
259                 else
260                 {
261                         outputstr = "<unprintable>";
262                         pq_sendcountedtext(&buf, outputstr, strlen(outputstr));
263                 }
264         }
265
266         pq_endmessage(&buf);
267 }
268
269 /* ----------------
270  *              printtup_cleanup
271  * ----------------
272  */
273 static void
274 printtup_cleanup(DestReceiver *self)
275 {
276         DR_printtup *myState = (DR_printtup *) self;
277
278         if (myState->myinfo)
279                 pfree(myState->myinfo);
280         pfree(myState);
281 }
282
283 /* ----------------
284  *              printatt
285  * ----------------
286  */
287 static void
288 printatt(unsigned attributeId,
289                  Form_pg_attribute attributeP,
290                  char *value)
291 {
292         printf("\t%2d: %s%s%s%s\t(typeid = %u, len = %d, typmod = %d, byval = %c)\n",
293                    attributeId,
294                    NameStr(attributeP->attname),
295                    value != NULL ? " = \"" : "",
296                    value != NULL ? value : "",
297                    value != NULL ? "\"" : "",
298                    (unsigned int) (attributeP->atttypid),
299                    attributeP->attlen,
300                    attributeP->atttypmod,
301                    attributeP->attbyval ? 't' : 'f');
302 }
303
304 /* ----------------
305  *              showatts
306  * ----------------
307  */
308 static void
309 showatts(const char *name, TupleDesc tupleDesc)
310 {
311         int                     natts = tupleDesc->natts;
312         Form_pg_attribute *attinfo = tupleDesc->attrs;
313         int                     i;
314
315         puts(name);
316         for (i = 0; i < natts; ++i)
317                 printatt((unsigned) i + 1, attinfo[i], (char *) NULL);
318         printf("\t----\n");
319 }
320
321 /* ----------------
322  *              debugSetup - prepare to print tuples for an interactive backend
323  * ----------------
324  */
325 void
326 debugSetup(DestReceiver *self, int operation,
327                    const char *portalName, TupleDesc typeinfo)
328 {
329         /*
330          * show the return type of the tuples
331          */
332         if (portalName == NULL)
333                 portalName = "blank";
334
335         showatts(portalName, typeinfo);
336 }
337
338 /* ----------------
339  *              debugtup - print one tuple for an interactive backend
340  * ----------------
341  */
342 void
343 debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
344 {
345         int                     natts = tuple->t_data->t_natts;
346         int                     i;
347         Datum           origattr,
348                                 attr;
349         char       *value;
350         bool            isnull;
351         Oid                     typoutput,
352                                 typelem;
353         bool            typisvarlena;
354
355         for (i = 0; i < natts; ++i)
356         {
357                 origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
358                 if (isnull)
359                         continue;
360                 if (getTypeOutputInfo(typeinfo->attrs[i]->atttypid,
361                                                           &typoutput, &typelem, &typisvarlena))
362                 {
363                         /*
364                          * If we have a toasted datum, forcibly detoast it here to
365                          * avoid memory leakage inside the type's output routine.
366                          */
367                         if (typisvarlena)
368                                 attr = PointerGetDatum(PG_DETOAST_DATUM(origattr));
369                         else
370                                 attr = origattr;
371
372                         value = DatumGetCString(OidFunctionCall3(typoutput,
373                                                                                                          attr,
374                                                                                            ObjectIdGetDatum(typelem),
375                                                   Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
376
377                         printatt((unsigned) i + 1, typeinfo->attrs[i], value);
378
379                         /* Clean up detoasted copy, if any */
380                         if (attr != origattr)
381                                 pfree(DatumGetPointer(attr));
382                         pfree(value);
383                 }
384         }
385         printf("\t----\n");
386 }
387
388 /* ----------------
389  *              printtup_internal
390  *              We use a different data prefix, e.g. 'B' instead of 'D' to
391  *              indicate a tuple in internal (binary) form.
392  *
393  *              This is largely same as printtup, except we don't use the typout func.
394  * ----------------
395  */
396 static void
397 printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
398 {
399         DR_printtup *myState = (DR_printtup *) self;
400         StringInfoData buf;
401         int                     natts = tuple->t_data->t_natts;
402         int                     i,
403                                 j,
404                                 k;
405
406         /* Set or update my derived attribute info, if needed */
407         if (myState->attrinfo != typeinfo || myState->nattrs != natts)
408                 printtup_prepare_info(myState, typeinfo, natts);
409
410         /*
411          * tell the frontend to expect new tuple data (in binary style)
412          */
413         pq_beginmessage(&buf, 'B');
414
415         /*
416          * send a bitmap of which attributes are not null
417          */
418         j = 0;
419         k = 1 << 7;
420         for (i = 0; i < natts; ++i)
421         {
422                 if (!heap_attisnull(tuple, i + 1))
423                         j |= k;                         /* set bit if not null */
424                 k >>= 1;
425                 if (k == 0)                             /* end of byte? */
426                 {
427                         pq_sendint(&buf, j, 1);
428                         j = 0;
429                         k = 1 << 7;
430                 }
431         }
432         if (k != (1 << 7))                      /* flush last partial byte */
433                 pq_sendint(&buf, j, 1);
434
435         /*
436          * send the attributes of this tuple
437          */
438 #ifdef IPORTAL_DEBUG
439         fprintf(stderr, "sending tuple with %d atts\n", natts);
440 #endif
441
442         for (i = 0; i < natts; ++i)
443         {
444                 PrinttupAttrInfo *thisState = myState->myinfo + i;
445                 Datum           origattr,
446                                         attr;
447                 bool            isnull;
448                 int32           len;
449
450                 origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
451                 if (isnull)
452                         continue;
453                 /* send # of bytes, and opaque data */
454                 if (thisState->typisvarlena)
455                 {
456                         /*
457                          * If we have a toasted datum, must detoast before sending.
458                          */
459                         attr = PointerGetDatum(PG_DETOAST_DATUM(origattr));
460
461                         len = VARSIZE(attr) - VARHDRSZ;
462
463                         pq_sendint(&buf, len, VARHDRSZ);
464                         pq_sendbytes(&buf, VARDATA(attr), len);
465
466 #ifdef IPORTAL_DEBUG
467                         {
468                                 char       *d = VARDATA(attr);
469
470                                 fprintf(stderr, "length %d data %x %x %x %x\n",
471                                                 len, *d, *(d + 1), *(d + 2), *(d + 3));
472                         }
473 #endif
474
475                         /* Clean up detoasted copy, if any */
476                         if (attr != origattr)
477                                 pfree(DatumGetPointer(attr));
478                 }
479                 else
480                 {
481                         /* fixed size or cstring */
482                         attr = origattr;
483                         len = typeinfo->attrs[i]->attlen;
484                         if (len <= 0)
485                         {
486                                 /* it's a cstring */
487                                 Assert(len == -2 && !typeinfo->attrs[i]->attbyval);
488                                 len = strlen(DatumGetCString(attr)) + 1;
489                         }
490                         pq_sendint(&buf, len, sizeof(int32));
491                         if (typeinfo->attrs[i]->attbyval)
492                         {
493                                 Datum           datumBuf;
494
495                                 /*
496                                  * We need this horsing around because we don't know how
497                                  * shorter data values are aligned within a Datum.
498                                  */
499                                 store_att_byval(&datumBuf, attr, len);
500                                 pq_sendbytes(&buf, (char *) &datumBuf, len);
501 #ifdef IPORTAL_DEBUG
502                                 fprintf(stderr, "byval length %d data %ld\n", len,
503                                                 (long) attr);
504 #endif
505                         }
506                         else
507                         {
508                                 pq_sendbytes(&buf, DatumGetPointer(attr), len);
509 #ifdef IPORTAL_DEBUG
510                                 fprintf(stderr, "byref length %d data %p\n", len,
511                                                 DatumGetPointer(attr));
512 #endif
513                         }
514                 }
515         }
516
517         pq_endmessage(&buf);
518 }