]> 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 9afff872642303f9bf2a21cefe9d55135441b3ad..27d6ffd508e48000c77b9ac8489c09e36307c70c 100644 (file)
 /*-------------------------------------------------------------------------
  *
- * printtup.c--
- *    Routines to print out tuples to the destination (binary or non-binary
- *    portals, frontend/interactive backend, etc.).
+ * printtup.c
+ *       Routines to print out tuples to the destination (both frontend
+ *       clients and interactive backends are supported here).
  *
- * 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.7 1996/11/01 09:41:39 scrappy Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.69 2003/05/06 00:20:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
-
-#include <stdio.h>
-#include <sys/types.h>
-#include <netinet/in.h>
-
 #include "postgres.h"
 
-#include "access/htup.h"
-#include "utils/rel.h"
-#include "access/tupdesc.h"
-
-#include "fmgr.h" 
-#include "libpq/pqcomm.h"
-#include "storage/buf.h"
-#include "utils/palloc.h"
-
+#include "access/heapam.h"
+#include "access/printtup.h"
 #include "libpq/libpq.h"
+#include "libpq/pqformat.h"
+#include "utils/lsyscache.h"
 
-#include "catalog/pg_type.h"
-#include "utils/syscache.h"
-#include "access/heaptuple.h" 
+
+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);
 
 /* ----------------------------------------------------------------
- *     printtup / debugtup support
+ *             printtup / debugtup support
  * ----------------------------------------------------------------
  */
 
 /* ----------------
- *     typtoout - used by printtup and debugtup
+ *             Private state for a printtup destination object
  * ----------------
  */
-Oid
-typtoout(Oid type)
+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
 {
-    HeapTuple  typeTuple;
-    
-    typeTuple = SearchSysCacheTuple(TYPOID,
-                                   ObjectIdGetDatum(type),
-                                   0, 0, 0);
-    
-    if (HeapTupleIsValid(typeTuple))
-       return((Oid)
-              ((TypeTupleForm) GETSTRUCT(typeTuple))->typoutput);
-    
-    elog(WARN, "typtoout: Cache lookup of type %d failed", type);
-    return(InvalidOid);
+       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 */
+} DR_printtup;
+
+/* ----------------
+ *             Initialize: create a DestReceiver for printtup
+ * ----------------
+ */
+DestReceiver *
+printtup_create_DR(bool isBinary, bool sendDescrip)
+{
+       DR_printtup *self = (DR_printtup *) palloc(sizeof(DR_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;
+
+       return (DestReceiver *) self;
 }
 
-Oid
-gettypelem(Oid type)
+static void
+printtup_setup(DestReceiver *self, int operation,
+                          const char *portalName, TupleDesc typeinfo, List *targetlist)
 {
-    HeapTuple  typeTuple;
-    
-    typeTuple = SearchSysCacheTuple(TYPOID,
-                                   ObjectIdGetDatum(type),
-                                   0,0,0);
-    
-    if (HeapTupleIsValid(typeTuple))
-       return((Oid)
-              ((TypeTupleForm) GETSTRUCT(typeTuple))->typelem);
-    
-    elog(WARN, "typtoout: Cache lookup of type %d failed", type);
-    return(InvalidOid);
+       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 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).
+        * ----------------
+        */
+}
+
+/*
+ * 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)
+{
+       int                     i;
+
+       if (myState->myinfo)
+               pfree(myState->myinfo); /* get rid of any old data */
+       myState->myinfo = NULL;
+       myState->attrinfo = typeinfo;
+       myState->nattrs = numAttrs;
+       if (numAttrs <= 0)
+               return;
+       myState->myinfo = (PrinttupAttrInfo *)
+               palloc(numAttrs * sizeof(PrinttupAttrInfo));
+       for (i = 0; i < numAttrs; i++)
+       {
+               PrinttupAttrInfo *thisState = myState->myinfo + i;
+
+               if (getTypeOutputInfo(typeinfo->attrs[i]->atttypid,
+                                                         &thisState->typoutput, &thisState->typelem,
+                                                         &thisState->typisvarlena))
+                       fmgr_info(thisState->typoutput, &thisState->finfo);
+       }
 }
 
 /* ----------------
- *     printtup
+ *             printtup
  * ----------------
  */
