]> granicus.if.org Git - postgresql/blobdiff - src/backend/access/common/printtup.c
Implement feature of new FE/BE protocol whereby RowDescription identifies
[postgresql] / src / backend / access / common / printtup.c
index 6fe0e9652c21272a1eb10615a30915dda9b8e4cb..27d6ffd508e48000c77b9ac8489c09e36307c70c 100644 (file)
@@ -1,30 +1,31 @@
 /*-------------------------------------------------------------------------
  *
  * printtup.c
- *       Routines to print out tuples to the destination (binary or non-binary
- *       portals, frontend/interactive backend, etc.).
+ *       Routines to print out tuples to the destination (both frontend
+ *       clients and interactive backends are supported here).
  *
- * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
- * Portions Copyright (c) 1994, Regents of the University of California
  *
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.53 2000/05/30 04:24:27 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.69 2003/05/06 00:20:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
-
-
 #include "postgres.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"
+#include "utils/lsyscache.h"
 
-static void printtup_setup(DestReceiver *self, TupleDesc typeinfo);
+
+static void printtup_setup(DestReceiver *self, int operation,
+                          const char *portalName, TupleDesc typeinfo, List *targetlist);
 static void printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self);
+static void printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self);
 static void printtup_cleanup(DestReceiver *self);
 
 /* ----------------------------------------------------------------
@@ -32,39 +33,6 @@ static void printtup_cleanup(DestReceiver *self);
  * ----------------------------------------------------------------
  */
 
