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 d5a29ef16c6d08fc746ceefd0281ca907a397035..6ad53ddb3ca2fdea5eb4338484fd8bbd2d3a6363 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 93d4b8406e87a59bea18ec5254c16a84747e1667..06c29c4894ba4464ed540cb143479a932b93d8f6 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 fe420ce978e7ce6d5a4b324aac6e57f4799c3210..785170b0c525b2ef6ac63c3d58fcb15b186dc8b6 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 0720f4e971df0ca59050a1a2ea6ad52ff03b1d13..74971c49c5de2066e9c7eb5d06487ab3b925c1ce 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 dd235e8765aa3fa373f1fe434ef07e4dae30d854..f444302e9651d8b72955aa65e32e8f9daa6c0a90 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 26d06c440fe5336d557598c035d39c7ec7777e35..49e545b68496ee7483c18e42c0fe6b809ed102f8 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 610b8361312e476b98dba732b6bac72bc153846b..848cc9f146e9c4415c2939ec62ee29d790fa8019 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);