-void
-printtup(HeapTuple tuple, TupleDesc typeinfo)
+static void
+printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
 {
-    int                i, j, k;
-    char       *outputstr, *attr;
-    bool       isnull;
-    Oid        typoutput;
-    
-    /* ----------------
-     * tell the frontend to expect new tuple data
-     * ----------------
-     */
-    pq_putnchar("D", 1);
-    
-    /* ----------------
-     * send a bitmap of which attributes are null
-     * ----------------
-     */
-    j = 0;
-    k = 1 << 7;
-    for (i = 0; i < tuple->t_natts; ) {
-       attr = heap_getattr(tuple, InvalidBuffer, ++i, typeinfo, &isnull);
-       if (!isnull)
-           j |= k;
-       k >>= 1;
-       if (!(i & 7)) {
-           pq_putint(j, 1);
-           j = 0;
-           k = 1 << 7;
+       DR_printtup *myState = (DR_printtup *) self;
+       StringInfoData buf;
+       int                     natts = tuple->t_data->t_natts;
+       int                     i,
+                               j,
+                               k;
+
+       /* 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 ASCII style)
+        */
+       pq_beginmessage(&buf, 'D');
+
+       /*
+        * send a bitmap of which attributes are not null
+        */
+       j = 0;
+       k = 1 << 7;
+       for (i = 0; i < natts; ++i)
+       {
+               if (!heap_attisnull(tuple, i + 1))
+                       j |= k;                         /* set bit if not null */
+               k >>= 1;
+               if (k == 0)                             /* end of byte? */
+               {
+                       pq_sendint(&buf, j, 1);
+                       j = 0;
+                       k = 1 << 7;
+               }
        }
-    }
-    if (i & 7)
-       pq_putint(j, 1);
-    
-    /* ----------------
-     * send the attributes of this tuple
-     * ----------------
-     */
-    for (i = 0; i < tuple->t_natts; ++i) {
-       attr = heap_getattr(tuple, InvalidBuffer, i+1, typeinfo, &isnull);
-       typoutput = typtoout((Oid) typeinfo->attrs[i]->atttypid);
-       
-       if (!isnull && OidIsValid(typoutput)) {
-           outputstr = fmgr(typoutput, attr, 
-                            gettypelem(typeinfo->attrs[i]->atttypid));
-           pq_putint(strlen(outputstr)+4, 4);
-           pq_putnchar(outputstr, strlen(outputstr));
-           pfree(outputstr);
+       if (k != (1 << 7))                      /* flush last partial byte */
+               pq_sendint(&buf, j, 1);
+
+       /*
+        * send the attributes of this tuple
+        */
+       for (i = 0; i < natts; ++i)
+       {
+               PrinttupAttrInfo *thisState = myState->myinfo + i;
+               Datum           origattr,
+                                       attr;
+               bool            isnull;
+               char       *outputstr;
+
+               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)));
+
+                       pq_sendcountedtext(&buf, outputstr, strlen(outputstr));
+
+                       /* Clean up detoasted copy, if any */
+                       if (attr != origattr)
+                               pfree(DatumGetPointer(attr));
+                       pfree(outputstr);
+               }
+               else
+               {
+                       outputstr = "<unprintable>";
+                       pq_sendcountedtext(&buf, outputstr, strlen(outputstr));
+               }
        }
-    }
+
+       pq_endmessage(&buf);
+}
+
+/* ----------------
+ *             printtup_cleanup
+ * ----------------
+ */
+static void
+printtup_cleanup(DestReceiver *self)
+{
+       DR_printtup *myState = (DR_printtup *) self;
+
+       if (myState->myinfo)
+               pfree(myState->myinfo);
+       pfree(myState);
 }
 
 /* ----------------
- *     printatt
+ *             printatt
  * ----------------
  */
 static void
 printatt(unsigned attributeId,
-        AttributeTupleForm attributeP,
-        char *value)
+                Form_pg_attribute attributeP,
+                char *value)
 {
-    printf("\t%2d: %.*s%s%s%s\t(typeid = %u, len = %d, byval = %c)\n",
-          attributeId,
-          NAMEDATALEN,         /* attname is a char16 */
-          attributeP->attname.data,
-          value != NULL ? " = \"" : "",
-          value != NULL ? value : "",
-          value != NULL ? "\"" : "",
-          (unsigned int) (attributeP->atttypid),
-          attributeP->attlen,
-          attributeP->attbyval ? 't' : 'f');
+       printf("\t%2d: %s%s%s%s\t(typeid = %u, len = %d, typmod = %d, byval = %c)\n",
+                  attributeId,
+                  NameStr(attributeP->attname),
+                  value != NULL ? " = \"" : "",
+                  value != NULL ? value : "",
+                  value != NULL ? "\"" : "",
+                  (unsigned int) (attributeP->atttypid),
+                  attributeP->attlen,
+                  attributeP->atttypmod,
+                  attributeP->attbyval ? 't' : 'f');
 }
 
 /* ----------------
- *     showatts
+ *             showatts
+ * ----------------
+ */
+static void
+showatts(const char *name, TupleDesc tupleDesc)
+{
+       int                     natts = tupleDesc->natts;
+       Form_pg_attribute *attinfo = tupleDesc->attrs;
+       int                     i;
+
+       puts(name);
+       for (i = 0; i < natts; ++i)
+               printatt((unsigned) i + 1, attinfo[i], (char *) NULL);
+       printf("\t----\n");
+}
+
+/* ----------------
+ *             debugSetup - prepare to print tuples for an interactive backend
  * ----------------
  */
 void
