OSDN Git Service

Implement new-protocol binary I/O support in DataRow, Bind, and FunctionCall
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 9 May 2003 18:08:48 +0000 (18:08 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 9 May 2003 18:08:48 +0000 (18:08 +0000)
messages.  Binary I/O is now up and working, but only for a small set
of datatypes (integers, text, bytea).

src/backend/access/common/printtup.c
src/backend/commands/copy.c
src/backend/executor/spi.c
src/backend/tcop/fastpath.c
src/backend/tcop/postgres.c
src/backend/utils/cache/lsyscache.c
src/include/utils/lsyscache.h

index d5a29ef..6ad53dd 100644 (file)
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.71 2003/05/08 18:16:36 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.72 2003/05/09 18:08:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -42,14 +42,19 @@ static void printtup_destroy(DestReceiver *self);
 
 /* ----------------
  *             Private state for a printtup destination object
+ *
+ * NOTE: finfo is the lookup info for either typoutput or typsend, whichever
+ * we are using for this column.
  * ----------------
  */
 typedef struct
 {                                                              /* Per-attribute information */
-       Oid                     typoutput;              /* Oid for the attribute's type output fn */
+       Oid                     typoutput;              /* Oid for the type's text output fn */
+       Oid                     typsend;                /* Oid for the type's binary 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 */
+       int16           format;                 /* format code for this column */
+       FmgrInfo        finfo;                  /* Precomputed call info for output fn */
 } PrinttupAttrInfo;
 
 typedef struct
@@ -219,9 +224,13 @@ SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, int16 *formats)
        pq_endmessage(&buf);
 }
 
+/*
+ * Get the lookup info that printtup() needs
+ */
 static void
 printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
 {
+       int16      *formats = myState->portal->formats;
        int                     i;
 
        if (myState->myinfo)
@@ -232,15 +241,31 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
        if (numAttrs <= 0)
                return;
        myState->myinfo = (PrinttupAttrInfo *)
-               palloc(numAttrs * sizeof(PrinttupAttrInfo));
+               palloc0(numAttrs * sizeof(PrinttupAttrInfo));
        for (i = 0; i < numAttrs; i++)
        {
                PrinttupAttrInfo *thisState = myState->myinfo + i;
+               int16           format = (formats ? formats[i] : 0);
 
-               if (getTypeOutputInfo(typeinfo->attrs[i]->atttypid,
-                                                         &thisState->typoutput, &thisState->typelem,
-                                                         &thisState->typisvarlena))
+               thisState->format = format;
+               if (format == 0)
+               {
+                       getTypeOutputInfo(typeinfo->attrs[i]->atttypid,
+                                                         &thisState->typoutput,
+                                                         &thisState->typelem,
+                                                         &thisState->typisvarlena);
                        fmgr_info(thisState->typoutput, &thisState->finfo);
+               }
+               else if (format == 1)
+               {
+                       getTypeBinaryOutputInfo(typeinfo->attrs[i]->atttypid,
+                                                                       &thisState->typsend,
+                                                                       &thisState->typelem,
+                                                                       &thisState->typisvarlena);
+                       fmgr_info(thisState->typsend, &thisState->finfo);
+               }
+               else
+                       elog(ERROR, "Unsupported format code %d", format);
        }
 }
 
@@ -252,7 +277,6 @@ static void
 printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
 {
        DR_printtup *myState = (DR_printtup *) self;
-       int16      *formats = myState->portal->formats;
        StringInfoData buf;
        int                     natts = tuple->t_data->t_natts;
        int                     i;
@@ -274,11 +298,9 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
        for (i = 0; i < natts; ++i)
        {
                PrinttupAttrInfo *thisState = myState->myinfo + i;
-               int16           format = (formats ? formats[i] : 0);
                Datum           origattr,
                                        attr;
                bool            isnull;
-               char       *outputstr;
 
                origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
                if (isnull)
@@ -286,46 +308,46 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
                        pq_sendint(&buf, -1, 4);
                        continue;
                }
-               if (format == 0)
-               {
-                       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), false);
+               /*
+                * 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;
 
-                               /* Clean up detoasted copy, if any */
-                               if (attr != origattr)
-                                       pfree(DatumGetPointer(attr));
-                               pfree(outputstr);
-                       }
-                       else
-                       {
-                               outputstr = "<unprintable>";
-                               pq_sendcountedtext(&buf, outputstr, strlen(outputstr), false);
-                       }
-               }
-               else if (format == 1)
+               if (thisState->format == 0)
                {
-                       /* XXX something similar to above */
-                       elog(ERROR, "Binary transmission not implemented yet");
+                       /* Text output */
+                       char       *outputstr;
+
+                       outputstr = DatumGetCString(FunctionCall3(&thisState->finfo,
+                                                                                                         attr,
+                                                                       ObjectIdGetDatum(thisState->typelem),
+                                                 Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
+                       pq_sendcountedtext(&buf, outputstr, strlen(outputstr), false);
+                       pfree(outputstr);
                }
                else
                {
-                       elog(ERROR, "Invalid format code %d", format);
+                       /* Binary output */
+                       bytea      *outputbytes;
+
+                       outputbytes = DatumGetByteaP(FunctionCall2(&thisState->finfo,
+                                                                                                          attr,
+                                                                       ObjectIdGetDatum(thisState->typelem)));
+                       /* We assume the result will not have been toasted */
+                       pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
+                       pq_sendbytes(&buf, VARDATA(outputbytes),
+                                                VARSIZE(outputbytes) - VARHDRSZ);
+                       pfree(outputbytes);
                }
+
+               /* Clean up detoasted copy, if any */
+               if (attr != origattr)
+                       pfree(DatumGetPointer(attr));
        }
 
        pq_endmessage(&buf);
@@ -388,34 +410,28 @@ printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
                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,
+               Assert(thisState->format == 0);
+
+               /*
+                * 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), true);
+               pfree(outputstr);
 
-                       pq_sendcountedtext(&buf, outputstr, strlen(outputstr), true);
-
-                       /* Clean up detoasted copy, if any */
-                       if (attr != origattr)
-                               pfree(DatumGetPointer(attr));
-                       pfree(outputstr);
-               }
-               else
-               {
-                       outputstr = "<unprintable>";
-                       pq_sendcountedtext(&buf, outputstr, strlen(outputstr), true);
-               }
+               /* Clean up detoasted copy, if any */
+               if (attr != origattr)
+                       pfree(DatumGetPointer(attr));
        }
 
        pq_endmessage(&buf);