-/* ----------------
- *             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.
- * ----------------
- */
-int
-getTypeOutAndElem(Oid type, Oid *typOutput, Oid *typElem)
-{
-       HeapTuple       typeTuple;
-
-       typeTuple = SearchSysCacheTuple(TYPEOID,
-                                                                       ObjectIdGetDatum(type),
-                                                                       0, 0, 0);
-
-       if (HeapTupleIsValid(typeTuple))
-       {
-               Form_pg_type pt = (Form_pg_type) GETSTRUCT(typeTuple);
-
-               *typOutput = (Oid) pt->typoutput;
-               *typElem = (Oid) pt->typelem;
-               return OidIsValid(*typOutput);
-       }
-
-       elog(ERROR, "getTypeOutAndElem: Cache lookup of type %u failed", type);
-
-       *typOutput = InvalidOid;
-       *typElem = InvalidOid;
-       return 0;
-}
-
 /* ----------------
  *             Private state for a printtup destination object
  * ----------------
@@ -73,12 +41,14 @@ 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 */
+       bool            typisvarlena;   /* is it varlena (ie possibly toastable)? */
        FmgrInfo        finfo;                  /* Precomputed call info for typoutput */
 } PrinttupAttrInfo;
 
 typedef struct
 {
        DestReceiver pub;                       /* publicly-known function pointers */
+       bool            sendDescrip;    /* send RowDescription at startup? */
        TupleDesc       attrinfo;               /* The attr info we are set up for */
        int                     nattrs;
        PrinttupAttrInfo *myinfo;       /* Cached info about each attr */
@@ -89,14 +59,16 @@ typedef struct
  * ----------------
  */
 DestReceiver *
-printtup_create_DR()
+printtup_create_DR(bool isBinary, bool sendDescrip)
 {
        DR_printtup *self = (DR_printtup *) palloc(sizeof(DR_printtup));
 
-       self->pub.receiveTuple = printtup;
+       self->pub.receiveTuple = isBinary ? printtup_internal : printtup;
        self->pub.setup = printtup_setup;
        self->pub.cleanup = printtup_cleanup;
 
+       self->sendDescrip = sendDescrip;
+
        self->attrinfo = NULL;
        self->nattrs = 0;
        self->myinfo = NULL;
@@ -105,21 +77,101 @@ printtup_create_DR()
 }
 
 static void
-printtup_setup(DestReceiver *self, TupleDesc typeinfo)
+printtup_setup(DestReceiver *self, int operation,
+                          const char *portalName, TupleDesc typeinfo, List *targetlist)
 {
+       DR_printtup *myState = (DR_printtup *) self;
+
+       if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
+       {
+               /*
+                * Send portal name to frontend (obsolete cruft, gone in proto 3.0)
+                *
+                * If portal name not specified, use "blank" portal.
+                */
+               if (portalName == NULL)
+                       portalName = "blank";
+
+               pq_puttextmessage('P', portalName);
+       }
+
+       /*
+        * If this is a retrieve, and we are supposed to emit row descriptions,
+        * then we send back the tuple descriptor of the tuples.  
+        */
+       if (operation == CMD_SELECT && myState->sendDescrip)
+               SendRowDescriptionMessage(typeinfo, targetlist);
+
        /* ----------------
         * We could set up the derived attr info at this time, but we postpone it
-        * until the first call of printtup, for 3 reasons:
+        * until the first call of printtup, for 2 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 :-(
         * ----------------
         */
 }
 
+/*
+ * SendRowDescriptionMessage --- send a RowDescription message to the frontend
+ *
+ * Notes: the TupleDesc has typically been manufactured by ExecTypeFromTL()
+ * or some similar function; it does not contain a full set of fields.
+ * The targetlist will be NIL when executing a utility function that does
+ * not have a plan.  If the targetlist isn't NIL then it is a Plan node's
+ * targetlist; it is up to us to ignore resjunk columns in it.
+ */
+void
+SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist)
+{
+       Form_pg_attribute *attrs = typeinfo->attrs;
+       int                     natts = typeinfo->natts;
+       int                     proto = PG_PROTOCOL_MAJOR(FrontendProtocol);
+       int                     i;
+       StringInfoData buf;
+
+       pq_beginmessage(&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));
+               /* column ID info appears in protocol 3.0 and up */
+               if (proto >= 3)
+               {
+                       /* Do we have a non-resjunk tlist item? */
+                       while (targetlist &&
+                                  ((TargetEntry *) lfirst(targetlist))->resdom->resjunk)
+                               targetlist = lnext(targetlist);
+                       if (targetlist)
+                       {
+                               Resdom     *res = ((TargetEntry *) lfirst(targetlist))->resdom;
+
+                               pq_sendint(&buf, res->resorigtbl, 4);
+                               pq_sendint(&buf, res->resorigcol, 2);
+                               targetlist = lnext(targetlist);
+                       }
+                       else
+                       {
+                               /* No info available, so send zeroes */
+                               pq_sendint(&buf, 0, 4);
+                               pq_sendint(&buf, 0, 2);
+                       }
+               }
+               pq_sendint(&buf, (int) attrs[i]->atttypid,
+                                  sizeof(attrs[i]->atttypid));
+               pq_sendint(&buf, attrs[i]->attlen,
+                                  sizeof(attrs[i]->attlen));
+               /* typmod appears in protocol 2.0 and up */
+               if (proto >= 2)
+                       pq_sendint(&buf, attrs[i]->atttypmod,
+                                          sizeof(attrs[i]->atttypmod));
+       }
+       pq_endmessage(&buf);
+}
+
 static void
 printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
 {
@@ -138,8 +190,9 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
        {
                PrinttupAttrInfo *thisState = myState->myinfo + i;
 
-               if (getTypeOutAndElem((Oid) typeinfo->attrs[i]->atttypid,
-                                                         &thisState->typoutput, &thisState->typelem))
+               if (getTypeOutputInfo(typeinfo->attrs[i]->atttypid,
+                                                         &thisState->typoutput, &thisState->typelem,
+                                                         &thisState->typisvarlena))
                        fmgr_info(thisState->typoutput, &thisState->finfo);
        }
 }