-showatts(char *name, TupleDesc tupleDesc)
+debugSetup(DestReceiver *self, int operation,
+                  const char *portalName, TupleDesc typeinfo, List *targetlist)
 {
-    int        i;
-    int natts = tupleDesc->natts;
-    AttributeTupleForm *attinfo = tupleDesc->attrs;
-
-    puts(name);
-    for (i = 0; i < natts; ++i)
-       printatt((unsigned) i+1, attinfo[i], (char *) NULL);
-    printf("\t----\n");
+       /*
+        * show the return type of the tuples
+        */
+       if (portalName == NULL)
+               portalName = "blank";
+
+       showatts(portalName, typeinfo);
 }
 
 /* ----------------
- *     debugtup
+ *             debugtup - print one tuple for an interactive backend
  * ----------------
  */
 void
-debugtup(HeapTuple tuple, TupleDesc typeinfo)
+debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
 {
-    register int       i;
-    char               *attr, *value;
-    bool               isnull;
-    Oid                typoutput;
-    
-    for (i = 0; i < tuple->t_natts; ++i) {
-       attr = heap_getattr(tuple, InvalidBuffer, i+1, typeinfo, &isnull);
-       typoutput = typtoout((Oid) typeinfo->attrs[i]->atttypid);
-       
-       if (!isnull && OidIsValid(typoutput)) {
-           value = fmgr(typoutput, attr, 
-                        gettypelem(typeinfo->attrs[i]->atttypid));
-           printatt((unsigned) i+1, typeinfo->attrs[i], value);
-           pfree(value);
+       int                     natts = tuple->t_data->t_natts;
+       int                     i;
+       Datum           origattr,
+                               attr;
+       char       *value;
+       bool            isnull;
+       Oid                     typoutput,
+                               typelem;
+       bool            typisvarlena;
+
+       for (i = 0; i < natts; ++i)
+       {
+               origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
+               if (isnull)
+                       continue;
+               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)));
+
+                       printatt((unsigned) i + 1, typeinfo->attrs[i], value);
+
+                       /* Clean up detoasted copy, if any */
+                       if (attr != origattr)
+                               pfree(DatumGetPointer(attr));
+                       pfree(value);
+               }
        }
-    }
-    printf("\t----\n");
+       printf("\t----\n");
 }
 
 /* ----------------
- *     printtup_internal
- *      Protocol expects either T, D, C, E, or N.
- *      We use a different data prefix, e.g. 'B' instead of 'D' to
- *      indicate a tuple in internal (binary) form.
+ *             printtup_internal
+ *             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.
+ *             This is largely same as printtup, except we don't use the typout func.
  * ----------------
  */