@@ -508,30 +524,29 @@ debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
                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;
+               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),
+               value = DatumGetCString(OidFunctionCall3(typoutput,
+                                                                                                attr,
+                                                                                                ObjectIdGetDatum(typelem),
                                                  Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
 
-                       printatt((unsigned) i + 1, typeinfo->attrs[i], value);
+               printatt((unsigned) i + 1, typeinfo->attrs[i], value);
 
-                       /* Clean up detoasted copy, if any */
-                       if (attr != origattr)
-                               pfree(DatumGetPointer(attr));
-                       pfree(value);
-               }
+               pfree(value);
+
+               /* Clean up detoasted copy, if any */
+               if (attr != origattr)
+                       pfree(DatumGetPointer(attr));
        }
        printf("\t----\n");
 }
@@ -542,7 +557,7 @@ debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
  * We use a different message type, i.e. 'B' instead of 'D' to
  * indicate a tuple in internal (binary) form.
  *
- * This is largely same as printtup_20, except we don't use the typout func.
+ * This is largely same as printtup_20, except we use binary formatting.
  * ----------------
  */
 static void
@@ -587,83 +602,41 @@ printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
        /*
         * send the attributes of this tuple
         */
-#ifdef IPORTAL_DEBUG
-       fprintf(stderr, "sending tuple with %d atts\n", natts);
-#endif
-
        for (i = 0; i < natts; ++i)
        {
                PrinttupAttrInfo *thisState = myState->myinfo + i;
                Datum           origattr,
                                        attr;
                bool            isnull;
-               int32           len;
+               bytea      *outputbytes;
 
                origattr = heap_getattr(tuple, i + 1, typeinfo, &isnull);
                if (isnull)
                        continue;
-               /* send # of bytes, and opaque data */
-               if (thisState->typisvarlena)
-               {
-                       /*
-                        * 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);
+               Assert(thisState->format == 1);
 
-#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
-
-                       /* Clean up detoasted copy, if any */
-                       if (attr != origattr)
-                               pfree(DatumGetPointer(attr));
-               }
+               /*
+                * 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
-               {
-                       /* 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 %ld\n", len,
-                                               (long) attr);
-#endif
-                       }
-                       else
-                       {
-                               pq_sendbytes(&buf, DatumGetPointer(attr), len);
-#ifdef IPORTAL_DEBUG
-                               fprintf(stderr, "byref length %d data %p\n", len,
-                                               DatumGetPointer(attr));
-#endif
-                       }
-               }
+
+               outputbytes = DatumGetByteaP(FunctionCall2(&thisState->finfo,
+                                                                                                  attr,
+                                                                       ObjectIdGetDatum(thisState->typelem)));
+               /* We assume the result will not have been toasted */
+               pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
+               pq_sendbytes(&buf, VARDATA(outputbytes),
+                                        VARSIZE(outputbytes) - VARHDRSZ);
+               pfree(outputbytes);
+
+               /* Clean up detoasted copy, if any */
+               if (attr != origattr)
+                       pfree(DatumGetPointer(attr));
        }
 
        pq_endmessage(&buf);
index 93d4b84..06c29c4 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.198 2003/05/08 18:16:36 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.199 2003/05/09 18:08:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -828,11 +828,9 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
                int                     attnum = lfirsti(cur);
                Oid                     out_func_oid;
 
