]> 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 5b79cb84a4878a65463d244037f35921de24b854..27d6ffd508e48000c77b9ac8489c09e36307c70c 100644 (file)
@@ -1,15 +1,15 @@
 /*-------------------------------------------------------------------------
  *
  * 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.56 2000/12/27 23:59:10 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.69 2003/05/06 00:20:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #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);
@@ -31,31 +33,6 @@ static void printtup_cleanup(DestReceiver *self);
  * ----------------------------------------------------------------
  */
 
-/* ----------------
- *             getTypeOutputInfo -- get info needed for printing values of a type
- * ----------------
- */
-bool
-getTypeOutputInfo(Oid type, Oid *typOutput, Oid *typElem,
-                                 bool *typIsVarlena)
-{
-       HeapTuple       typeTuple;
-       Form_pg_type pt;
-
-       typeTuple = SearchSysCache(TYPEOID,
-                                                          ObjectIdGetDatum(type),
-                                                          0, 0, 0);
-       if (!HeapTupleIsValid(typeTuple))
-               elog(ERROR, "getTypeOutputInfo: Cache lookup of type %u failed", type);
-       pt = (Form_pg_type) GETSTRUCT(typeTuple);
-
-       *typOutput = pt->typoutput;
-       *typElem = pt->typelem;
-       *typIsVarlena = (! pt->typbyval) && (pt->typlen == -1);
-       ReleaseSysCache(typeTuple);
-       return OidIsValid(*typOutput);
-}
-
 /* ----------------
  *             Private state for a printtup destination object
  * ----------------
@@ -71,6 +48,7 @@ typedef struct
 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 */
@@ -81,7 +59,7 @@ typedef struct
  * ----------------
  */
 DestReceiver *
-printtup_create_DR(bool isBinary)
+printtup_create_DR(bool isBinary, bool sendDescrip)
 {
        DR_printtup *self = (DR_printtup *) palloc(sizeof(DR_printtup));
 
@@ -89,6 +67,8 @@ printtup_create_DR(bool isBinary)
        self->pub.setup = printtup_setup;
        self->pub.cleanup = printtup_cleanup;
 
+       self->sendDescrip = sendDescrip;
+
        self->attrinfo = NULL;
        self->nattrs = 0;
        self->myinfo = NULL;
@@ -97,21 +77,101 @@ printtup_create_DR(bool isBinary)
 }
 
 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)
 {
@@ -155,16 +215,13 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
        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;
@@ -183,9 +240,8 @@ 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 < natts; ++i)
        {
@@ -201,8 +257,8 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
                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 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));
@@ -210,9 +266,9 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
                                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));
 
@@ -270,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)
@@ -284,7 +340,24 @@ 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
@@ -309,8 +382,8 @@ debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
                                                          &typoutput, &typelem, &typisvarlena))
                {
                        /*
-                        * If we have a toasted datum, forcibly detoast it here to avoid
-                        * memory leakage inside the type's output routine.
+                        * 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));
@@ -318,9 +391,9 @@ debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
                                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);
 
@@ -355,16 +428,13 @@ printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
        if (myState->attrinfo != typeinfo || myState->nattrs != natts)
                printtup_prepare_info(myState, typeinfo, natts);
 
-       /* ----------------
-        *      tell the frontend to expect new tuple data (in binary style)
-        * ----------------
+       /*
+        * 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;
@@ -383,9 +453,8 @@ 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", natts);
@@ -430,9 +499,15 @@ printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
                }
                else
                {
-                       /* fixed size */
+                       /* 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;
+                       }
                        pq_sendint(&buf, len, sizeof(int32));
                        if (typeinfo->attrs[i]->attbyval)
                        {