-void
-printtup_internal(HeapTuple tuple, TupleDesc typeinfo)
+static void
+printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
 {
-    int                i, j, k;
-    char       *attr;
-    bool       isnull;
-    
-    /* ----------------
-     * tell the frontend to expect new tuple data
-     * ----------------
-     */
-    pq_putnchar("B", 1);
-    
-    /* ----------------
-     * send a bitmap of which attributes are null
-     * ----------------
-     */
-    j = 0;
-    k = 1 << 7;
-    for (i = 0; i < tuple->t_natts; ) {
-       attr = heap_getattr(tuple, InvalidBuffer, ++i, typeinfo, &isnull);
-       if (!isnull)
-           j |= k;
-       k >>= 1;
-       if (!(i & 7)) {
-           pq_putint(j, 1);
-           j = 0;
-           k = 1 << 7;
+       DR_printtup *myState = (DR_printtup *) self;
+       StringInfoData buf;
+       int                     natts = tuple->t_data->t_natts;
+       int                     i,
+                               j,
+                               k;
+
+       /* 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, 'B');
+
+       /*
+        * send a bitmap of which attributes are not null
+        */
+       j = 0;
+       k = 1 << 7;
+       for (i = 0; i < natts; ++i)
+       {
+               if (!heap_attisnull(tuple, i + 1))
+                       j |= k;                         /* set bit if not null */
+               k >>= 1;
+               if (k == 0)                             /* end of byte? */
+               {
+                       pq_sendint(&buf, j, 1);
+                       j = 0;
+                       k = 1 << 7;
+               }
        }
-    }
-    if (i & 7)
-       pq_putint(j, 1);
-    
-    /* ----------------
-     * send the attributes of this tuple
-     * ----------------
-     */
+       if (k != (1 << 7))                      /* flush last partial byte */
+               pq_sendint(&buf, j, 1);
+
+       /*
+        * send the attributes of this tuple
+        */
 #ifdef IPORTAL_DEBUG
-    fprintf(stderr, "sending tuple with %d atts\n", tuple->t_natts);
+       fprintf(stderr, "sending tuple with %d atts\n", natts);
 #endif
-    for (i = 0; i < tuple->t_natts; ++i) {
-       int32 len = typeinfo->attrs[i]->attlen;
-       
-       attr = heap_getattr(tuple, InvalidBuffer, i+1, typeinfo, &isnull);
-       if (!isnull) {
-           /* # of bytes, and opaque data */
-           if (len == -1) {
-               /* variable length, assume a varlena structure */
-               len = VARSIZE(attr) - VARHDRSZ;
-               
-               pq_putint(len, sizeof(int32));
-               pq_putnchar(VARDATA(attr), len);
-#ifdef IPORTAL_DEBUG
+
+       for (i = 0; i < natts; ++i)
+       {
+               PrinttupAttrInfo *thisState = myState->myinfo + i;
+               Datum           origattr,
+                                       attr;
+               bool            isnull;
+               int32           len;
+
+               origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
+               if (isnull)
+                       continue;
+               /* send # of bytes, and opaque data */
+               if (thisState->typisvarlena)
                {
-                   char *d = VARDATA(attr);
-                   
-                   fprintf(stderr, "length %d data %x%x%x%x\n",
-                           len, *d, *(d+1), *(d+2), *(d+3));
-               }
+                       /*
+                        * 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);
+
+#ifdef IPORTAL_DEBUG
+                       {
+                               char       *d = VARDATA(attr);
+
+                               fprintf(stderr, "length %d data %x %x %x %x\n",
+                                               len, *d, *(d + 1), *(d + 2), *(d + 3));
+                       }
 #endif
-           } else {
-               /* fixed size */
-               if (typeinfo->attrs[i]->attbyval) {
-                   int8 i8;
-                   int16 i16;
-                   int32 i32;
-                   
-                   pq_putint(len, sizeof(int32));
-                   switch (len) {
-                   case sizeof(int8):
-                       i8 = DatumGetChar(attr);
-                       pq_putnchar((char *) &i8, len);
-                       break;
-                   case sizeof(int16):
-                       i16 = DatumGetInt16(attr);
-                       pq_putnchar((char *) &i16, len);
-                       break;
-                   case sizeof(int32):
-                       i32 = DatumGetInt32(attr);
-                       pq_putnchar((char *) &i32, len);
-                       break;
-                   }
+
+                       /* 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;
+                       }
+                       pq_sendint(&buf, len, sizeof(int32));
+                       if (typeinfo->attrs[i]->attbyval)
+                       {
+                               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_putint(len, sizeof(int32));
-                   pq_putnchar(attr, len);
+                       }
+                       else
+                       {
+                               pq_sendbytes(&buf, DatumGetPointer(attr), len);
 #ifdef IPORTAL_DEBUG
-                   fprintf(stderr, "byref length %d data %x\n", len, attr);
+                               fprintf(stderr, "byref length %d data %p\n", len,
+                                               DatumGetPointer(attr));
 #endif
+                       }
                }
-           }
        }
-    }
+
+       pq_endmessage(&buf);
 }