-               if (!getTypeOutputInfo(attr[attnum - 1]->atttypid,
-                                                          &out_func_oid, &elements[attnum - 1],
-                                                          &isvarlena[attnum - 1]))
-                       elog(ERROR, "COPY: couldn't lookup info for type %u",
-                                attr[attnum - 1]->atttypid);
+               getTypeOutputInfo(attr[attnum - 1]->atttypid,
+                                                 &out_func_oid, &elements[attnum - 1],
+                                                 &isvarlena[attnum - 1]);
                fmgr_info(out_func_oid, &out_functions[attnum - 1]);
                if (binary && attr[attnum - 1]->attlen == -2)
                        elog(ERROR, "COPY BINARY: cstring not supported");
index fe420ce..785170b 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.97 2003/05/08 18:16:36 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.98 2003/05/09 18:08:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -546,11 +546,7 @@ SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
                typmod = -1;
        }
 
-       if (!getTypeOutputInfo(typoid, &foutoid, &typelem, &typisvarlena))
-       {
-               SPI_result = SPI_ERROR_NOOUTFUNC;
-               return NULL;
-       }
+       getTypeOutputInfo(typoid, &foutoid, &typelem, &typisvarlena);
 
        /*
         * If we have a toasted datum, forcibly detoast it here to avoid
index 0720f4e..74971c4 100644 (file)
@@ -8,26 +8,11 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.62 2003/05/08 18:16:36 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.63 2003/05/09 18:08:48 tgl Exp $
  *
  * NOTES
  *       This cruft is the server side of PQfn.
  *
- *       - jolly 07/11/95:
- *
- *       no longer rely on return sizes provided by the frontend.      Always
- *       use the true lengths for the catalogs.  Assume that the frontend
- *       has allocated enough space to handle the result value returned.
- *
- *       trust that the user knows what he is doing with the args.  If the
- *       sys catalog says it is a varlena, assume that the user is only sending
- *       down VARDATA and that the argsize is the VARSIZE.  If the arg is
- *       fixed len, assume that the argsize given by the user is correct.
- *
- *       if the function returns by value, then only send 4 bytes value
- *       back to the frontend.  If the return returns by reference,
- *       send down only the data portion and set the return size appropriately.
- *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
-#include "access/xact.h"
 #include "catalog/pg_proc.h"
 #include "libpq/libpq.h"
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
+#include "mb/pg_wchar.h"
 #include "tcop/fastpath.h"
 #include "utils/acl.h"
 #include "utils/lsyscache.h"
@@ -62,17 +47,16 @@ struct fp_info
 {
        Oid                     funcid;
        FmgrInfo        flinfo;                 /* function lookup info for funcid */
-       int16           arglen[FUNC_MAX_ARGS];
-       bool            argbyval[FUNC_MAX_ARGS];
-       int16           retlen;
-       bool            retbyval;
+       Oid                     namespace;              /* other stuff from pg_proc */
+       Oid                     rettype;
+       Oid                     argtypes[FUNC_MAX_ARGS];
 };
 
 
