]> 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 0d2aeeecd1c289929055187675fe321ebb118fa4..27d6ffd508e48000c77b9ac8489c09e36307c70c 100644 (file)
 /*-------------------------------------------------------------------------
  *
  * 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).
  *
- * 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.44 1999/04/25 19:27:43 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.69 2003/05/06 00:20:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
-
-#include <string.h>
-
 #include "postgres.h"
 
-#include "fmgr.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(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self);
-static void printtup_cleanup(DestReceiver* self);
+
+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
  * ----------------------------------------------------------------
  */
 
-/* ----------------
- *             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(TYPOID,
-                                                                       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 %d failed", type);
-
-       *typOutput = InvalidOid;
-       *typElem = InvalidOid;
-       return 0;
-}
-
 /* ----------------
  *             Private state for a printtup destination object
  * ----------------
  */
-typedef struct {                               /* Per-attribute information */
+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 */
-       TupleDesc                       attrinfo;       /* The attr info we are set up for */
-       int                                     nattrs;
-       PrinttupAttrInfo   *myinfo;             /* Cached info about each attr */
+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 */
 } DR_printtup;
 
 /* ----------------
  *             Initialize: create a DestReceiver for printtup
  * ----------------
  */
-DestReceiver*
-printtup_create_DR()
+DestReceiver *
+printtup_create_DR(bool isBinary, bool sendDescrip)
 {
-       DR_printtup* self = (DR_printtup*) palloc(sizeof(DR_printtup));
+       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;
 
-       return (DestReceiver*) self;
+       return (DestReceiver *) self;
 }
 
 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.
+        *        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 :-(
+        *        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_printtupmyState, TupleDesc typeinfo, int numAttrs)
+printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
 {
-       int i;
+       int                     i;
 
        if (myState->myinfo)
-               pfree(myState->myinfo); /* get rid of any old data */
+               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*)
+       myState->myinfo = (PrinttupAttrInfo *)
                palloc(numAttrs * sizeof(PrinttupAttrInfo));
        for (i = 0; i < numAttrs; i++)
        {
-               PrinttupAttrInfo* thisState = myState->myinfo + i;
-               if (getTypeOutAndElem((Oid) typeinfo->attrs[i]->atttypid,
-                                                         &thisState->typoutput, &thisState->typelem))
+               PrinttupAttrInfo *thisState = myState->myinfo + i;
+
+               if (getTypeOutputInfo(typeinfo->attrs[i]->atttypid,
+                                                         &thisState->typoutput, &thisState->typelem,
+                                                         &thisState->typisvarlena))
                        fmgr_info(thisState->typoutput, &thisState->finfo);
        }
 }
@@ -147,38 +202,32 @@ printtup_prepare_info(DR_printtup* myState, TupleDesc typeinfo, int numAttrs)
  * ----------------
  */
 static void
-printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiverself)
+printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
 {
-       DR_printtup *myState = (DR_printtup*) 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))
+               if (!heap_attisnull(tuple, i + 1))
                        j |= k;                         /* set bit if not null */
                k >>= 1;
                if (k == 0)                             /* end of byte? */
@@ -191,21 +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;
-               attr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
+               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))
                {
-                       outputstr = (char *) (*fmgr_faddr(&thisState->finfo))
-                               (attr, thisState->typelem, typeinfo->attrs[i]->atttypmod);
+                       /*
+                        * 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
@@ -223,9 +292,10 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver* self)
  * ----------------
  */
 static void
-printtup_cleanup(DestReceiverself)
+printtup_cleanup(DestReceiver *self)
 {
-       DR_printtup* myState = (DR_printtup*) self;
+       DR_printtup *myState = (DR_printtup *) self;
+
        if (myState->myinfo)
                pfree(myState->myinfo);
        pfree(myState);
@@ -242,7 +312,7 @@ printatt(unsigned attributeId,
 {
        printf("\t%2d: %s%s%s%s\t(typeid = %u, len = %d, typmod = %d, byval = %c)\n",
                   attributeId,
-                  attributeP->attname.data,
+                  NameStr(attributeP->attname),
                   value != NULL ? " = \"" : "",
                   value != NULL ? value : "",
                   value != NULL ? "\"" : "",
@@ -256,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)
@@ -270,30 +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, DestReceiverself)
+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))
                {
-                       value = fmgr(typoutput, attr, typelem,
-                                                typeinfo->attrs[i]->atttypmod);
+                       /*
+                        * 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);
                }
        }
@@ -305,36 +411,36 @@ 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
-printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiverself)
+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))
+               if (!heap_attisnull(tuple, i + 1))
                        j |= k;                         /* set bit if not null */
                k >>= 1;
                if (k == 0)                             /* end of byte? */
@@ -347,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
-                               }
                        }
                }
        }