@@ -153,32 +206,26 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
 {
        DR_printtup *myState = (DR_printtup *) self;
        StringInfoData buf;
+       int                     natts = tuple->t_data->t_natts;
        int                     i,
                                j,
                                k;
-       char       *outputstr;
-       Datum           attr;
-       bool            isnull;
 
        /* 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);
+       if (myState->attrinfo != typeinfo || myState->nattrs != natts)
+               printtup_prepare_info(myState, typeinfo, natts);
 
-       /* ----------------
-        *      tell the frontend to expect new tuple data (in ASCII style)
-        * ----------------
+       /*
+        * tell the frontend to expect new tuple data (in ASCII style)
         */
-       pq_beginmessage(&buf);
-       pq_sendbyte(&buf, 'D');
+       pq_beginmessage(&buf, 'D');
 
-       /* ----------------
-        *      send a bitmap of which attributes are not null
-        * ----------------
+       /*
+        * send a bitmap of which attributes are not null
         */
        j = 0;
        k = 1 << 7;
-       for (i = 0; i < tuple->t_data->t_natts; ++i)
+       for (i = 0; i < natts; ++i)
        {
                if (!heap_attisnull(tuple, i + 1))
                        j |= k;                         /* set bit if not null */
@@ -193,24 +240,41 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
        if (k != (1 << 7))                      /* flush last partial byte */
                pq_sendint(&buf, j, 1);
 
-       /* ----------------
-        *      send the attributes of this tuple
-        * ----------------
+       /*
+        * send the attributes of this tuple
         */
-       for (i = 0; i < tuple->t_data->t_natts; ++i)
+       for (i = 0; i < natts; ++i)
        {
                PrinttupAttrInfo *thisState = myState->myinfo + i;
+               Datum           origattr,
+                                       attr;
+               bool            isnull;
+               char       *outputstr;
 
-               attr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
+               origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
                if (isnull)
                        continue;
                if (OidIsValid(thisState->typoutput))
                {
+                       /*
+                        * If we have a toasted datum, forcibly detoast it here to
+                        * avoid memory leakage inside the type's output routine.
+                        */
+                       if (thisState->typisvarlena)
+                               attr = PointerGetDatum(PG_DETOAST_DATUM(origattr));
+                       else
+                               attr = origattr;
+
                        outputstr = DatumGetCString(FunctionCall3(&thisState->finfo,
-                                                                               attr,
-                                                                               ObjectIdGetDatum(thisState->typelem),
-                                                                               Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
+                                                                                                         attr,
+                                                                       ObjectIdGetDatum(thisState->typelem),
+                                                 Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
+
                        pq_sendcountedtext(&buf, outputstr, strlen(outputstr));
+
+                       /* Clean up detoasted copy, if any */
+                       if (attr != origattr)
+                               pfree(DatumGetPointer(attr));
                        pfree(outputstr);
                }
                else
@@ -262,12 +326,12 @@ printatt(unsigned attributeId,
  *             showatts
  * ----------------
  */
-void
-showatts(char *name, TupleDesc tupleDesc)
+static void
+showatts(const char *name, TupleDesc tupleDesc)
 {
-       int                     i;
        int                     natts = tupleDesc->natts;
        Form_pg_attribute *attinfo = tupleDesc->attrs;
+       int                     i;
 
        puts(name);
        for (i = 0; i < natts; ++i)
@@ -276,32 +340,66 @@ showatts(char *name, TupleDesc tupleDesc)
 }
 
 /* ----------------
- *             debugtup
+ *             debugSetup - prepare to print tuples for an interactive backend
+ * ----------------
+ */
+void
+debugSetup(DestReceiver *self, int operation,
+                  const char *portalName, TupleDesc typeinfo, List *targetlist)
+{
+       /*
+        * show the return type of the tuples
+        */
+       if (portalName == NULL)
+               portalName = "blank";
+
+       showatts(portalName, typeinfo);
+}
+
+/* ----------------
+ *             debugtup - print one tuple for an interactive backend
  * ----------------
  */
 void
 debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
 {
+       int                     natts = tuple->t_data->t_natts;
        int                     i;
-       Datum           attr;
+       Datum           origattr,
+                               attr;
        char       *value;
        bool            isnull;
        Oid                     typoutput,
                                typelem;
+       bool            typisvarlena;
 
-       for (i = 0; i < tuple->t_data->t_natts; ++i)
+       for (i = 0; i < natts; ++i)
        {
-               attr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
+               origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
                if (isnull)
                        continue;
-               if (getTypeOutAndElem((Oid) typeinfo->attrs[i]->atttypid,
-                                                         &typoutput, &typelem))
+               if (getTypeOutputInfo(typeinfo->attrs[i]->atttypid,
+                                                         &typoutput, &typelem, &typisvarlena))
                {
+                       /*
+                        * If we have a toasted datum, forcibly detoast it here to
+                        * avoid memory leakage inside the type's output routine.
+                        */
+                       if (typisvarlena)
+                               attr = PointerGetDatum(PG_DETOAST_DATUM(origattr));
+                       else
+                               attr = origattr;
+
                        value = DatumGetCString(OidFunctionCall3(typoutput,
-                                                                       attr,
-                                                                       ObjectIdGetDatum(typelem),
-                                                                       Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
+                                                                                                        attr,
+                                                                                          ObjectIdGetDatum(typelem),
+                                                 Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
+
                        printatt((unsigned) i + 1, typeinfo->attrs[i], value);
+
+                       /* Clean up detoasted copy, if any */
+                       if (attr != origattr)
+                               pfree(DatumGetPointer(attr));
                        pfree(value);
                }
        }
@@ -313,34 +411,34 @@ debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
  *             We use a different data prefix, e.g. 'B' instead of 'D' to
  *             indicate a tuple in internal (binary) form.
  *
- *             This is same as printtup, except we don't use the typout func,
- *             and therefore have no need for persistent state.
+ *             This is largely same as printtup, except we don't use the typout func.
  * ----------------
  */
-void
+static void
 printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
 {
+       DR_printtup *myState = (DR_printtup *) self;
        StringInfoData buf;
+       int                     natts = tuple->t_data->t_natts;
        int                     i,
                                j,
                                k;
-       Datum           attr;
-       bool            isnull;
 
-       /* ----------------
-        *      tell the frontend to expect new tuple data (in binary style)
-        * ----------------
+       /* Set or update my derived attribute info, if needed */
+       if (myState->attrinfo != typeinfo || myState->nattrs != natts)
+               printtup_prepare_info(myState, typeinfo, natts);
+
+       /*
+        * tell the frontend to expect new tuple data (in binary style)
         */
-       pq_beginmessage(&buf);
-       pq_sendbyte(&buf, 'B');
+       pq_beginmessage(&buf, 'B');
 
-       /* ----------------
-        *      send a bitmap of which attributes are not null
-        * ----------------
+       /*
+        * send a bitmap of which attributes are not null
         */
        j = 0;
        k = 1 << 7;
-       for (i = 0; i < tuple->t_data->t_natts; ++i)
+       for (i = 0; i < natts; ++i)
        {
                if (!heap_attisnull(tuple, i + 1))
                        j |= k;                         /* set bit if not null */
@@ -355,76 +453,84 @@ printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
        if (k != (1 << 7))                      /* flush last partial byte */
                pq_sendint(&buf, j, 1);
 
-       /* ----------------
-        *      send the attributes of this tuple
-        * ----------------
+       /*
+        * send the attributes of this tuple
         */
 #ifdef IPORTAL_DEBUG
-       fprintf(stderr, "sending tuple with %d atts\n", tuple->t_data->t_natts);
+       fprintf(stderr, "sending tuple with %d atts\n", natts);
 #endif
-       for (i = 0; i < tuple->t_data->t_natts; ++i)
+
+       for (i = 0; i < natts; ++i)
        {
-               int32           len = typeinfo->attrs[i]->attlen;
+               PrinttupAttrInfo *thisState = myState->myinfo + i;
+               Datum           origattr,
+                                       attr;
+               bool            isnull;
+               int32           len;
 
-               attr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
-               if (!isnull)
+               origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
+               if (isnull)
+                       continue;
+               /* send # of bytes, and opaque data */
+               if (thisState->typisvarlena)
                {
-                       /* # of bytes, and opaque data */
-                       if (len == -1)
-                       {
-                               /* variable length, assume a varlena structure */
-                               len = VARSIZE(attr) - VARHDRSZ;
+                       /*
+                        * If we have a toasted datum, must detoast before sending.
+                        */
+                       attr = PointerGetDatum(PG_DETOAST_DATUM(origattr));
+
+                       len = VARSIZE(attr) - VARHDRSZ;
 
-                               pq_sendint(&buf, len, VARHDRSZ);
-                               pq_sendbytes(&buf, VARDATA(attr), len);
+                       pq_sendint(&buf, len, VARHDRSZ);
+                       pq_sendbytes(&buf, VARDATA(attr), len);
 
 #ifdef IPORTAL_DEBUG
-                               {
-                                       char       *d = VARDATA(attr);
+                       {
+                               char       *d = VARDATA(attr);
 
-                                       fprintf(stderr, "length %d data %x%x%x%x\n",
-                                                       len, *d, *(d + 1), *(d + 2), *(d + 3));
-                               }
+                               fprintf(stderr, "length %d data %x %x %x %x\n",
+                                               len, *d, *(d + 1), *(d + 2), *(d + 3));
+                       }
 #endif
+
+                       /* Clean up detoasted copy, if any */
+                       if (attr != origattr)
+                               pfree(DatumGetPointer(attr));
+               }
+               else
+               {
+                       /* fixed size or cstring */
+                       attr = origattr;
+                       len = typeinfo->attrs[i]->attlen;
+                       if (len <= 0)
+                       {
+                               /* it's a cstring */
+                               Assert(len == -2 && !typeinfo->attrs[i]->attbyval);
+                               len = strlen(DatumGetCString(attr)) + 1;
                        }
-                       else
+                       pq_sendint(&buf, len, sizeof(int32));
+                       if (typeinfo->attrs[i]->attbyval)
                        {
-                               /* fixed size */
-                               if (typeinfo->attrs[i]->attbyval)
-                               {
-                                       int8            i8;
-                                       int16           i16;
-                                       int32           i32;
-
-                                       pq_sendint(&buf, len, sizeof(int32));
-                                       switch (len)
-                                       {
-                                               case sizeof(int8):
-                                                       i8 = DatumGetChar(attr);
-                                                       pq_sendbytes(&buf, (char *) &i8, len);
-                                                       break;
-                                               case sizeof(int16):
-                                                       i16 = DatumGetInt16(attr);
-                                                       pq_sendbytes(&buf, (char *) &i16, len);
-                                                       break;
-                                               case sizeof(int32):
-                                                       i32 = DatumGetInt32(attr);
-                                                       pq_sendbytes(&buf, (char *) &i32, len);
-                                                       break;
-                                       }
+                               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);
 #ifdef IPORTAL_DEBUG
-                                       fprintf(stderr, "byval length %d data %d\n", len, attr);
+                               fprintf(stderr, "byval length %d data %ld\n", len,
+                                               (long) attr);
 #endif
-                               }
-                               else
-                               {
-                                       pq_sendint(&buf, len, sizeof(int32));
-                                       pq_sendbytes(&buf, DatumGetPointer(attr), len);
+                       }
+                       else
+                       {
+                               pq_sendbytes(&buf, DatumGetPointer(attr), len);
 #ifdef IPORTAL_DEBUG
-                                       fprintf(stderr, "byref length %d data %x\n", len,
-                                                       DatumGetPointer(attr));
+                               fprintf(stderr, "byref length %d data %p\n", len,
+                                               DatumGetPointer(attr));
 #endif
-                               }
                        }
                }
        }