-static void parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
-                                                                 FunctionCallInfo fcinfo);
-static void parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info *fip,
-                                                                        FunctionCallInfo fcinfo);
+static int16 parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
+                                                                  FunctionCallInfo fcinfo);
+static int16 parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info *fip,
+                                                                         FunctionCallInfo fcinfo);
 
 
 /* ----------------
@@ -114,7 +98,7 @@ GetOldFunctionMessage(StringInfo buf)
                        return EOF;
                appendBinaryStringInfo(buf, (char *) &ibuf, 4);
                argsize = ntohl(ibuf);
-               if (argsize < 0)
+               if (argsize < -1)
                {
                        /* FATAL here since no hope of regaining message sync */
                        elog(FATAL, "HandleFunctionRequest: bogus argsize %d",
@@ -139,79 +123,69 @@ GetOldFunctionMessage(StringInfo buf)
 /* ----------------
  *             SendFunctionResult
  *
- * retlen is 0 if returning NULL, else the typlen according to the catalogs
+ * Note: although this routine doesn't check, the format had better be 1
+ * (binary) when talking to a pre-3.0 client.
  * ----------------
  */
 static void
-SendFunctionResult(Datum retval, bool retbyval, int retlen)
+SendFunctionResult(Datum retval, bool isnull, Oid rettype, int16 format)
 {
+       bool            newstyle = (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3);
        StringInfoData buf;
 
        pq_beginmessage(&buf, 'V');
 
-       if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
+       if (isnull)
        {
-               /* New-style message */
-               /* XXX replace this with standard binary (or text!) output */
-               if (retlen != 0)
-               {
-                       if (retbyval)
-                       {                                               /* by-value */
-                               pq_sendint(&buf, retlen, 4);
-                               pq_sendint(&buf, DatumGetInt32(retval), retlen);
-                       }
-                       else
-                       {                                               /* by-reference ... */
-                               if (retlen == -1)
-                               {                                       /* ... varlena */
-                                       struct varlena *v = PG_DETOAST_DATUM(retval);
-
-                                       pq_sendint(&buf, VARSIZE(v) - VARHDRSZ, VARHDRSZ);
-                                       pq_sendbytes(&buf, VARDATA(v), VARSIZE(v) - VARHDRSZ);
-                               }
-                               else
-                               {                                       /* ... fixed */
-                                       pq_sendint(&buf, retlen, 4);
-                                       pq_sendbytes(&buf, DatumGetPointer(retval), retlen);
-                               }
-                       }
-               }
-               else
-               {
-                       /* NULL marker */
+               if (newstyle)
                        pq_sendint(&buf, -1, 4);
-               }
        }
        else
        {
-               /* Old-style message */
-               if (retlen != 0)
-               {
+               if (!newstyle)
                        pq_sendbyte(&buf, 'G');
-                       if (retbyval)
-                       {                                               /* by-value */
-                               pq_sendint(&buf, retlen, 4);
-                               pq_sendint(&buf, DatumGetInt32(retval), retlen);
-                       }
-                       else
-                       {                                               /* by-reference ... */
-                               if (retlen == -1)
-                               {                                       /* ... varlena */
-                                       struct varlena *v = PG_DETOAST_DATUM(retval);
-
-                                       pq_sendint(&buf, VARSIZE(v) - VARHDRSZ, VARHDRSZ);
-                                       pq_sendbytes(&buf, VARDATA(v), VARSIZE(v) - VARHDRSZ);
-                               }
-                               else
-                               {                                       /* ... fixed */
-                                       pq_sendint(&buf, retlen, 4);
-                                       pq_sendbytes(&buf, DatumGetPointer(retval), retlen);
-                               }
-                       }
+
+               if (format == 0)
+               {
+                       Oid                     typoutput,
+                                               typelem;
+                       bool            typisvarlena;
+                       char       *outputstr;
+
+                       getTypeOutputInfo(rettype,
+                                                         &typoutput, &typelem, &typisvarlena);
+                       outputstr = DatumGetCString(OidFunctionCall3(typoutput,
+                                                                                                                retval,
+                                                                                                ObjectIdGetDatum(typelem),
+                                                                                                Int32GetDatum(-1)));
+                       pq_sendcountedtext(&buf, outputstr, strlen(outputstr), false);
+                       pfree(outputstr);
                }
-               pq_sendbyte(&buf, '0');
+               else if (format == 1)
+               {
+                       Oid                     typsend,
+                                               typelem;
+                       bool            typisvarlena;
+                       bytea      *outputbytes;
+
+                       getTypeBinaryOutputInfo(rettype,
+                                                                       &typsend, &typelem, &typisvarlena);
+                       outputbytes = DatumGetByteaP(OidFunctionCall2(typsend,
+                                                                                                                 retval,
+                                                                                                 ObjectIdGetDatum(typelem)));
+                       /* We assume the result will not have been toasted */
+                       pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
+                       pq_sendbytes(&buf, VARDATA(outputbytes),
+                                                VARSIZE(outputbytes) - VARHDRSZ);
+                       pfree(outputbytes);
+               }
+               else
+                       elog(ERROR, "Invalid format code %d", format);
        }
 
+       if (!newstyle)
+               pq_sendbyte(&buf, '0');
+
        pq_endmessage(&buf);
 }
 
@@ -224,11 +198,8 @@ SendFunctionResult(Datum retval, bool retbyval, int retlen)
 static void
 fetch_fp_info(Oid func_id, struct fp_info * fip)
 {
-       Oid                *argtypes;           /* an oidvector */
-       Oid                     rettype;
        HeapTuple       func_htp;
        Form_pg_proc pp;
-       int                     i;
 
        Assert(OidIsValid(func_id));
        Assert(fip != (struct fp_info *) NULL);
@@ -254,20 +225,10 @@ fetch_fp_info(Oid func_id, struct fp_info * fip)
                elog(ERROR, "fetch_fp_info: cache lookup for function %u failed",
                         func_id);
        pp = (Form_pg_proc) GETSTRUCT(func_htp);
-       rettype = pp->prorettype;
-       argtypes = pp->proargtypes;
-
-       for (i = 0; i < pp->pronargs; ++i)
-       {
-               get_typlenbyval(argtypes[i], &fip->arglen[i], &fip->argbyval[i]);
-               /* We don't support cstring in fastpath protocol */
-               if (fip->arglen[i] == -2)
-                       elog(ERROR, "CSTRING not supported in fastpath protocol");
-       }
 
-       get_typlenbyval(rettype, &fip->retlen, &fip->retbyval);
-       if (fip->retlen == -2)
-               elog(ERROR, "CSTRING not supported in fastpath protocol");
+       fip->namespace = pp->pronamespace;
+       fip->rettype = pp->prorettype;
+       memcpy(fip->argtypes, pp->proargtypes, FUNC_MAX_ARGS * sizeof(Oid));
 
        ReleaseSysCache(func_htp);
 
@@ -308,6 +269,7 @@ HandleFunctionRequest(StringInfo msgBuf)
        Oid                     fid;
        AclResult       aclresult;
        FunctionCallInfoData fcinfo;
+       int16           rformat;
        Datum           retval;
        struct fp_info my_fp;
        struct fp_info *fip;
@@ -334,7 +296,7 @@ HandleFunctionRequest(StringInfo msgBuf)
                         "queries ignored until end of transaction block");
 
        /*
-        * Parse the buffer contents.
+        * Begin parsing the buffer contents.
         */
        if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
                (void) pq_getmsgstring(msgBuf); /* dummy string */
@@ -348,7 +310,14 @@ HandleFunctionRequest(StringInfo msgBuf)
        fip = &my_fp;
        fetch_fp_info(fid, fip);
 
-       /* Check permission to call function */
+       /*
+        * Check permission to access and call function.  Since we didn't go
+        * through a normal name lookup, we need to check schema usage too.
+        */
+       aclresult = pg_namespace_aclcheck(fip->namespace, GetUserId(), ACL_USAGE);
+       if (aclresult != ACLCHECK_OK)
+               aclcheck_error(aclresult, get_namespace_name(fip->namespace));
+
        aclresult = pg_proc_aclcheck(fid, GetUserId(), ACL_EXECUTE);
        if (aclresult != ACLCHECK_OK)
                aclcheck_error(aclresult, get_func_name(fid));
@@ -365,9 +334,9 @@ HandleFunctionRequest(StringInfo msgBuf)
        fcinfo.flinfo = &fip->flinfo;
 
        if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
-               parse_fcall_arguments(msgBuf, fip, &fcinfo);
+               rformat = parse_fcall_arguments(msgBuf, fip, &fcinfo);
        else
-               parse_fcall_arguments_20(msgBuf, fip, &fcinfo);
+               rformat = parse_fcall_arguments_20(msgBuf, fip, &fcinfo);
 
        /* Verify we reached the end of the message where expected. */
        pq_getmsgend(msgBuf);
@@ -375,18 +344,18 @@ HandleFunctionRequest(StringInfo msgBuf)
        /* Okay, do it ... */
        retval = FunctionCallInvoke(&fcinfo);
 
-       if (fcinfo.isnull)
-               SendFunctionResult(retval, fip->retbyval, 0);
-       else
-               SendFunctionResult(retval, fip->retbyval, fip->retlen);
+       SendFunctionResult(retval, fcinfo.isnull, fip->rettype, rformat);
 
        return 0;
 }
 
 /*
  * Parse function arguments in a 3.0 protocol message
+ *
+ * Argument values are loaded into *fcinfo, and the desired result format
+ * is returned.
  */
-static void
+static int16
 parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
                                          FunctionCallInfo fcinfo)
 {
@@ -394,6 +363,7 @@ parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
        int                     i;
        int                     numAFormats;
        int16      *aformats = NULL;
+       StringInfoData abuf;
 
        /* Get the argument format codes */
        numAFormats = pq_getmsgint(msgBuf, 2);
@@ -412,59 +382,110 @@ parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
 
        fcinfo->nargs = nargs;
 
+       if (numAFormats > 1 && numAFormats != nargs)
+               elog(ERROR, "Function Call message has %d argument formats but %d arguments",
+                        numAFormats, nargs);
+
+       initStringInfo(&abuf);
+
        /*
         * Copy supplied arguments into arg vector.
         */
        for (i = 0; i < nargs; ++i)
        {
                int                     argsize;
-               char       *p;
+               int16           aformat;
 
                argsize = pq_getmsgint(msgBuf, 4);
-               if (fip->argbyval[i])
-               {                                               /* by-value */
-                       if (argsize < 1 || argsize > 4)
-                               elog(ERROR, "HandleFunctionRequest: bogus argsize %d",
-                                        argsize);
-                       /* XXX should we demand argsize == fip->arglen[i] ? */
-                       fcinfo->arg[i] = (Datum) pq_getmsgint(msgBuf, argsize);
+               if (argsize == -1)
+               {
+                       fcinfo->argnull[i] = true;
+                       continue;
                }
+               if (argsize < 0)
+                       elog(ERROR, "HandleFunctionRequest: bogus argsize %d",
+                                argsize);
+
+               /* Reset abuf to empty, and insert raw data into it */
+               abuf.len = 0;
+               abuf.data[0] = '\0';
+               abuf.cursor = 0;
+
+               appendBinaryStringInfo(&abuf,
+                                                          pq_getmsgbytes(msgBuf, argsize),
+                                                          argsize);
+
+               if (numAFormats > 1)
+                       aformat = aformats[i];
+               else if (numAFormats > 0)
+                       aformat = aformats[0];
                else
-               {                                               /* by-reference ... */
-                       if (fip->arglen[i] == -1)
-                       {                                       /* ... varlena */
-                               if (argsize < 0)
-                                       elog(ERROR, "HandleFunctionRequest: bogus argsize %d",
-                                                argsize);
-                               p = palloc(argsize + VARHDRSZ);
-                               VARATT_SIZEP(p) = argsize + VARHDRSZ;
-                               pq_copymsgbytes(msgBuf, VARDATA(p), argsize);
-                       }
-                       else
-                       {                                       /* ... fixed */
-                               if (argsize != fip->arglen[i])
-                                       elog(ERROR, "HandleFunctionRequest: bogus argsize %d, should be %d",
-                                                argsize, fip->arglen[i]);
-                               p = palloc(argsize + 1);                /* +1 in case argsize is 0 */
-                               pq_copymsgbytes(msgBuf, p, argsize);
-                       }
-                       fcinfo->arg[i] = PointerGetDatum(p);
+                       aformat = 0;            /* default = text */
+
+               if (aformat == 0)
+               {
+                       Oid                     typInput;
+                       Oid                     typElem;
+                       char       *pstring;
+
+                       getTypeInputInfo(fip->argtypes[i], &typInput, &typElem);
+                       /*
+                        * Since stringinfo.c keeps a trailing null in
+                        * place even for binary data, the contents of
+                        * abuf are a valid C string.  We have to do
+                        * encoding conversion before calling the typinput
+                        * routine, though.
+                        */
+                       pstring = (char *)
+                               pg_client_to_server((unsigned char *) abuf.data,
+                                                                       argsize);
+                       fcinfo->arg[i] =
+                               OidFunctionCall3(typInput,
+                                                                CStringGetDatum(pstring),
+                                                                ObjectIdGetDatum(typElem),
+                                                                Int32GetDatum(-1));
+                       /* Free result of encoding conversion, if any */
+                       if (pstring != abuf.data)
+                               pfree(pstring);
+               }
+               else if (aformat == 1)
+               {
+                       Oid                     typReceive;
+                       Oid                     typElem;
+
+                       /* Call the argument type's binary input converter */
+                       getTypeBinaryInputInfo(fip->argtypes[i], &typReceive, &typElem);
+
+                       fcinfo->arg[i] = OidFunctionCall2(typReceive,
+                                                                                         PointerGetDatum(&abuf),
+                                                                                         ObjectIdGetDatum(typElem));
+
+                       /* Trouble if it didn't eat the whole buffer */
+                       if (abuf.cursor != abuf.len)
+                               elog(ERROR, "Improper binary format in function argument %d",
+                                        i + 1);
                }
+               else
+                       elog(ERROR, "Invalid format code %d", aformat);
        }
 
-       /* XXX for the moment, ignore result format code */
-       (void) pq_getmsgint(msgBuf, 2);
+       /* Return result format code */
+       return (int16) pq_getmsgint(msgBuf, 2);
 }
 
 /*
  * Parse function arguments in a 2.0 protocol message
+ *
+ * Argument values are loaded into *fcinfo, and the desired result format
+ * is returned.
  */
-static void
+static int16
 parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info *fip,
                                                 FunctionCallInfo fcinfo)
 {
        int                     nargs;
        int                     i;
+       StringInfoData abuf;
 
        nargs = pq_getmsgint(msgBuf, 4);        /* # of arguments */
 
@@ -474,44 +495,54 @@ parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info *fip,
 
        fcinfo->nargs = nargs;
 
+       initStringInfo(&abuf);
+
        /*
-        * Copy supplied arguments into arg vector.  Note there is no way for
-        * frontend to specify a NULL argument --- this protocol is misdesigned.
+        * Copy supplied arguments into arg vector.  In protocol 2.0 these are
+        * always assumed to be supplied in binary format.
+        *
+        * Note: although the original protocol 2.0 code did not have any way for
+        * the frontend to specify a NULL argument, we now choose to interpret
+        * length == -1 as meaning a NULL.
         */
        for (i = 0; i < nargs; ++i)
        {
                int                     argsize;
-               char       *p;
+               Oid                     typReceive;
+               Oid                     typElem;
 
                argsize = pq_getmsgint(msgBuf, 4);
-               if (fip->argbyval[i])
-               {                                               /* by-value */
-                       if (argsize < 1 || argsize > 4)
-                               elog(ERROR, "HandleFunctionRequest: bogus argsize %d",
-                                        argsize);
-                       /* XXX should we demand argsize == fip->arglen[i] ? */
-                       fcinfo->arg[i] = (Datum) pq_getmsgint(msgBuf, argsize);
-               }
-               else
-               {                                               /* by-reference ... */
-                       if (fip->arglen[i] == -1)
-                       {                                       /* ... varlena */
-                               if (argsize < 0)
-                                       elog(ERROR, "HandleFunctionRequest: bogus argsize %d",
-                                                argsize);
-                               p = palloc(argsize + VARHDRSZ);
-                               VARATT_SIZEP(p) = argsize + VARHDRSZ;
-                               pq_copymsgbytes(msgBuf, VARDATA(p), argsize);
-                       }
-                       else
-                       {                                       /* ... fixed */
-                               if (argsize != fip->arglen[i])
-                                       elog(ERROR, "HandleFunctionRequest: bogus argsize %d, should be %d",
-                                                argsize, fip->arglen[i]);
-                               p = palloc(argsize + 1);                /* +1 in case argsize is 0 */
-                               pq_copymsgbytes(msgBuf, p, argsize);
-                       }
-                       fcinfo->arg[i] = PointerGetDatum(p);
+               if (argsize == -1)
+               {
+                       fcinfo->argnull[i] = true;
+                       continue;
                }
+               if (argsize < 0)
+                       elog(ERROR, "HandleFunctionRequest: bogus argsize %d",
+                                argsize);
+
+               /* Reset abuf to empty, and insert raw data into it */
+               abuf.len = 0;
+               abuf.data[0] = '\0';
+               abuf.cursor = 0;
+
+               appendBinaryStringInfo(&abuf,
+                                                          pq_getmsgbytes(msgBuf, argsize),
+                                                          argsize);
+
+               /* Call the argument type's binary input converter */
+               getTypeBinaryInputInfo(fip->argtypes[i], &typReceive, &typElem);
+
+               fcinfo->arg[i] = OidFunctionCall2(typReceive,
+                                                                                 PointerGetDatum(&abuf),
+                                                                                 ObjectIdGetDatum(typElem));
+
+               /* Trouble if it didn't eat the whole buffer */
+               if (abuf.cursor != abuf.len)
+                       elog(ERROR, "Improper binary format in function argument %d",
+                                i + 1);
        }
+
+       /* Desired result format is always binary in protocol 2.0 */
+       return 1;
 }
index dd235e8..f444302 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.341 2003/05/09 15:57:24 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.342 2003/05/09 18:08:48 tgl Exp $
  *
  * NOTES
  *       this is the "main" module of the postgres backend and
@@ -1342,8 +1342,21 @@ exec_bind_message(StringInfo input_message)
                                        }
                                        else if (pformat == 1)
                                        {
-                                               /* XXX something similar to above */
-                                               elog(ERROR, "Binary BIND not implemented yet");
+                                               Oid                     typReceive;
+                                               Oid                     typElem;
+
+                                               /* Call the parameter type's binary input converter */
+                                               getTypeBinaryInputInfo(ptype, &typReceive, &typElem);
+
+                                               params[i].value =
+                                                       OidFunctionCall2(typReceive,
+                                                                                        PointerGetDatum(&pbuf),
+                                                                                        ObjectIdGetDatum(typElem));
+
+                                               /* Trouble if it didn't eat the whole buffer */
+                                               if (pbuf.cursor != pbuf.len)
+                                                       elog(ERROR, "Improper binary format in BIND parameter %d",
+                                                                i + 1);
                                        }
                                        else
                                        {
@@ -2511,7 +2524,7 @@ PostgresMain(int argc, char *argv[], const char *username)
        if (!IsUnderPostmaster)
        {
                puts("\nPOSTGRES backend interactive interface ");
-               puts("$Revision: 1.341 $ $Date: 2003/05/09 15:57:24 $\n");
+               puts("$Revision: 1.342 $ $Date: 2003/05/09 18:08:48 $\n");
        }
 
        /*
@@ -2771,6 +2784,9 @@ PostgresMain(int argc, char *argv[], const char *username)
                                /* start an xact for this function invocation */
                                start_xact_command();
 
+                               /* switch back to message context */
+                               MemoryContextSwitchTo(MessageContext);
+
                                if (HandleFunctionRequest(input_message) == EOF)
                                {
                                        /* lost frontend connection during F message input */
index 26d06c4..49e545b 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.92 2003/04/08 23:20:02 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.93 2003/05/09 18:08:48 tgl Exp $
  *
  * NOTES
  *       Eventually, the index information should go through here, too.
@@ -1361,11 +1361,15 @@ getTypeInputInfo(Oid type, Oid *typInput, Oid *typElem)
                                                           ObjectIdGetDatum(type),
                                                           0, 0, 0);
        if (!HeapTupleIsValid(typeTuple))
-               elog(ERROR, "getTypeInputInfo: Cache lookup of type %u failed", type);
+               elog(ERROR, "Cache lookup of type %u failed", type);
        pt = (Form_pg_type) GETSTRUCT(typeTuple);
 
        if (!pt->typisdefined)
-               elog(ERROR, "Type \"%s\" is only a shell", NameStr(pt->typname));
+               elog(ERROR, "Type %s is only a shell",
+                        format_type_be(type));
+       if (!OidIsValid(pt->typinput))
+               elog(ERROR, "No input function available for type %s",
+                        format_type_be(type));
 
        *typInput = pt->typinput;
        *typElem = pt->typelem;
@@ -1377,10 +1381,8 @@ getTypeInputInfo(Oid type, Oid *typInput, Oid *typElem)
  * getTypeOutputInfo
  *
  *             Get info needed for printing values of a type
- *
- * Returns true if data valid (a false result probably means it's a shell type)
  */
-bool
+void
 getTypeOutputInfo(Oid type, Oid *typOutput, Oid *typElem,
                                  bool *typIsVarlena)
 {
@@ -1391,14 +1393,85 @@ getTypeOutputInfo(Oid type, Oid *typOutput, Oid *typElem,
                                                           ObjectIdGetDatum(type),
                                                           0, 0, 0);
        if (!HeapTupleIsValid(typeTuple))
-               elog(ERROR, "getTypeOutputInfo: Cache lookup of type %u failed", type);
+               elog(ERROR, "Cache lookup of type %u failed", type);
        pt = (Form_pg_type) GETSTRUCT(typeTuple);
 
+       if (!pt->typisdefined)
+               elog(ERROR, "Type %s is only a shell",
+                        format_type_be(type));
+       if (!OidIsValid(pt->typoutput))
+               elog(ERROR, "No output function available for type %s",
+                        format_type_be(type));
+
        *typOutput = pt->typoutput;
        *typElem = pt->typelem;
        *typIsVarlena = (!pt->typbyval) && (pt->typlen == -1);
+
+       ReleaseSysCache(typeTuple);
+}
+
+/*
+ * getTypeBinaryInputInfo
+ *
+ *             Get info needed for binary input of values of a type
+ */
+void
+getTypeBinaryInputInfo(Oid type, Oid *typReceive, Oid *typElem)
+{
+       HeapTuple       typeTuple;
+       Form_pg_type pt;
+
+       typeTuple = SearchSysCache(TYPEOID,
+                                                          ObjectIdGetDatum(type),
+                                                          0, 0, 0);
+       if (!HeapTupleIsValid(typeTuple))
+               elog(ERROR, "Cache lookup of type %u failed", type);
+       pt = (Form_pg_type) GETSTRUCT(typeTuple);
+
+       if (!pt->typisdefined)
+               elog(ERROR, "Type %s is only a shell",
+                        format_type_be(type));
+       if (!OidIsValid(pt->typreceive))
+               elog(ERROR, "No binary input function available for type %s",
+                        format_type_be(type));
+
+       *typReceive = pt->typreceive;
+       *typElem = pt->typelem;
+
+       ReleaseSysCache(typeTuple);
+}
+
+/*
+ * getTypeBinaryOutputInfo
+ *
+ *             Get info needed for binary output of values of a type
+ */
+void
+getTypeBinaryOutputInfo(Oid type, Oid *typSend, Oid *typElem,
+                                               bool *typIsVarlena)
+{
+       HeapTuple       typeTuple;
+       Form_pg_type pt;
+
+       typeTuple = SearchSysCache(TYPEOID,
+                                                          ObjectIdGetDatum(type),
+                                                          0, 0, 0);
+       if (!HeapTupleIsValid(typeTuple))
+               elog(ERROR, "Cache lookup of type %u failed", type);
+       pt = (Form_pg_type) GETSTRUCT(typeTuple);
+
+       if (!pt->typisdefined)
+               elog(ERROR, "Type %s is only a shell",
+                        format_type_be(type));
+       if (!OidIsValid(pt->typsend))
+               elog(ERROR, "No binary output function available for type %s",
+                        format_type_be(type));
+
+       *typSend = pt->typsend;
+       *typElem = pt->typelem;
+       *typIsVarlena = (!pt->typbyval) && (pt->typlen == -1);
+
        ReleaseSysCache(typeTuple);
-       return OidIsValid(*typOutput);
 }
 
 
index 610b836..848cc9f 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: lsyscache.h,v 1.68 2003/04/08 23:20:04 tgl Exp $
+ * $Id: lsyscache.h,v 1.69 2003/05/09 18:08:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -61,8 +61,11 @@ extern Oid   get_typ_typrelid(Oid typid);
 extern Oid     get_element_type(Oid typid);
 extern Oid     get_array_type(Oid typid);
 extern void getTypeInputInfo(Oid type, Oid *typInput, Oid *typElem);
-extern bool getTypeOutputInfo(Oid type, Oid *typOutput, Oid *typElem,
+extern void getTypeOutputInfo(Oid type, Oid *typOutput, Oid *typElem,
                                  bool *typIsVarlena);
+extern void getTypeBinaryInputInfo(Oid type, Oid *typReceive, Oid *typElem);
+extern void getTypeBinaryOutputInfo(Oid type, Oid *typSend, Oid *typElem,
+                                                                       bool *typIsVarlena);
 extern Oid     getBaseType(Oid typid);
 extern int32 get_typavgwidth(Oid typid, int32 typmod);
 extern int32 get_attavgwidth(Oid relid, AttrNumber attnum);