Update 3.0 protocol support to match recent agreements about how to
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 8 May 2003 18:16:37 +0000 (18:16 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 8 May 2003 18:16:37 +0000 (18:16 +0000)
handle multiple 'formats' for data I/O.  Restructure CommandDest and
DestReceiver stuff one more time (it's finally starting to look a bit
clean though).  Code now matches latest 3.0 protocol document as far
as message formats go --- but there is no support for binary I/O yet.

24 files changed:
src/backend/access/common/printtup.c
src/backend/commands/copy.c
src/backend/commands/explain.c
src/backend/commands/portalcmds.c
src/backend/commands/prepare.c
src/backend/executor/execMain.c
src/backend/executor/execTuples.c
src/backend/executor/functions.c
src/backend/executor/spi.c
src/backend/executor/tstoreReceiver.c
src/backend/libpq/pqformat.c
src/backend/tcop/dest.c
src/backend/tcop/fastpath.c
src/backend/tcop/postgres.c
src/backend/tcop/pquery.c
src/include/access/printtup.h
src/include/executor/execdesc.h
src/include/libpq/pqcomm.h
src/include/libpq/pqformat.h
src/include/tcop/dest.h
src/include/tcop/pquery.h
src/include/utils/portal.h
src/interfaces/libpq/fe-exec.c
src/interfaces/libpq/libpq-int.h

index 35355b8b9b9c132aa1056fd06c69e01b4717dfed..d5a29ef16c6d08fc746ceefd0281ca907a397035 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.70 2003/05/06 20:26:26 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.71 2003/05/08 18:16:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "libpq/libpq.h"
 #include "libpq/pqformat.h"
 #include "utils/lsyscache.h"
+#include "utils/portal.h"
 
 
 static void printtup_startup(DestReceiver *self, int operation,
-              const char *portalName, TupleDesc typeinfo, List *targetlist);
-static void printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self);
-static void printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self);
+                            TupleDesc typeinfo);
+static void printtup(HeapTuple tuple, TupleDesc typeinfo,
+                    DestReceiver *self);
+static void printtup_20(HeapTuple tuple, TupleDesc typeinfo,
+                       DestReceiver *self);
+static void printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo,
+                                DestReceiver *self);
 static void printtup_shutdown(DestReceiver *self);
 static void printtup_destroy(DestReceiver *self);
 
@@ -50,6 +55,7 @@ typedef struct
 typedef struct
 {
    DestReceiver pub;           /* publicly-known function pointers */
+   Portal      portal;         /* the Portal we are printing from */
    bool        sendDescrip;    /* send RowDescription at startup? */
    TupleDesc   attrinfo;       /* The attr info we are set up for */
    int         nattrs;
@@ -61,43 +67,33 @@ typedef struct
  * ----------------
  */
 DestReceiver *
-printtup_create_DR(CommandDest dest)
+printtup_create_DR(CommandDest dest, Portal portal)
 {
    DR_printtup *self = (DR_printtup *) palloc(sizeof(DR_printtup));
-   bool    isBinary;
-   bool    sendDescrip;
 
-   switch (dest)
+   if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
+       self->pub.receiveTuple = printtup;
+   else
    {
-       case Remote:
-           isBinary = false;
-           sendDescrip = true;
-           break;
-       case RemoteInternal:
-           isBinary = true;
-           sendDescrip = true;
-           break;
-       case RemoteExecute:
-           isBinary = false;
-           sendDescrip = false; /* no T message for Execute */
-           break;
-       case RemoteExecuteInternal:
-           isBinary = true;
-           sendDescrip = false; /* no T message for Execute */
-           break;
-
-       default:
-           elog(ERROR, "printtup_create_DR: unsupported dest");
-           return NULL;
+       /*
+        * In protocol 2.0 the Bind message does not exist, so there is
+        * no way for the columns to have different print formats; it's
+        * sufficient to look at the first one.
+        */
+       if (portal->formats && portal->formats[0] != 0)
+           self->pub.receiveTuple = printtup_internal_20;
+       else
+           self->pub.receiveTuple = printtup_20;
    }
-
-   self->pub.receiveTuple = isBinary ? printtup_internal : printtup;
    self->pub.startup = printtup_startup;
    self->pub.shutdown = printtup_shutdown;
    self->pub.destroy = printtup_destroy;
    self->pub.mydest = dest;
 
-   self->sendDescrip = sendDescrip;
+   self->portal = portal;
+
+   /* Send T message automatically if Remote, but not if RemoteExecute */
+   self->sendDescrip = (dest == Remote);
 
    self->attrinfo = NULL;
    self->nattrs = 0;
@@ -107,10 +103,10 @@ printtup_create_DR(CommandDest dest)
 }
 
 static void
-printtup_startup(DestReceiver *self, int operation,
-                const char *portalName, TupleDesc typeinfo, List *targetlist)
+printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 {
    DR_printtup *myState = (DR_printtup *) self;
+   Portal  portal = myState->portal;
 
    if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
    {
@@ -119,7 +115,9 @@ printtup_startup(DestReceiver *self, int operation,
         *
         * If portal name not specified, use "blank" portal.
         */
-       if (portalName == NULL)
+       const char *portalName = portal->name;
+
+       if (portalName == NULL || portalName[0] == '\0')
            portalName = "blank";
 
        pq_puttextmessage('P', portalName);
@@ -130,7 +128,16 @@ printtup_startup(DestReceiver *self, int operation,
     * then we send back the tuple descriptor of the tuples.  
     */
    if (operation == CMD_SELECT && myState->sendDescrip)
-       SendRowDescriptionMessage(typeinfo, targetlist);
+   {
+       List       *targetlist;
+
+       if (portal->strategy == PORTAL_ONE_SELECT)
+           targetlist = ((Query *) lfirst(portal->parseTrees))->targetList;
+       else
+           targetlist = NIL;
+
+       SendRowDescriptionMessage(typeinfo, targetlist, portal->formats);
+   }
 
    /* ----------------
     * We could set up the derived attr info at this time, but we postpone it
@@ -150,11 +157,13 @@ printtup_startup(DestReceiver *self, int operation,
  * Notes: the TupleDesc has typically been manufactured by ExecTypeFromTL()
  * or some similar function; it does not contain a full set of fields.
  * The targetlist will be NIL when executing a utility function that does
- * not have a plan.  If the targetlist isn't NIL then it is a Plan node's
- * targetlist; it is up to us to ignore resjunk columns in it.
+ * not have a plan.  If the targetlist isn't NIL then it is a Query node's
+ * targetlist; it is up to us to ignore resjunk columns in it.  The formats[]
+ * array pointer might be NULL (if we are doing Describe on a prepared stmt);
+ * send zeroes for the format codes in that case.
  */
 void
-SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist)
+SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, int16 *formats)
 {
    Form_pg_attribute *attrs = typeinfo->attrs;
    int         natts = typeinfo->natts;
@@ -198,6 +207,14 @@ SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist)
        if (proto >= 2)
            pq_sendint(&buf, attrs[i]->atttypmod,
                       sizeof(attrs[i]->atttypmod));
+       /* format info appears in protocol 3.0 and up */
+       if (proto >= 3)
+       {
+           if (formats)
+               pq_sendint(&buf, formats[i], 2);
+           else
+               pq_sendint(&buf, 0, 2);
+       }
    }
    pq_endmessage(&buf);
 }
@@ -228,11 +245,98 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
 }
 
 /* ----------------
- *     printtup
+ *     printtup --- print a tuple in protocol 3.0
  * ----------------
  */
 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;
+
+   /* Set or update my derived attribute info, if needed */
+   if (myState->attrinfo != typeinfo || myState->nattrs != natts)
+       printtup_prepare_info(myState, typeinfo, natts);
+
+   /*
+    * Prepare a DataRow message
+    */
+   pq_beginmessage(&buf, 'D');
+
+   pq_sendint(&buf, natts, 2);
+
+   /*
+    * send the attributes of this tuple
+    */
+   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)
+       {
+           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);
+
+               /* 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)
+       {
+           /* XXX something similar to above */
+           elog(ERROR, "Binary transmission not implemented yet");
+       }
+       else
+       {
+           elog(ERROR, "Invalid format code %d", format);
+       }
+   }
+
+   pq_endmessage(&buf);
+}
+
+/* ----------------
+ *     printtup_20 --- print a tuple in protocol 2.0
+ * ----------------
+ */
+static void
+printtup_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
 {
    DR_printtup *myState = (DR_printtup *) self;
    StringInfoData buf;
@@ -300,7 +404,7 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
                                    ObjectIdGetDatum(thisState->typelem),
                          Int32GetDatum(typeinfo->attrs[i]->atttypmod)));
 
-           pq_sendcountedtext(&buf, outputstr, strlen(outputstr));
+           pq_sendcountedtext(&buf, outputstr, strlen(outputstr), true);
 
            /* Clean up detoasted copy, if any */
            if (attr != origattr)
@@ -310,7 +414,7 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
        else
        {
            outputstr = "<unprintable>";
-           pq_sendcountedtext(&buf, outputstr, strlen(outputstr));
+           pq_sendcountedtext(&buf, outputstr, strlen(outputstr), true);
        }
    }
 
@@ -363,38 +467,23 @@ printatt(unsigned attributeId,
           attributeP->attbyval ? 't' : 'f');
 }
 
-/* ----------------
- *     showatts
- * ----------------
- */
-static void
-showatts(const char *name, TupleDesc tupleDesc)
-{
-   int         natts = tupleDesc->natts;
-   Form_pg_attribute *attinfo = tupleDesc->attrs;
-   int         i;
-
-   puts(name);
-   for (i = 0; i < natts; ++i)
-       printatt((unsigned) i + 1, attinfo[i], (char *) NULL);
-   printf("\t----\n");
-}
-
 /* ----------------
  *     debugStartup - prepare to print tuples for an interactive backend
  * ----------------
  */
 void
-debugStartup(DestReceiver *self, int operation,
-            const char *portalName, TupleDesc typeinfo, List *targetlist)
+debugStartup(DestReceiver *self, int operation, TupleDesc typeinfo)
 {
+   int         natts = typeinfo->natts;
+   Form_pg_attribute *attinfo = typeinfo->attrs;
+   int         i;
+
    /*
     * show the return type of the tuples
     */
-   if (portalName == NULL)
-       portalName = "blank";
-
-   showatts(portalName, typeinfo);
+   for (i = 0; i < natts; ++i)
+       printatt((unsigned) i + 1, attinfo[i], (char *) NULL);
+   printf("\t----\n");
 }
 
 /* ----------------
@@ -448,15 +537,16 @@ debugtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
 }
 
 /* ----------------
- *     printtup_internal
- *     We use a different data prefix, e.g. 'B' instead of 'D' to
- *     indicate a tuple in internal (binary) form.
+ *     printtup_internal_20 --- print a binary tuple in protocol 2.0
+ *
+ * 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, except we don't use the typout func.
+ * This is largely same as printtup_20, except we don't use the typout func.
  * ----------------
  */
 static void
-printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
+printtup_internal_20(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
 {
    DR_printtup *myState = (DR_printtup *) self;
    StringInfoData buf;
index ec6e771aedcaba3f81655d96c545115d1806c40b..93d4b8406e87a59bea18ec5254c16a84747e1667 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.197 2003/04/25 02:28:22 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.198 2003/05/08 18:16:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -125,8 +125,8 @@ static int  server_encoding;
 /*
  * Internal communications functions
  */
-static void SendCopyBegin(bool binary);
-static void ReceiveCopyBegin(bool binary);
+static void SendCopyBegin(bool binary, int natts);
+static void ReceiveCopyBegin(bool binary, int natts);
 static void SendCopyEnd(bool binary);
 static void CopySendData(void *databuf, int datasize);
 static void CopySendString(const char *str);
@@ -143,15 +143,20 @@ static void CopyDonePeek(int c, bool pickup);
  * in past protocol redesigns.
  */
 static void
-SendCopyBegin(bool binary)
+SendCopyBegin(bool binary, int natts)
 {
    if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
    {
        /* new way */
        StringInfoData buf;
+       int16   format = (binary ? 1 : 0);
+       int     i;
 
        pq_beginmessage(&buf, 'H');
-       pq_sendbyte(&buf, binary ? 1 : 0);
+       pq_sendbyte(&buf, format);          /* overall format */
+       pq_sendint(&buf, natts, 2);
+       for (i = 0; i < natts; i++)
+           pq_sendint(&buf, format, 2);    /* per-column formats */
        pq_endmessage(&buf);
        copy_dest = COPY_NEW_FE;
        copy_msgbuf = makeStringInfo();
@@ -179,15 +184,20 @@ SendCopyBegin(bool binary)
 }
 
 static void
-ReceiveCopyBegin(bool binary)
+ReceiveCopyBegin(bool binary, int natts)
 {
    if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
    {
        /* new way */
        StringInfoData buf;
+       int16   format = (binary ? 1 : 0);
+       int     i;
 
        pq_beginmessage(&buf, 'G');
-       pq_sendbyte(&buf, binary ? 1 : 0);
+       pq_sendbyte(&buf, format);          /* overall format */
+       pq_sendint(&buf, natts, 2);
+       for (i = 0; i < natts; i++)
+           pq_sendint(&buf, format, 2);    /* per-column formats */
        pq_endmessage(&buf);
        copy_dest = COPY_NEW_FE;
        copy_msgbuf = makeStringInfo();
@@ -682,7 +692,7 @@ DoCopy(const CopyStmt *stmt)
        if (pipe)
        {
            if (IsUnderPostmaster)
-               ReceiveCopyBegin(binary);
+               ReceiveCopyBegin(binary, length(attnumlist));
            else
                copy_file = stdin;
        }
@@ -724,7 +734,7 @@ DoCopy(const CopyStmt *stmt)
        if (pipe)
        {
            if (IsUnderPostmaster)
-               SendCopyBegin(binary);
+               SendCopyBegin(binary, length(attnumlist));
            else
                copy_file = stdout;
        }
index 28a6e0378c8c27088440b6fd404b661e468359bf..879a7e6d6825f5cc40857248a2414bfd81b2ad59 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.108 2003/05/06 20:26:26 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.109 2003/05/08 18:16:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -179,7 +179,7 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate)
    plan = planner(query, isCursor, cursorOptions);
 
    /* Create a QueryDesc requesting no output */
-   queryDesc = CreateQueryDesc(query, plan, None_Receiver, NULL, NULL,
+   queryDesc = CreateQueryDesc(query, plan, None_Receiver, NULL,
                                stmt->analyze);
 
    ExplainOnePlan(queryDesc, stmt, tstate);
index 1c51a1bb89fece70ddc95ba9bdf79273935dccf5..f9e31c3aaa1cdba27089e36cf94328ae5710ee07 100644 (file)
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.15 2003/05/06 20:26:26 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.16 2003/05/08 18:16:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -25,7 +25,6 @@
 
 #include "commands/portalcmds.h"
 #include "executor/executor.h"
-#include "executor/tstoreReceiver.h"
 #include "optimizer/planner.h"
 #include "rewrite/rewriteHandler.h"
 #include "tcop/pquery.h"
@@ -145,7 +144,6 @@ PerformPortalFetch(FetchStmt *stmt,
                   DestReceiver *dest,
                   char *completionTag)
 {
-   DestReceiver *mydest = dest;
    Portal      portal;
    long        nprocessed;
 
@@ -168,35 +166,21 @@ PerformPortalFetch(FetchStmt *stmt,
        return;
    }
 
-   /*
-    * Adjust dest if needed.  MOVE wants destination None.
-    *
-    * If fetching from a binary cursor and the requested destination is
-    * Remote, change it to RemoteInternal.  Note we do NOT change if the
-    * destination is RemoteExecute --- so the Execute message's format
-    * specification wins out over the cursor's type.
-    */
+   /* Adjust dest if needed.  MOVE wants destination None */
    if (stmt->ismove)
-       mydest = CreateDestReceiver(None);
-   else if (dest->mydest == Remote &&
-            (portal->cursorOptions & CURSOR_OPT_BINARY))
-       mydest = CreateDestReceiver(RemoteInternal);
+       dest = None_Receiver;
 
    /* Do it */
    nprocessed = PortalRunFetch(portal,
                                stmt->direction,
                                stmt->howMany,
-                               mydest);
+                               dest);
 
    /* Return command status if wanted */
    if (completionTag)
        snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %ld",
                 stmt->ismove ? "MOVE" : "FETCH",
                 nprocessed);
-
-   /* Clean up if we created a local destination */
-   if (mydest != dest)
-       (mydest->destroy) (mydest);
 }
 
 /*
@@ -329,8 +313,7 @@ PersistHoldablePortal(Portal portal)
    ExecutorRewind(queryDesc);
 
    /* Change the destination to output to the tuplestore */
-   queryDesc->dest = CreateTuplestoreDestReceiver(portal->holdStore,
-                                                  portal->holdContext);
+   queryDesc->dest = CreateDestReceiver(Tuplestore, portal);
 
    /* Fetch the result set into the tuplestore */
    ExecutorRun(queryDesc, ForwardScanDirection, 0L);
index 6e16853fd78a11dba6c9d94694bc520825986671..433fd8e049e86a7a4c8419ac43a2385c6c40f981 100644 (file)
@@ -10,7 +10,7 @@
  * Copyright (c) 2002-2003, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.17 2003/05/06 21:51:41 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.18 2003/05/08 18:16:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -528,7 +528,7 @@ ExplainExecuteQuery(ExplainStmt *stmt, TupOutputState *tstate)
            }
 
            /* Create a QueryDesc requesting no output */
-           qdesc = CreateQueryDesc(query, plan, None_Receiver, NULL,
+           qdesc = CreateQueryDesc(query, plan, None_Receiver,
                                    paramLI, stmt->analyze);
 
            ExplainOnePlan(qdesc, stmt, tstate);
index 3d1b950e210021f2d17a726b4a3fd2430066fc50..8b720c110fd80272b8ec8bfa95b9721f6088eb0e 100644 (file)
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.208 2003/05/06 20:26:26 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.209 2003/05/08 18:16:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -217,10 +217,7 @@ ExecutorRun(QueryDesc *queryDesc,
    estate->es_processed = 0;
    estate->es_lastoid = InvalidOid;
 
-   (*dest->startup) (dest, operation,
-                     queryDesc->portalName,
-                     queryDesc->tupDesc,
-                     queryDesc->planstate->plan->targetlist);
+   (*dest->startup) (dest, operation, queryDesc->tupDesc);
 
    /*
     * run plan
index 2e040f7890351c63afcb18038aa3dec99a391db0..cb85be630f9ade7f3f3b84ccd7dd0ef8057fcc54 100644 (file)
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.65 2003/05/06 20:26:27 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.66 2003/05/08 18:16:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -765,8 +765,7 @@ begin_tup_output_tupdesc(DestReceiver *dest, TupleDesc tupdesc)
    tstate->metadata = TupleDescGetAttInMetadata(tupdesc);
    tstate->dest = dest;
 
-   (*tstate->dest->startup) (tstate->dest, (int) CMD_SELECT,
-                             NULL, tupdesc, NIL);
+   (*tstate->dest->startup) (tstate->dest, (int) CMD_SELECT, tupdesc);
 
    return tstate;
 }
index e89e5f32d645447bca98df271cc5e963806fd5bc..46d1e51c4b9b9eac600d4ac36cbfa2ea14680fdd 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.64 2003/05/06 20:26:27 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.65 2003/05/08 18:16:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -245,7 +245,7 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache)
 {
    Assert(es->qd == NULL);
    es->qd = CreateQueryDesc(es->query, es->plan,
-                            None_Receiver, NULL,
+                            None_Receiver,
                             fcache->paramLI, false);
 
    /* Utility commands don't need Executor. */
index c3b3ec3530a6462e5a5ffbd2776a49f3347b77af..fe420ce978e7ce6d5a4b324aac6e57f4799c3210 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.96 2003/05/06 20:26:27 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.97 2003/05/08 18:16:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -841,7 +841,8 @@ SPI_cursor_find(const char *name)
 void
 SPI_cursor_fetch(Portal portal, bool forward, int count)
 {
-   _SPI_cursor_operation(portal, forward, count, CreateDestReceiver(SPI));
+   _SPI_cursor_operation(portal, forward, count,
+                         CreateDestReceiver(SPI, NULL));
    /* we know that the SPI receiver doesn't need a destroy call */
 }
 
@@ -880,8 +881,7 @@ SPI_cursor_close(Portal portal)
  *     of current SPI procedure
  */
 void
-spi_dest_startup(DestReceiver *self, int operation,
-                const char *portalName, TupleDesc typeinfo, List *targetlist)
+spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 {
    SPITupleTable *tuptable;
    MemoryContext oldcxt;
@@ -1035,7 +1035,7 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
            planTree = pg_plan_query(queryTree);
            plan_list = lappend(plan_list, planTree);
 
-           dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None);
+           dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None, NULL);
            if (queryTree->commandType == CMD_UTILITY)
            {
                if (IsA(queryTree->utilityStmt, CopyStmt))
@@ -1061,7 +1061,7 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
            else if (plan == NULL)
            {
                qdesc = CreateQueryDesc(queryTree, planTree, dest,
-                                       NULL, NULL, false);
+                                       NULL, false);
                res = _SPI_pquery(qdesc, true,
                                  queryTree->canSetTag ? tcount : 0);
                if (res < 0)
@@ -1071,7 +1071,7 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
            else
            {
                qdesc = CreateQueryDesc(queryTree, planTree, dest,
-                                       NULL, NULL, false);
+                                       NULL, false);
                res = _SPI_pquery(qdesc, false, 0);
                if (res < 0)
                    return res;
@@ -1150,7 +1150,7 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
            planTree = lfirst(plan_list);
            plan_list = lnext(plan_list);
 
-           dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None);
+           dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None, NULL);
            if (queryTree->commandType == CMD_UTILITY)
            {
                ProcessUtility(queryTree->utilityStmt, dest, NULL);
@@ -1160,7 +1160,7 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
            else
            {
                qdesc = CreateQueryDesc(queryTree, planTree, dest,
-                                       NULL, paramLI, false);
+                                       paramLI, false);
                res = _SPI_pquery(qdesc, true,
                                  queryTree->canSetTag ? tcount : 0);
                if (res < 0)
index bcab8154a323bc355c4cf39f7a67ab76328f9b1d..3d8479faee24fa352487e160a76e47e689e7b742 100644 (file)
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/executor/tstoreReceiver.c,v 1.5 2003/05/06 20:26:27 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/executor/tstoreReceiver.c,v 1.6 2003/05/08 18:16:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,9 +31,7 @@ typedef struct
  * Prepare to receive tuples from executor.
  */
 static void
-tstoreStartupReceiver(DestReceiver *self, int operation,
-                     const char *portalname,
-                     TupleDesc typeinfo, List *targetlist)
+tstoreStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo)
 {
    /* do nothing */
 }
index dacfa93ecc7c0f2bef6a8d18e929df7ef122c6d1..6f574e383b72c38ded820f3d663b426b6a836d14 100644 (file)
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Header: /cvsroot/pgsql/src/backend/libpq/pqformat.c,v 1.28 2003/04/22 00:08:06 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/libpq/pqformat.c,v 1.29 2003/05/08 18:16:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -104,27 +104,32 @@ pq_sendbytes(StringInfo buf, const char *data, int datalen)
  *     pq_sendcountedtext - append a text string (with character set conversion)
  *
  * The data sent to the frontend by this routine is a 4-byte count field
- * (the count includes itself, by convention) followed by the string.
+ * followed by the string.  The count includes itself or not, as per the
+ * countincludesself flag (pre-3.0 protocol requires it to include itself).
  * The passed text string need not be null-terminated, and the data sent
  * to the frontend isn't either.
  * --------------------------------
  */
 void
-pq_sendcountedtext(StringInfo buf, const char *str, int slen)
+pq_sendcountedtext(StringInfo buf, const char *str, int slen,
+                  bool countincludesself)
 {
+   int         extra = countincludesself ? 4 : 0;
    char       *p;
 
    p = (char *) pg_server_to_client((unsigned char *) str, slen);
    if (p != str)               /* actual conversion has been done? */
    {
        slen = strlen(p);
-       pq_sendint(buf, slen + 4, 4);
+       pq_sendint(buf, slen + extra, 4);
        appendBinaryStringInfo(buf, p, slen);
        pfree(p);
-       return;
    }
-   pq_sendint(buf, slen + 4, 4);
-   appendBinaryStringInfo(buf, str, slen);
+   else
+   {
+       pq_sendint(buf, slen + extra, 4);
+       appendBinaryStringInfo(buf, str, slen);
+   }
 }
 
 /* --------------------------------
@@ -296,7 +301,7 @@ pq_getmsgbytes(StringInfo msg, int datalen)
 {
    const char *result;
 
-   if (datalen > (msg->len - msg->cursor))
+   if (datalen < 0 || datalen > (msg->len - msg->cursor))
        elog(ERROR, "pq_getmsgbytes: insufficient data left in message");
    result = &msg->data[msg->cursor];
    msg->cursor += datalen;
@@ -312,7 +317,7 @@ pq_getmsgbytes(StringInfo msg, int datalen)
 void
 pq_copymsgbytes(StringInfo msg, char *buf, int datalen)
 {
-   if (datalen > (msg->len - msg->cursor))
+   if (datalen < 0 || datalen > (msg->len - msg->cursor))
        elog(ERROR, "pq_copymsgbytes: insufficient data left in message");
    memcpy(buf, &msg->data[msg->cursor], datalen);
    msg->cursor += datalen;
index bce77603f5b9b0ed60de3675e59cf9b813be2056..a590cffd35a273e8a10e93e88f98ae4d1aea53ea 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.57 2003/05/06 20:26:27 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.58 2003/05/08 18:16:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "access/printtup.h"
 #include "access/xact.h"
+#include "executor/tstoreReceiver.h"
 #include "libpq/libpq.h"
 #include "libpq/pqformat.h"
+#include "utils/portal.h"
 
 
 /* ----------------
@@ -44,8 +46,7 @@ donothingReceive(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
 }
 
 static void
-donothingStartup(DestReceiver *self, int operation,
-                const char *portalName, TupleDesc typeinfo, List *targetlist)
+donothingStartup(DestReceiver *self, int operation, TupleDesc typeinfo)
 {
 }
 
@@ -90,18 +91,21 @@ BeginCommand(const char *commandTag, CommandDest dest)
 
 /* ----------------
  *     CreateDestReceiver - return appropriate receiver function set for dest
+ *
+ * Note: a Portal must be specified for destinations Remote, RemoteExecute,
+ * and Tuplestore.  It can be NULL for the others.
  * ----------------
  */
 DestReceiver *
-CreateDestReceiver(CommandDest dest)
+CreateDestReceiver(CommandDest dest, Portal portal)
 {
    switch (dest)
    {
        case Remote:
-       case RemoteInternal:
        case RemoteExecute:
-       case RemoteExecuteInternal:
-           return printtup_create_DR(dest);
+           if (portal == NULL)
+               elog(ERROR, "CreateDestReceiver: no portal specified");
+           return printtup_create_DR(dest, portal);
 
        case None:
            return &donothingDR;
@@ -113,12 +117,13 @@ CreateDestReceiver(CommandDest dest)
            return &spi_printtupDR;
 
        case Tuplestore:
-           /*
-            * This is disallowed, you must use tstoreReceiver.c's
-            * specialized function to create a Tuplestore DestReceiver
-            */
-           elog(ERROR, "CreateDestReceiver: cannot handle Tuplestore");
-           break;
+           if (portal == NULL)
+               elog(ERROR, "CreateDestReceiver: no portal specified");
+           if (portal->holdStore == NULL ||
+               portal->holdContext == NULL)
+               elog(ERROR, "CreateDestReceiver: portal has no holdStore");
+           return CreateTuplestoreDestReceiver(portal->holdStore,
+                                               portal->holdContext);
    }
 
    /* should never get here */
@@ -135,9 +140,7 @@ EndCommand(const char *commandTag, CommandDest dest)
    switch (dest)
    {
        case Remote:
-       case RemoteInternal:
        case RemoteExecute:
-       case RemoteExecuteInternal:
            pq_puttextmessage('C', commandTag);
            break;
 
@@ -167,9 +170,7 @@ NullCommand(CommandDest dest)
    switch (dest)
    {
        case Remote:
-       case RemoteInternal:
        case RemoteExecute:
-       case RemoteExecuteInternal:
 
            /*
             * tell the fe that we saw an empty query string.  In protocols
@@ -206,9 +207,7 @@ ReadyForQuery(CommandDest dest)
    switch (dest)
    {
        case Remote:
-       case RemoteInternal:
        case RemoteExecute:
-       case RemoteExecuteInternal:
            if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
            {
                StringInfoData buf;
index 65161c54ff3cb6d2ad63da391862ad0da9223917..0720f4e971df0ca59050a1a2ea6ad52ff03b1d13 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.61 2003/05/05 00:44:56 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.62 2003/05/08 18:16:36 tgl Exp $
  *
  * NOTES
  *   This cruft is the server side of PQfn.
 #include "utils/tqual.h"
 
 
+/*
+ * Formerly, this code attempted to cache the function and type info
+ * looked up by fetch_fp_info, but only for the duration of a single
+ * transaction command (since in theory the info could change between
+ * commands).  This was utterly useless, because postgres.c executes
+ * each fastpath call as a separate transaction command, and so the
+ * cached data could never actually have been reused.  If it had worked
+ * as intended, it would have had problems anyway with dangling references
+ * in the FmgrInfo struct. So, forget about caching and just repeat the
+ * syscache fetches on each usage. They're not *that* expensive.
+ */
+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;
+};
+
+
+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);
+
+
 /* ----------------
  *     GetOldFunctionMessage
  *
@@ -121,56 +149,72 @@ SendFunctionResult(Datum retval, bool retbyval, int retlen)
 
    pq_beginmessage(&buf, 'V');
 
-   if (retlen != 0)
+   if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
    {
-       pq_sendbyte(&buf, 'G');
-       if (retbyval)
-       {                       /* by-value */
-           pq_sendint(&buf, retlen, 4);
-           pq_sendint(&buf, DatumGetInt32(retval), retlen);
+       /* 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
-       {                       /* 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);
+       {
+           /* NULL marker */
+           pq_sendint(&buf, -1, 4);
+       }
+   }
+   else
+   {
+       /* Old-style message */
+       if (retlen != 0)
+       {
+           pq_sendbyte(&buf, 'G');
+           if (retbyval)
+           {                       /* by-value */
+               pq_sendint(&buf, retlen, 4);
+               pq_sendint(&buf, DatumGetInt32(retval), retlen);
            }
            else
-           {                   /* ... fixed */
-               pq_sendint(&buf, retlen, 4);
-               pq_sendbytes(&buf, DatumGetPointer(retval), retlen);
+           {                       /* 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);
+               }
            }
        }
+       pq_sendbyte(&buf, '0');
    }
 
-   pq_sendbyte(&buf, '0');
    pq_endmessage(&buf);
 }
 
-/*
- * Formerly, this code attempted to cache the function and type info
- * looked up by fetch_fp_info, but only for the duration of a single
- * transaction command (since in theory the info could change between
- * commands).  This was utterly useless, because postgres.c executes
- * each fastpath call as a separate transaction command, and so the
- * cached data could never actually have been reused.  If it had worked
- * as intended, it would have had problems anyway with dangling references
- * in the FmgrInfo struct. So, forget about caching and just repeat the
- * syscache fetches on each usage. They're not *that* expensive.
- */
-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;
-};
-
 /*
  * fetch_fp_info
  *
@@ -262,11 +306,9 @@ int
 HandleFunctionRequest(StringInfo msgBuf)
 {
    Oid         fid;
-   int         nargs;
    AclResult   aclresult;
    FunctionCallInfoData fcinfo;
    Datum       retval;
-   int         i;
    struct fp_info my_fp;
    struct fp_info *fip;
 
@@ -294,9 +336,10 @@ HandleFunctionRequest(StringInfo msgBuf)
    /*
     * Parse the buffer contents.
     */
-   (void) pq_getmsgstring(msgBuf); /* dummy string */
+   if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
+       (void) pq_getmsgstring(msgBuf); /* dummy string */
+
    fid = (Oid) pq_getmsgint(msgBuf, 4); /* function oid */
-   nargs = pq_getmsgint(msgBuf, 4);    /* # of arguments */
 
    /*
     * There used to be a lame attempt at caching lookup info here. Now we
@@ -316,19 +359,61 @@ HandleFunctionRequest(StringInfo msgBuf)
    SetQuerySnapshot();
 
    /*
-    * Prepare function call info block.
+    * Prepare function call info block and insert arguments.
     */
+   MemSet(&fcinfo, 0, sizeof(fcinfo));
+   fcinfo.flinfo = &fip->flinfo;
+
+   if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
+       parse_fcall_arguments(msgBuf, fip, &fcinfo);
+   else
+       parse_fcall_arguments_20(msgBuf, fip, &fcinfo);
+
+   /* Verify we reached the end of the message where expected. */
+   pq_getmsgend(msgBuf);
+
+   /* Okay, do it ... */
+   retval = FunctionCallInvoke(&fcinfo);
+
+   if (fcinfo.isnull)
+       SendFunctionResult(retval, fip->retbyval, 0);
+   else
+       SendFunctionResult(retval, fip->retbyval, fip->retlen);
+
+   return 0;
+}
+
+/*
+ * Parse function arguments in a 3.0 protocol message
+ */
+static void
+parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
+                     FunctionCallInfo fcinfo)
+{
+   int         nargs;
+   int         i;
+   int         numAFormats;
+   int16      *aformats = NULL;
+
+   /* Get the argument format codes */
+   numAFormats = pq_getmsgint(msgBuf, 2);
+   if (numAFormats > 0)
+   {
+       aformats = (int16 *) palloc(numAFormats * sizeof(int16));
+       for (i = 0; i < numAFormats; i++)
+           aformats[i] = pq_getmsgint(msgBuf, 2);
+   }
+
+   nargs = pq_getmsgint(msgBuf, 2);    /* # of arguments */
+
    if (fip->flinfo.fn_nargs != nargs || nargs > FUNC_MAX_ARGS)
        elog(ERROR, "HandleFunctionRequest: actual arguments (%d) != registered arguments (%d)",
             nargs, fip->flinfo.fn_nargs);
 
-   MemSet(&fcinfo, 0, sizeof(fcinfo));
-   fcinfo.flinfo = &fip->flinfo;
-   fcinfo.nargs = nargs;
+   fcinfo->nargs = nargs;
 
    /*
-    * 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.
     */
    for (i = 0; i < nargs; ++i)
    {
@@ -342,7 +427,7 @@ HandleFunctionRequest(StringInfo msgBuf)
                elog(ERROR, "HandleFunctionRequest: bogus argsize %d",
                     argsize);
            /* XXX should we demand argsize == fip->arglen[i] ? */
-           fcinfo.arg[i] = (Datum) pq_getmsgint(msgBuf, argsize);
+           fcinfo->arg[i] = (Datum) pq_getmsgint(msgBuf, argsize);
        }
        else
        {                       /* by-reference ... */
@@ -363,25 +448,70 @@ HandleFunctionRequest(StringInfo msgBuf)
                p = palloc(argsize + 1);        /* +1 in case argsize is 0 */
                pq_copymsgbytes(msgBuf, p, argsize);
            }
-           fcinfo.arg[i] = PointerGetDatum(p);
+           fcinfo->arg[i] = PointerGetDatum(p);
        }
    }
 
-   /* Verify we reached the end of the message where expected. */
-   pq_getmsgend(msgBuf);
+   /* XXX for the moment, ignore result format code */
+   (void) pq_getmsgint(msgBuf, 2);
+}
 
-#ifdef NO_FASTPATH
-   /* force a NULL return */
-   retval = (Datum) 0;
-   fcinfo.isnull = true;
-#else
-   retval = FunctionCallInvoke(&fcinfo);
-#endif   /* NO_FASTPATH */
+/*
+ * Parse function arguments in a 2.0 protocol message
+ */
+static void
+parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info *fip,
+                        FunctionCallInfo fcinfo)
+{
+   int         nargs;
+   int         i;
 
-   if (fcinfo.isnull)
-       SendFunctionResult(retval, fip->retbyval, 0);
-   else
-       SendFunctionResult(retval, fip->retbyval, fip->retlen);
+   nargs = pq_getmsgint(msgBuf, 4);    /* # of arguments */
 
-   return 0;
+   if (fip->flinfo.fn_nargs != nargs || nargs > FUNC_MAX_ARGS)
+       elog(ERROR, "HandleFunctionRequest: actual arguments (%d) != registered arguments (%d)",
+            nargs, fip->flinfo.fn_nargs);
+
+   fcinfo->nargs = nargs;
+
+   /*
+    * Copy supplied arguments into arg vector.  Note there is no way for
+    * frontend to specify a NULL argument --- this protocol is misdesigned.
+    */
+   for (i = 0; i < nargs; ++i)
+   {
+       int         argsize;
+       char       *p;
+
+       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);
+       }
+   }
 }
index 895645587085cfb2d31c45bc5bc1f4df44911395..63b08dc969e18149b015c9d654c22eddc0b349f7 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.339 2003/05/08 14:49:04 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.340 2003/05/08 18:16:36 tgl Exp $
  *
  * NOTES
  *   this is the "main" module of the postgres backend and
@@ -658,7 +658,6 @@ static void
 exec_simple_query(const char *query_string)
 {
    CommandDest dest = whereToSendOutput;
-   DestReceiver *receiver;
    MemoryContext oldcontext;
    List       *parsetree_list,
               *parsetree_item;
@@ -685,12 +684,6 @@ exec_simple_query(const char *query_string)
    if (save_log_statement_stats)
        ResetUsage();
 
-   /*
-    * Create destination receiver object --- we can reuse it for all
-    * queries in the string.  Note it is created in MessageContext.
-    */
-   receiver = CreateDestReceiver(dest);
-
    /*
     * Start up a transaction command.  All queries generated by the
     * query_string will be in this same command block, *unless* we find a
@@ -743,6 +736,8 @@ exec_simple_query(const char *query_string)
        List       *querytree_list,
                   *plantree_list;
        Portal      portal;
+       DestReceiver *receiver;
+       int16       format;
 
        /*
         * Get the command name for use in status display (it also becomes the
@@ -803,11 +798,6 @@ exec_simple_query(const char *query_string)
        /* If we got a cancel signal in analysis or planning, quit */
        CHECK_FOR_INTERRUPTS();
 
-       /*
-        * Switch back to transaction context for execution.
-        */
-       MemoryContextSwitchTo(oldcontext);
-
        /*
         * Create unnamed portal to run the query or queries in.
         * If there already is one, silently drop it.
@@ -822,16 +812,53 @@ exec_simple_query(const char *query_string)
                          MessageContext);
 
        /*
-        * Run the portal to completion, and then drop it.
+        * Start the portal.  No parameters here.
         */
        PortalStart(portal, NULL);
 
+       /*
+        * Select the appropriate output format: text unless we are doing
+        * a FETCH from a binary cursor.  (Pretty grotty to have to do this
+        * here --- but it avoids grottiness in other places.  Ah, the joys
+        * of backward compatibility...)
+        */
+       format = 0;             /* TEXT is default */
+       if (IsA(parsetree, FetchStmt))
+       {
+           FetchStmt  *stmt = (FetchStmt *) parsetree;
+
+           if (!stmt->ismove)
+           {
+               Portal      fportal = GetPortalByName(stmt->portalname);
+
+               if (PortalIsValid(fportal) &&
+                   (fportal->cursorOptions & CURSOR_OPT_BINARY))
+                   format = 1;     /* BINARY */
+           }
+       }
+       PortalSetResultFormat(portal, 1, &format);
+
+       /*
+        * Now we can create the destination receiver object.
+        */
+       receiver = CreateDestReceiver(dest, portal);
+
+       /*
+        * Switch back to transaction context for execution.
+        */
+       MemoryContextSwitchTo(oldcontext);
+
+       /*
+        * Run the portal to completion, and then drop it (and the receiver).
+        */
        (void) PortalRun(portal,
                         FETCH_ALL,
                         receiver,
                         receiver,
                         completionTag);
 
+       (*receiver->destroy) (receiver);
+
        PortalDrop(portal, false);
 
 
@@ -886,8 +913,6 @@ exec_simple_query(const char *query_string)
    if (!parsetree_list)
        NullCommand(dest);
 
-   (*receiver->destroy) (receiver);
-
    QueryContext = NULL;
 
    /*
@@ -1156,8 +1181,12 @@ exec_bind_message(StringInfo input_message)
 {
    const char *portal_name;
    const char *stmt_name;
-   int         is_binary;
+   int         numPFormats;
+   int16      *pformats = NULL;
    int         numParams;
+   int         numRFormats;
+   int16      *rformats = NULL;
+   int         i;
    PreparedStatement *pstmt;
    Portal      portal;
    ParamListInfo params;
@@ -1173,14 +1202,28 @@ exec_bind_message(StringInfo input_message)
     */
    start_xact_command();
 
+   /* Switch back to message context */
+   MemoryContextSwitchTo(MessageContext);
+
    /* Get the fixed part of the message */
    portal_name = pq_getmsgstring(input_message);
    stmt_name = pq_getmsgstring(input_message);
-   is_binary = pq_getmsgbyte(input_message);
-   numParams = pq_getmsgint(input_message, 4);
 
-   if (is_binary)
-       elog(ERROR, "Binary BIND not implemented yet");
+   /* Get the parameter format codes */
+   numPFormats = pq_getmsgint(input_message, 2);
+   if (numPFormats > 0)
+   {
+       pformats = (int16 *) palloc(numPFormats * sizeof(int16));
+       for (i = 0; i < numPFormats; i++)
+           pformats[i] = pq_getmsgint(input_message, 2);
+   }
+
+   /* Get the parameter value count */
+   numParams = pq_getmsgint(input_message, 2);
+
+   if (numPFormats > 1 && numPFormats != numParams)
+       elog(ERROR, "BIND message has %d parameter formats but %d parameters",
+            numPFormats, numParams);
 
    /* Find prepared statement */
    if (stmt_name[0] != '\0')
@@ -1217,45 +1260,98 @@ exec_bind_message(StringInfo input_message)
     * Fetch parameters, if any, and store in the portal's memory context.
     *
     * In an aborted transaction, we can't risk calling user-defined functions,
-    * so bind all parameters to null values.
+    * but we can't fail to Bind either, so bind all parameters to null values.
     */
    if (numParams > 0)
    {
        bool    isaborted = IsAbortedTransactionBlockState();
-       int     i = 0;
+       StringInfoData pbuf;
        List   *l;
        MemoryContext oldContext;
 
+       /* Note that the string buffer lives in MessageContext */
+       initStringInfo(&pbuf);
+
        oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
 
        params = (ParamListInfo)
            palloc0((numParams + 1) * sizeof(ParamListInfoData));
 
+       i = 0;
        foreach(l, pstmt->argtype_list)
        {
            Oid         ptype = lfirsto(l);
+           int32       plength;
            bool        isNull;
 
-           isNull = (pq_getmsgbyte(input_message) != 0) ? false : true;
+           plength = pq_getmsgint(input_message, 4);
+           isNull = (plength == -1);
+
            if (!isNull)
            {
-               const char *ptext = pq_getmsgstring(input_message);
+               /* Reset pbuf to empty, and insert raw data into it */
+               pbuf.len = 0;
+               pbuf.data[0] = '\0';
+               pbuf.cursor = 0;
+
+               appendBinaryStringInfo(&pbuf,
+                                      pq_getmsgbytes(input_message, plength),
+                                      plength);
 
                if (isaborted)
+               {
+                   /* We don't bother to check the format in this case */
                    isNull = true;
+               }
                else
                {
-                   Oid         typInput;
-                   Oid         typElem;
-
-                   getTypeInputInfo(ptype, &typInput, &typElem);
-                   params[i].value =
-                       OidFunctionCall3(typInput,
-                                        CStringGetDatum(ptext),
-                                        ObjectIdGetDatum(typElem),
-                                        Int32GetDatum(-1));
+                   int16   pformat;
+
+                   if (numPFormats > 1)
+                       pformat = pformats[i];
+                   else if (numPFormats > 0)
+                       pformat = pformats[0];
+                   else
+                       pformat = 0;        /* default = text */
+
+                   if (pformat == 0)
+                   {
+                       Oid         typInput;
+                       Oid         typElem;
+                       char       *pstring;
+
+                       getTypeInputInfo(ptype, &typInput, &typElem);
+                       /*
+                        * Since stringinfo.c keeps a trailing null in
+                        * place even for binary data, the contents of
+                        * pbuf 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 *) pbuf.data,
+                                               plength);
+                       params[i].value =
+                           OidFunctionCall3(typInput,
+                                            CStringGetDatum(pstring),
+                                            ObjectIdGetDatum(typElem),
+                                            Int32GetDatum(-1));
+                       /* Free result of encoding conversion, if any */
+                       if (pstring != pbuf.data)
+                           pfree(pstring);
+                   }
+                   else if (pformat == 1)
+                   {
+                       /* XXX something similar to above */
+                       elog(ERROR, "Binary BIND not implemented yet");
+                   }
+                   else
+                   {
+                       elog(ERROR, "Invalid format code %d", pformat);
+                   }
                }
            }
+
            params[i].kind = PARAM_NUM;
            params[i].id = i + 1;
            params[i].isnull = isNull;
@@ -1270,6 +1366,15 @@ exec_bind_message(StringInfo input_message)
    else
        params = NULL;
 
+   /* Get the result format codes */
+   numRFormats = pq_getmsgint(input_message, 2);
+   if (numRFormats > 0)
+   {
+       rformats = (int16 *) palloc(numRFormats * sizeof(int16));
+       for (i = 0; i < numRFormats; i++)
+           rformats[i] = pq_getmsgint(input_message, 2);
+   }
+
    pq_getmsgend(input_message);
 
    /*
@@ -1277,6 +1382,11 @@ exec_bind_message(StringInfo input_message)
     */
    PortalStart(portal, params);
 
+   /*
+    * Apply the result format requests to the portal.
+    */
+   PortalSetResultFormat(portal, numRFormats, rformats);
+
    /*
     * Send BindComplete.
     */
@@ -1290,7 +1400,7 @@ exec_bind_message(StringInfo input_message)
  * Process an "Execute" message for a portal
  */
 static void
-exec_execute_message(const char *portal_name, int is_binary, long max_rows)
+exec_execute_message(const char *portal_name, long max_rows)
 {
    CommandDest dest;
    DestReceiver *receiver;
@@ -1303,7 +1413,7 @@ exec_execute_message(const char *portal_name, int is_binary, long max_rows)
    /* Adjust destination to tell printtup.c what to do */
    dest = whereToSendOutput;
    if (dest == Remote)
-       dest = is_binary ? RemoteExecuteInternal : RemoteExecute;
+       dest = RemoteExecute;
 
    portal = GetPortalByName(portal_name);
    if (!PortalIsValid(portal))
@@ -1352,6 +1462,12 @@ exec_execute_message(const char *portal_name, int is_binary, long max_rows)
        }
    }
 
+   /*
+    * Create dest receiver in MessageContext (we don't want it in transaction
+    * context, because that may get deleted if portal contains VACUUM).
+    */
+   receiver = CreateDestReceiver(dest, portal);
+
    /*
     * Ensure we are in a transaction command (this should normally be
     * the case already due to prior BIND).
@@ -1375,8 +1491,6 @@ exec_execute_message(const char *portal_name, int is_binary, long max_rows)
    /*
     * Okay to run the portal.
     */
-   receiver = CreateDestReceiver(dest);
-
    if (max_rows <= 0)
        max_rows = FETCH_ALL;
 
@@ -1451,7 +1565,7 @@ exec_describe_statement_message(const char *stmt_name)
     * First describe the parameters...
     */
    pq_beginmessage(&buf, 't');     /* parameter description message type */
-   pq_sendint(&buf, length(pstmt->argtype_list), 4);
+   pq_sendint(&buf, length(pstmt->argtype_list), 2);
 
    foreach(l, pstmt->argtype_list)
    {
@@ -1473,7 +1587,7 @@ exec_describe_statement_message(const char *stmt_name)
            targetlist = ((Query *) lfirst(pstmt->query_list))->targetList;
        else
            targetlist = NIL;
-       SendRowDescriptionMessage(tupdesc, targetlist);
+       SendRowDescriptionMessage(tupdesc, targetlist, NULL);
    }
    else
        pq_putemptymessage('n');    /* NoData */
@@ -1502,10 +1616,11 @@ exec_describe_portal_message(const char *portal_name)
        List   *targetlist;
 
        if (portal->strategy == PORTAL_ONE_SELECT)
-           targetlist = ((Plan *) lfirst(portal->planTrees))->targetlist;
+           targetlist = ((Query *) lfirst(portal->parseTrees))->targetList;
        else
            targetlist = NIL;
-       SendRowDescriptionMessage(portal->tupDesc, targetlist);
+       SendRowDescriptionMessage(portal->tupDesc, targetlist,
+                                 portal->formats);
    }
    else
        pq_putemptymessage('n');    /* NoData */
@@ -2397,7 +2512,7 @@ PostgresMain(int argc, char *argv[], const char *username)
    if (!IsUnderPostmaster)
    {
        puts("\nPOSTGRES backend interactive interface ");
-       puts("$Revision: 1.339 $ $Date: 2003/05/08 14:49:04 $\n");
+       puts("$Revision: 1.340 $ $Date: 2003/05/08 18:16:36 $\n");
    }
 
    /*
@@ -2613,7 +2728,7 @@ PostgresMain(int argc, char *argv[], const char *username)
 
                    stmt_name = pq_getmsgstring(input_message);
                    query_string = pq_getmsgstring(input_message);
-                   numParams = pq_getmsgint(input_message, 4);
+                   numParams = pq_getmsgint(input_message, 2);
                    if (numParams > 0)
                    {
                        int     i;
@@ -2640,15 +2755,13 @@ PostgresMain(int argc, char *argv[], const char *username)
            case 'E':           /* execute */
                {
                    const char *portal_name;
-                   int     is_binary;
                    int     max_rows;
 
                    portal_name = pq_getmsgstring(input_message);
-                   is_binary = pq_getmsgbyte(input_message);
                    max_rows = pq_getmsgint(input_message, 4);
                    pq_getmsgend(input_message);
 
-                   exec_execute_message(portal_name, is_binary, max_rows);
+                   exec_execute_message(portal_name, max_rows);
                }
                break;
 
index e3a37b7310e2f532f088f48929a309ad46052924..bf63dcbc29c2c832ac66fc14476d6366c3f59d19 100644 (file)
@@ -8,14 +8,13 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.63 2003/05/06 21:01:04 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.64 2003/05/08 18:16:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
 #include "executor/executor.h"
-#include "executor/tstoreReceiver.h"
 #include "miscadmin.h"
 #include "tcop/tcopprot.h"
 #include "tcop/pquery.h"
@@ -47,7 +46,6 @@ QueryDesc *
 CreateQueryDesc(Query *parsetree,
                Plan *plantree,
                DestReceiver *dest,
-               const char *portalName,
                ParamListInfo params,
                bool doInstrument)
 {
@@ -57,7 +55,6 @@ CreateQueryDesc(Query *parsetree,
    qd->parsetree = parsetree;  /* parse tree */
    qd->plantree = plantree;    /* plan */
    qd->dest = dest;            /* output dest */
-   qd->portalName = portalName;    /* name, if dest is a portal */
    qd->params = params;        /* parameter values passed into query */
    qd->doInstrument = doInstrument; /* instrumentation wanted? */
 
@@ -89,7 +86,6 @@ FreeQueryDesc(QueryDesc *qdesc)
  * parsetree: the query tree
  * plan: the plan tree for the query
  * params: any parameters needed
- * portalName: name of portal being used
  * dest: where to send results
  * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
  *     in which to store a command completion status string.
@@ -103,7 +99,6 @@ void
 ProcessQuery(Query *parsetree,
             Plan *plan,
             ParamListInfo params,
-            const char *portalName,
             DestReceiver *dest,
             char *completionTag)
 {
@@ -131,8 +126,7 @@ ProcessQuery(Query *parsetree,
    /*
     * Create the QueryDesc object
     */
-   queryDesc = CreateQueryDesc(parsetree, plan, dest, portalName, params,
-                               false);
+   queryDesc = CreateQueryDesc(parsetree, plan, dest, params, false);
 
    /*
     * Call ExecStart to prepare the plan for execution
@@ -269,7 +263,6 @@ PortalStart(Portal portal, ParamListInfo params)
            queryDesc = CreateQueryDesc((Query *) lfirst(portal->parseTrees),
                                        (Plan *) lfirst(portal->planTrees),
                                        None_Receiver,
-                                       portal->name,
                                        params,
                                        false);
            /*
@@ -281,7 +274,7 @@ PortalStart(Portal portal, ParamListInfo params)
             */
            portal->queryDesc = queryDesc;
            /*
-            * Remember tuple descriptor
+            * Remember tuple descriptor (computed by ExecutorStart)
             */
            portal->tupDesc = queryDesc->tupDesc;
            /*
@@ -320,6 +313,53 @@ PortalStart(Portal portal, ParamListInfo params)
    portal->portalReady = true;
 }
 
+/*
+ * PortalSetResultFormat
+ *     Select the format codes for a portal's output.
+ *
+ * This must be run after PortalStart for a portal that will be read by
+ * a Remote or RemoteExecute destination.  It is not presently needed for
+ * other destination types.
+ *
+ * formats[] is the client format request, as per Bind message conventions.
+ */
+void
+PortalSetResultFormat(Portal portal, int nFormats, int16 *formats)
+{
+   int         natts;
+   int         i;
+
+   /* Do nothing if portal won't return tuples */
+   if (portal->tupDesc == NULL)
+       return;
+   natts = portal->tupDesc->natts;
+   /* +1 avoids palloc(0) if no columns */
+   portal->formats = (int16 *)
+       MemoryContextAlloc(PortalGetHeapMemory(portal),
+                          (natts + 1) * sizeof(int16));
+   if (nFormats > 1)
+   {
+       /* format specified for each column */
+       if (nFormats != natts)
+           elog(ERROR, "BIND message has %d result formats but query has %d columns",
+                nFormats, natts);
+       memcpy(portal->formats, formats, natts * sizeof(int16));
+   } else if (nFormats > 0)
+   {
+       /* single format specified, use for all columns */
+       int16       format1 = formats[0];
+
+       for (i = 0; i < natts; i++)
+           portal->formats[i] = format1;
+   }
+   else
+   {
+       /* use default format for all columns */
+       for (i = 0; i < natts; i++)
+           portal->formats[i] = 0;
+   }
+}
+
 /*
  * PortalRun
  *     Run a portal's query or queries.
@@ -399,8 +439,7 @@ PortalRun(Portal portal, long count,
                DestReceiver *treceiver;
 
                PortalCreateHoldStore(portal);
-               treceiver = CreateTuplestoreDestReceiver(portal->holdStore,
-                                                        portal->holdContext);
+               treceiver = CreateDestReceiver(Tuplestore, portal);
                PortalRunUtility(portal, lfirst(portal->parseTrees),
                                 treceiver, NULL);
                (*treceiver->destroy) (treceiver);
@@ -604,16 +643,9 @@ static uint32
 RunFromStore(Portal portal, ScanDirection direction, long count,
             DestReceiver *dest)
 {
-   List       *targetlist;
    long        current_tuple_count = 0;
 
-   if (portal->strategy == PORTAL_ONE_SELECT)
-       targetlist = ((Plan *) lfirst(portal->planTrees))->targetlist;
-   else
-       targetlist = NIL;
-
-   (*dest->startup) (dest, CMD_SELECT, portal->name, portal->tupDesc,
-                     targetlist);
+   (*dest->startup) (dest, CMD_SELECT, portal->tupDesc);
 
    if (direction == NoMovementScanDirection)
    {
@@ -737,11 +769,9 @@ PortalRunMulti(Portal portal,
     * but the results will be discarded unless you use "simple Query"
     * protocol.
     */
-   if (dest->mydest == RemoteExecute ||
-       dest->mydest == RemoteExecuteInternal)
+   if (dest->mydest == RemoteExecute)
        dest = None_Receiver;
-   if (altdest->mydest == RemoteExecute ||
-       altdest->mydest == RemoteExecuteInternal)
+   if (altdest->mydest == RemoteExecute)
        altdest = None_Receiver;
 
    /*
@@ -791,14 +821,14 @@ PortalRunMulti(Portal portal,
            {
                /* statement can set tag string */
                ProcessQuery(query, plan,
-                            portal->portalParams, portal->name,
+                            portal->portalParams,
                             dest, completionTag);
            }
            else
            {
                /* stmt added by rewrite cannot set tag */
                ProcessQuery(query, plan,
-                            portal->portalParams, portal->name,
+                            portal->portalParams,
                             altdest, NULL);
            }
 
index 9d10fe4607bb87b945bf277a2504534cf58f11ac..981b0f26483471058c0f6f0e01d440549ed1df59 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: printtup.h,v 1.26 2003/05/06 20:26:27 tgl Exp $
+ * $Id: printtup.h,v 1.27 2003/05/08 18:16:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "tcop/dest.h"
 
-extern DestReceiver *printtup_create_DR(CommandDest dest);
+extern DestReceiver *printtup_create_DR(CommandDest dest, Portal portal);
 
-extern void SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist);
+extern void SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist,
+                                     int16 *formats);
 
 extern void debugStartup(DestReceiver *self, int operation,
-          const char *portalName, TupleDesc typeinfo, List *targetlist);
+                        TupleDesc typeinfo);
 extern void debugtup(HeapTuple tuple, TupleDesc typeinfo,
         DestReceiver *self);
 
 /* XXX these are really in executor/spi.c */
 extern void spi_dest_startup(DestReceiver *self, int operation,
-          const char *portalName, TupleDesc typeinfo, List *targetlist);
+                            TupleDesc typeinfo);
 extern void spi_printtup(HeapTuple tuple, TupleDesc typeinfo,
             DestReceiver *self);
 
index 9c54ed1d6439984a7510751d91a0908aa6cee318..5eecb53b8d2aaea741cb7c5ace746f8c4172ea70 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: execdesc.h,v 1.23 2003/05/06 20:26:28 tgl Exp $
+ * $Id: execdesc.h,v 1.24 2003/05/08 18:16:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -34,7 +34,6 @@ typedef struct QueryDesc
    Query      *parsetree;      /* rewritten parsetree */
    Plan       *plantree;       /* planner's output */
    DestReceiver *dest;         /* the destination for tuple output */
-   const char *portalName;     /* name of portal, or NULL */
    ParamListInfo params;       /* param values being passed in */
    bool        doInstrument;   /* TRUE requests runtime instrumentation */
 
@@ -46,7 +45,7 @@ typedef struct QueryDesc
 
 /* in pquery.c */
 extern QueryDesc *CreateQueryDesc(Query *parsetree, Plan *plantree,
-                                 DestReceiver *dest, const char *portalName,
+                                 DestReceiver *dest,
                                  ParamListInfo params,
                                  bool doInstrument);
 
index 998b3a391f535d43a619bcde5c430ff50e5ae138..e23b4563edba90e25f9b6ff0ca500e43e2efa892 100644 (file)
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pqcomm.h,v 1.83 2003/05/06 21:51:42 tgl Exp $
+ * $Id: pqcomm.h,v 1.84 2003/05/08 18:16:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -106,7 +106,7 @@ typedef union SockAddr
 /* The earliest and latest frontend/backend protocol version supported. */
 
 #define PG_PROTOCOL_EARLIEST   PG_PROTOCOL(1,0)
-#define PG_PROTOCOL_LATEST     PG_PROTOCOL(3,107) /* XXX temporary value */
+#define PG_PROTOCOL_LATEST     PG_PROTOCOL(3,108) /* XXX temporary value */
 
 typedef uint32 ProtocolVersion; /* FE/BE protocol version number */
 
index 229de38c9b36d3e6213235bea6ee178d37f8cd15..7167c6d63add484301233e87ccbb2f23d9509d7d 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: pqformat.h,v 1.15 2003/04/22 00:08:07 tgl Exp $
+ * $Id: pqformat.h,v 1.16 2003/05/08 18:16:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,7 +18,8 @@
 extern void pq_beginmessage(StringInfo buf, char msgtype);
 extern void pq_sendbyte(StringInfo buf, int byt);
 extern void pq_sendbytes(StringInfo buf, const char *data, int datalen);
-extern void pq_sendcountedtext(StringInfo buf, const char *str, int slen);
+extern void pq_sendcountedtext(StringInfo buf, const char *str, int slen,
+                              bool countincludesself);
 extern void pq_sendstring(StringInfo buf, const char *str);
 extern void pq_sendint(StringInfo buf, int i, int b);
 extern void pq_endmessage(StringInfo buf);
index d2162e99760243a044809a8289d33483e4a8cfd5..b5721f4593f44c6767a5a483cde8fc155666d679 100644 (file)
@@ -54,7 +54,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: dest.h,v 1.37 2003/05/06 20:26:28 tgl Exp $
+ * $Id: dest.h,v 1.38 2003/05/08 18:16:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -82,12 +82,9 @@ typedef enum
    None,                       /* results are discarded */
    Debug,                      /* results go to debugging output */
    Remote,                     /* results sent to frontend process */
-   RemoteInternal,             /* results sent to frontend process in
-                                * internal (binary) form */
-   SPI,                        /* results sent to SPI manager */
-   Tuplestore,                 /* results sent to Tuplestore */
    RemoteExecute,              /* sent to frontend, in Execute command */
-   RemoteExecuteInternal       /* same, but binary format */
+   SPI,                        /* results sent to SPI manager */
+   Tuplestore                  /* results sent to Tuplestore */
 } CommandDest;
 
 /* ----------------
@@ -106,13 +103,13 @@ typedef struct _DestReceiver DestReceiver;
 struct _DestReceiver
 {
    /* Called for each tuple to be output: */
-   void        (*receiveTuple) (HeapTuple tuple, TupleDesc typeinfo,
+   void        (*receiveTuple) (HeapTuple tuple,
+                                TupleDesc typeinfo,
                                 DestReceiver *self);
    /* Per-executor-run initialization and shutdown: */
-   void        (*startup) (DestReceiver *self, int operation,
-                           const char *portalName,
-                           TupleDesc typeinfo,
-                           List *targetlist);
+   void        (*startup) (DestReceiver *self,
+                           int operation,
+                           TupleDesc typeinfo);
    void        (*shutdown) (DestReceiver *self);
    /* Destroy the receiver object itself (if dynamically allocated) */
    void        (*destroy) (DestReceiver *self);
@@ -123,10 +120,14 @@ struct _DestReceiver
 
 extern DestReceiver *None_Receiver;    /* permanent receiver for None */
 
+/* This is a forward reference to utils/portal.h */
+
+typedef struct PortalData *Portal;
+
 /* The primary destination management functions */
 
 extern void BeginCommand(const char *commandTag, CommandDest dest);
-extern DestReceiver *CreateDestReceiver(CommandDest dest);
+extern DestReceiver *CreateDestReceiver(CommandDest dest, Portal portal);
 extern void EndCommand(const char *commandTag, CommandDest dest);
 
 /* Additional functions that go with destination management, more or less. */
index d9d6a6221d5af75c7ce8e750e0bc486fd9a4cea6..ff9cc9d76acd78e2d31e921c3cb36188f1cc9d84 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pquery.h,v 1.26 2003/05/06 20:26:28 tgl Exp $
+ * $Id: pquery.h,v 1.27 2003/05/08 18:16:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,7 +20,6 @@
 extern void ProcessQuery(Query *parsetree,
                         Plan *plan,
                         ParamListInfo params,
-                        const char *portalName,
                         DestReceiver *dest,
                         char *completionTag);
 
@@ -28,6 +27,9 @@ extern PortalStrategy ChoosePortalStrategy(List *parseTrees);
 
 extern void PortalStart(Portal portal, ParamListInfo params);
 
+extern void PortalSetResultFormat(Portal portal, int nFormats,
+                                 int16 *formats);
+
 extern bool PortalRun(Portal portal, long count,
                      DestReceiver *dest, DestReceiver *altdest,
                      char *completionTag);
index e2617f321ec048568e245421f8a5fa64a9b72766..7da359cbe73b40e42551a010dd8d341038b69e9e 100644 (file)
@@ -39,7 +39,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: portal.h,v 1.43 2003/05/06 20:26:28 tgl Exp $
+ * $Id: portal.h,v 1.44 2003/05/08 18:16:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -79,7 +79,10 @@ typedef enum PortalStrategy
    PORTAL_MULTI_QUERY
 } PortalStrategy;
 
-typedef struct PortalData *Portal;
+/*
+ * Note: typedef Portal is declared in tcop/dest.h as
+ *     typedef struct PortalData *Portal;
+ */
 
 typedef struct PortalData
 {
@@ -119,6 +122,8 @@ typedef struct PortalData
 
    /* If portal returns tuples, this is their tupdesc: */
    TupleDesc   tupDesc;        /* descriptor for result tuples */
+   /* and these are the format codes to use for the columns: */
+   int16      *formats;        /* a format code for each column */
 
    /*
     * Where we store tuples for a held cursor or a PORTAL_UTIL_SELECT query.
index db513d64ef5df47c325c4be69d326a04fbc67b7c..7d0eb39f96cbe22cb638b0a9a24053ea91cf5379 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.134 2003/04/26 20:22:59 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.135 2003/05/08 18:16:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -55,250 +55,10 @@ static void parseInput(PGconn *conn);
 static void handleSendFailure(PGconn *conn);
 static void handleSyncLoss(PGconn *conn, char id, int msgLength);
 static int getRowDescriptions(PGconn *conn);
-static int getAnotherTuple(PGconn *conn, int binary);
+static int getAnotherTuple(PGconn *conn);
 static int getParameterStatus(PGconn *conn);
 static int getNotify(PGconn *conn);
 
-/* ---------------
- * Escaping arbitrary strings to get valid SQL strings/identifiers.
- *
- * Replaces "\\" with "\\\\" and "'" with "''".
- * length is the length of the buffer pointed to by
- * from.  The buffer at to must be at least 2*length + 1 characters
- * long.  A terminating NUL character is written.
- * ---------------
- */
-
-size_t
-PQescapeString(char *to, const char *from, size_t length)
-{
-   const char *source = from;
-   char       *target = to;
-   unsigned int remaining = length;
-
-   while (remaining > 0)
-   {
-       switch (*source)
-       {
-           case '\\':
-               *target = '\\';
-               target++;
-               *target = '\\';
-               /* target and remaining are updated below. */
-               break;
-
-           case '\'':
-               *target = '\'';
-               target++;
-               *target = '\'';
-               /* target and remaining are updated below. */
-               break;
-
-           default:
-               *target = *source;
-               /* target and remaining are updated below. */
-       }
-       source++;
-       target++;
-       remaining--;
-   }
-
-   /* Write the terminating NUL character. */
-   *target = '\0';
-
-   return target - to;
-}
-
-/*
- *     PQescapeBytea   - converts from binary string to the
- *     minimal encoding necessary to include the string in an SQL
- *     INSERT statement with a bytea type column as the target.
- *
- *     The following transformations are applied
- *     '\0' == ASCII  0 == \\000
- *     '\'' == ASCII 39 == \'
- *     '\\' == ASCII 92 == \\\\
- *     anything >= 0x80 ---> \\ooo (where ooo is an octal expression)
- */
-unsigned char *
-PQescapeBytea(const unsigned char *bintext, size_t binlen, size_t *bytealen)
-{
-   const unsigned char *vp;
-   unsigned char *rp;
-   unsigned char *result;
-   size_t      i;
-   size_t      len;
-
-   /*
-    * empty string has 1 char ('\0')
-    */
-   len = 1;
-
-   vp = bintext;
-   for (i = binlen; i > 0; i--, vp++)
-   {
-       if (*vp == 0 || *vp >= 0x80)
-           len += 5;           /* '5' is for '\\ooo' */
-       else if (*vp == '\'')
-           len += 2;
-       else if (*vp == '\\')
-           len += 4;
-       else
-           len++;
-   }
-
-   rp = result = (unsigned char *) malloc(len);
-   if (rp == NULL)
-       return NULL;
-
-   vp = bintext;
-   *bytealen = len;
-
-   for (i = binlen; i > 0; i--, vp++)
-   {
-       if (*vp == 0 || *vp >= 0x80)
-       {
-           (void) sprintf(rp, "\\\\%03o", *vp);
-           rp += 5;
-       }
-       else if (*vp == '\'')
-       {
-           rp[0] = '\\';
-           rp[1] = '\'';
-           rp += 2;
-       }
-       else if (*vp == '\\')
-       {
-           rp[0] = '\\';
-           rp[1] = '\\';
-           rp[2] = '\\';
-           rp[3] = '\\';
-           rp += 4;
-       }
-       else
-           *rp++ = *vp;
-   }
-   *rp = '\0';
-
-   return result;
-}
-
-/*
- *     PQunescapeBytea - converts the null terminated string representation
- *     of a bytea, strtext, into binary, filling a buffer. It returns a
- *     pointer to the buffer which is NULL on error, and the size of the
- *     buffer in retbuflen. The pointer may subsequently be used as an
- *     argument to the function free(3). It is the reverse of PQescapeBytea.
- *
- *     The following transformations are reversed:
- *     '\0' == ASCII  0 == \000
- *     '\'' == ASCII 39 == \'
- *     '\\' == ASCII 92 == \\
- *
- *     States:
- *     0   normal      0->1->2->3->4
- *     1   \              1->5
- *     2   \0             1->6
- *     3   \00
- *     4   \000
- *     5   \'
- *     6   \\
- */
-unsigned char *
-PQunescapeBytea(const unsigned char *strtext, size_t *retbuflen)
-{
-   size_t      buflen;
-   unsigned char *buffer,
-              *bp;
-   const unsigned char *sp;
-   unsigned int state = 0;
-
-   if (strtext == NULL)
-       return NULL;
-   buflen = strlen(strtext);   /* will shrink, also we discover if
-                                * strtext */
-   buffer = (unsigned char *) malloc(buflen);  /* isn't NULL terminated */
-   if (buffer == NULL)
-       return NULL;
-   for (bp = buffer, sp = strtext; *sp != '\0'; bp++, sp++)
-   {
-       switch (state)
-       {
-           case 0:
-               if (*sp == '\\')
-                   state = 1;
-               *bp = *sp;
-               break;
-           case 1:
-               if (*sp == '\'')    /* state=5 */
-               {               /* replace \' with 39 */
-                   bp--;
-                   *bp = '\'';
-                   buflen--;
-                   state = 0;
-               }
-               else if (*sp == '\\')   /* state=6 */
-               {               /* replace \\ with 92 */
-                   bp--;
-                   *bp = '\\';
-                   buflen--;
-                   state = 0;
-               }
-               else
-               {
-                   if (isdigit(*sp))
-                       state = 2;
-                   else
-                       state = 0;
-                   *bp = *sp;
-               }
-               break;
-           case 2:
-               if (isdigit(*sp))
-                   state = 3;
-               else
-                   state = 0;
-               *bp = *sp;
-               break;
-           case 3:
-               if (isdigit(*sp))       /* state=4 */
-               {
-                   int         v;
-
-                   bp -= 3;
-                   sscanf(sp - 2, "%03o", &v);
-                   *bp = v;
-                   buflen -= 3;
-                   state = 0;
-               }
-               else
-               {
-                   *bp = *sp;
-                   state = 0;
-               }
-               break;
-       }
-   }
-   buffer = realloc(buffer, buflen);
-   if (buffer == NULL)
-       return NULL;
-
-   *retbuflen = buflen;
-   return buffer;
-}
-
-
-/*
- *     PQfreemem - safely frees memory allocated
- *
- * Needed mostly by Win32, unless multithreaded DLL (/MD in VC6)
- * Used for freeing memory from PQescapeByte()a/PQunescapeBytea()
- */
-void PQfreemem(void *ptr)
-{
-   free(ptr);
-}
-
 
 /* ----------------
  * Space management for PGresult.
@@ -909,7 +669,7 @@ parseInput(PGconn *conn)
            return;
        }
        if (msgLength > 30000 &&
-           !(id == 'T' || id == 'D' || id == 'B' || id == 'd'))
+           !(id == 'T' || id == 'D' || id == 'd'))
        {
            handleSyncLoss(conn, id, msgLength);
            return;
@@ -1074,11 +834,11 @@ parseInput(PGconn *conn)
                        return;
                    }
                    break;
-               case 'D':       /* ASCII data tuple */
+               case 'D':       /* Data Row */
                    if (conn->result != NULL)
                    {
                        /* Read another tuple of a normal query response */
-                       if (getAnotherTuple(conn, FALSE))
+                       if (getAnotherTuple(conn))
                            return;
                    }
                    else
@@ -1090,30 +850,18 @@ parseInput(PGconn *conn)
                        conn->inCursor += msgLength;
                    }
                    break;
-               case 'B':       /* Binary data tuple */
-                   if (conn->result != NULL)
-                   {
-                       /* Read another tuple of a normal query response */
-                       if (getAnotherTuple(conn, TRUE))
-                           return;
-                   }
-                   else
-                   {
-                       snprintf(noticeWorkspace, sizeof(noticeWorkspace),
-                                libpq_gettext("server sent binary data (\"B\" message) without prior row description (\"T\" message)\n"));
-                       DONOTICE(conn, noticeWorkspace);
-                       /* Discard the unexpected message */
-                       conn->inCursor += msgLength;
-                   }
-                   break;
                case 'G':       /* Start Copy In */
                    if (pqGetc(&conn->copy_is_binary, conn))
                        return;
+                   /* XXX we currently ignore the rest of the message */
+                   conn->inCursor = conn->inStart + 5 + msgLength;
                    conn->asyncStatus = PGASYNC_COPY_IN;
                    break;
                case 'H':       /* Start Copy Out */
                    if (pqGetc(&conn->copy_is_binary, conn))
                        return;
+                   /* XXX we currently ignore the rest of the message */
+                   conn->inCursor = conn->inStart + 5 + msgLength;
                    conn->asyncStatus = PGASYNC_COPY_OUT;
                    conn->copy_already_done = 0;
                    break;
@@ -1229,13 +977,15 @@ getRowDescriptions(PGconn *conn)
        int         typid;
        int         typlen;
        int         atttypmod;
+       int         format;
 
        if (pqGets(&conn->workBuffer, conn) ||
            pqGetInt(&tableid, 4, conn) ||
            pqGetInt(&columnid, 2, conn) ||
            pqGetInt(&typid, 4, conn) ||
            pqGetInt(&typlen, 2, conn) ||
-           pqGetInt(&atttypmod, 4, conn))
+           pqGetInt(&atttypmod, 4, conn) ||
+           pqGetInt(&format, 2, conn))
        {
            PQclear(result);
            return EOF;
@@ -1247,13 +997,14 @@ getRowDescriptions(PGconn *conn)
         */
        columnid = (int) ((int16) columnid);
        typlen = (int) ((int16) typlen);
+       format = (int) ((int16) format);
 
        result->attDescs[i].name = pqResultStrdup(result,
                                                  conn->workBuffer.data);
        result->attDescs[i].typid = typid;
        result->attDescs[i].typlen = typlen;
        result->attDescs[i].atttypmod = atttypmod;
-       /* XXX todo: save tableid/columnid too */
+       /* XXX todo: save tableid/columnid, format too */
    }
 
    /* Success! */
@@ -1262,7 +1013,7 @@ getRowDescriptions(PGconn *conn)
 }
 
 /*
- * parseInput subroutine to read a 'B' or 'D' (row data) message.
+ * parseInput subroutine to read a 'D' (row data) message.
  * We add another tuple to the existing PGresult structure.
  * Returns: 0 if completed message, EOF if error or not enough data yet.
  *
@@ -1272,23 +1023,14 @@ getRowDescriptions(PGconn *conn)
  */
 
 static int
-getAnotherTuple(PGconn *conn, int binary)
+getAnotherTuple(PGconn *conn)
 {
    PGresult   *result = conn->result;
    int         nfields = result->numAttributes;
    PGresAttValue *tup;
-
-   /* the backend sends us a bitmap of which attributes are null */
-   char        std_bitmap[64]; /* used unless it doesn't fit */
-   char       *bitmap = std_bitmap;
-   int         i;
-   size_t      nbytes;         /* the number of bytes in bitmap  */
-   char        bmap;           /* One byte of the bitmap */
-   int         bitmap_index;   /* Its index */
-   int         bitcnt;         /* number of bits examined in current byte */
+   int         tupnfields;     /* # fields from tuple */
    int         vlen;           /* length of the current field value */
-
-   result->binary = binary;
+   int         i;
 
    /* Allocate tuple space if first time for this data message */
    if (conn->curTuple == NULL)
@@ -1301,61 +1043,50 @@ getAnotherTuple(PGconn *conn, int binary)
    }
    tup = conn->curTuple;
 
-   /* Get the null-value bitmap */
-   nbytes = (nfields + BYTELEN - 1) / BYTELEN;
-   /* malloc() only for unusually large field counts... */
-   if (nbytes > sizeof(std_bitmap))
-       bitmap = (char *) malloc(nbytes);
+   /* Get the field count and make sure it's what we expect */
+   if (pqGetInt(&tupnfields, 2, conn))
+       return EOF;
 
-   if (pqGetnchar(bitmap, nbytes, conn))
-       goto EOFexit;
+   if (tupnfields != nfields)
+   {
+       /* Replace partially constructed result with an error result */
+       pqClearAsyncResult(conn);
+       printfPQExpBuffer(&conn->errorMessage,
+                         libpq_gettext("unexpected field count in D message\n"));
+       saveErrorResult(conn);
+       conn->asyncStatus = PGASYNC_READY;
+       /* Discard the failed message by pretending we read it */
+       return 0;
+   }
 
    /* Scan the fields */
-   bitmap_index = 0;
-   bmap = bitmap[bitmap_index];
-   bitcnt = 0;
-
    for (i = 0; i < nfields; i++)
    {
-       if (!(bmap & 0200))
+       /* get the value length */
+       if (pqGetInt(&vlen, 4, conn))
+           return EOF;
+       if (vlen == -1)
        {
-           /* if the field value is absent, make it a null string */
+           /* null field */
            tup[i].value = result->null_field;
            tup[i].len = NULL_LEN;
+           continue;
        }
-       else
+       if (vlen < 0)
+           vlen = 0;
+       if (tup[i].value == NULL)
        {
-           /* get the value length (the first four bytes are for length) */
-           if (pqGetInt(&vlen, 4, conn))
-               goto EOFexit;
-           if (binary == 0)
-               vlen = vlen - 4;
-           if (vlen < 0)
-               vlen = 0;
+           tup[i].value = (char *) pqResultAlloc(result, vlen + 1, false);
            if (tup[i].value == NULL)
-           {
-               tup[i].value = (char *) pqResultAlloc(result, vlen + 1, (bool) binary);
-               if (tup[i].value == NULL)
-                   goto outOfMemory;
-           }
-           tup[i].len = vlen;
-           /* read in the value */
-           if (vlen > 0)
-               if (pqGetnchar((char *) (tup[i].value), vlen, conn))
-                   goto EOFexit;
-           /* we have to terminate this ourselves */
-           tup[i].value[vlen] = '\0';
-       }
-       /* advance the bitmap stuff */
-       bitcnt++;
-       if (bitcnt == BYTELEN)
-       {
-           bitmap_index++;
-           bmap = bitmap[bitmap_index];
-           bitcnt = 0;
+               goto outOfMemory;
        }
-       else
-           bmap <<= 1;
+       tup[i].len = vlen;
+       /* read in the value */
+       if (vlen > 0)
+           if (pqGetnchar((char *) (tup[i].value), vlen, conn))
+               return EOF;
+       /* we have to terminate this ourselves */
+       tup[i].value[vlen] = '\0';
    }
 
    /* Success!  Store the completed tuple in the result */
@@ -1364,8 +1095,6 @@ getAnotherTuple(PGconn *conn, int binary)
    /* and reset for a new message */
    conn->curTuple = NULL;
 
-   if (bitmap != std_bitmap)
-       free(bitmap);
    return 0;
 
 outOfMemory:
@@ -1380,13 +1109,8 @@ outOfMemory:
                      libpq_gettext("out of memory\n"));
    conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
    conn->asyncStatus = PGASYNC_READY;
-   /* Discard the failed message --- good idea? */
-   conn->inStart = conn->inEnd;
-
-EOFexit:
-   if (bitmap != std_bitmap)
-       free(bitmap);
-   return EOF;
+   /* Discard the failed message by pretending we read it */
+   return 0;
 }
 
 
@@ -2129,10 +1853,11 @@ PQfn(PGconn *conn,
        return NULL;
    }
 
-   if (pqPutMsgStart('F', conn) < 0 || /* function call msg */
-       pqPuts("", conn) < 0 || /* useless string */
-       pqPutInt(fnid, 4, conn) < 0 || /* function id */
-       pqPutInt(nargs, 4, conn) < 0)   /* # of args */
+   if (pqPutMsgStart('F', conn) < 0 ||     /* function call msg */
+       pqPutInt(fnid, 4, conn) < 0 ||      /* function id */
+       pqPutInt(1, 2, conn) < 0 ||         /* # of format codes */
+       pqPutInt(1, 2, conn) < 0 ||         /* format code: BINARY */
+       pqPutInt(nargs, 2, conn) < 0)       /* # of args */
    {
        handleSendFailure(conn);
        return NULL;
@@ -2145,10 +1870,12 @@ PQfn(PGconn *conn,
            handleSendFailure(conn);
            return NULL;
        }
+       if (args[i].len == -1)
+           continue;           /* it's NULL */
 
        if (args[i].isint)
        {
-           if (pqPutInt(args[i].u.integer, 4, conn))
+           if (pqPutInt(args[i].u.integer, args[i].len, conn))
            {
                handleSendFailure(conn);
                return NULL;
@@ -2164,6 +1891,12 @@ PQfn(PGconn *conn,
        }
    }
 
+   if (pqPutInt(1, 2, conn) < 0)       /* result format code: BINARY */
+   {
+       handleSendFailure(conn);
+       return NULL;
+   }
+
    if (pqPutMsgEnd(conn) < 0 ||
        pqFlush(conn))
    {
@@ -2204,7 +1937,7 @@ PQfn(PGconn *conn,
            break;
        }
        if (msgLength > 30000 &&
-           !(id == 'T' || id == 'D' || id == 'B' || id == 'd' || id == 'V'))
+           !(id == 'T' || id == 'D' || id == 'd' || id == 'V'))
        {
            handleSyncLoss(conn, id, msgLength);
            break;
@@ -2243,16 +1976,13 @@ PQfn(PGconn *conn,
        switch (id)
        {
            case 'V':           /* function result */
-               if (pqGetc(&id, conn))
+               if (pqGetInt(actual_result_len, 4, conn))
                    continue;
-               if (id == 'G')
+               if (*actual_result_len != -1)
                {
-                   /* function returned nonempty value */
-                   if (pqGetInt(actual_result_len, 4, conn))
-                       continue;
                    if (result_is_int)
                    {
-                       if (pqGetInt(result_buf, 4, conn))
+                       if (pqGetInt(result_buf, *actual_result_len, conn))
                            continue;
                    }
                    else
@@ -2262,24 +1992,9 @@ PQfn(PGconn *conn,
                                       conn))
                            continue;
                    }
-                   if (pqGetc(&id, conn))      /* get the last '0' */
-                       continue;
-               }
-               if (id == '0')
-               {
-                   /* correctly finished function result message */
-                   status = PGRES_COMMAND_OK;
-               }
-               else
-               {
-                   /* The backend violates the protocol. */
-                   printfPQExpBuffer(&conn->errorMessage,
-                             libpq_gettext("protocol error: id=0x%x\n"),
-                                     id);
-                   saveErrorResult(conn);
-                   conn->inStart += 5 + msgLength;
-                   return prepareAsyncResult(conn);
                }
+               /* correctly finished function result message */
+               status = PGRES_COMMAND_OK;
                break;
            case 'E':           /* error return */
                if (pqGetErrorNotice(conn, true))
@@ -2744,6 +2459,18 @@ PQflush(PGconn *conn)
    return (pqFlush(conn));
 }
 
+
+/*
+ *     PQfreemem - safely frees memory allocated
+ *
+ * Needed mostly by Win32, unless multithreaded DLL (/MD in VC6)
+ * Used for freeing memory from PQescapeByte()a/PQunescapeBytea()
+ */
+void PQfreemem(void *ptr)
+{
+   free(ptr);
+}
+
 /*
  * PQfreeNotify - free's the memory associated with a PGnotify
  *
@@ -2760,3 +2487,232 @@ PQfreeNotify(PGnotify *notify)
 {
    PQfreemem(notify);
 }
+
+
+/* ---------------
+ * Escaping arbitrary strings to get valid SQL strings/identifiers.
+ *
+ * Replaces "\\" with "\\\\" and "'" with "''".
+ * length is the length of the buffer pointed to by
+ * from.  The buffer at to must be at least 2*length + 1 characters
+ * long.  A terminating NUL character is written.
+ * ---------------
+ */
+
+size_t
+PQescapeString(char *to, const char *from, size_t length)
+{
+   const char *source = from;
+   char       *target = to;
+   unsigned int remaining = length;
+
+   while (remaining > 0)
+   {
+       switch (*source)
+       {
+           case '\\':
+               *target = '\\';
+               target++;
+               *target = '\\';
+               /* target and remaining are updated below. */
+               break;
+
+           case '\'':
+               *target = '\'';
+               target++;
+               *target = '\'';
+               /* target and remaining are updated below. */
+               break;
+
+           default:
+               *target = *source;
+               /* target and remaining are updated below. */
+       }
+       source++;
+       target++;
+       remaining--;
+   }
+
+   /* Write the terminating NUL character. */
+   *target = '\0';
+
+   return target - to;
+}
+
+/*
+ *     PQescapeBytea   - converts from binary string to the
+ *     minimal encoding necessary to include the string in an SQL
+ *     INSERT statement with a bytea type column as the target.
+ *
+ *     The following transformations are applied
+ *     '\0' == ASCII  0 == \\000
+ *     '\'' == ASCII 39 == \'
+ *     '\\' == ASCII 92 == \\\\
+ *     anything >= 0x80 ---> \\ooo (where ooo is an octal expression)
+ */
+unsigned char *
+PQescapeBytea(const unsigned char *bintext, size_t binlen, size_t *bytealen)
+{
+   const unsigned char *vp;
+   unsigned char *rp;
+   unsigned char *result;
+   size_t      i;
+   size_t      len;
+
+   /*
+    * empty string has 1 char ('\0')
+    */
+   len = 1;
+
+   vp = bintext;
+   for (i = binlen; i > 0; i--, vp++)
+   {
+       if (*vp == 0 || *vp >= 0x80)
+           len += 5;           /* '5' is for '\\ooo' */
+       else if (*vp == '\'')
+           len += 2;
+       else if (*vp == '\\')
+           len += 4;
+       else
+           len++;
+   }
+
+   rp = result = (unsigned char *) malloc(len);
+   if (rp == NULL)
+       return NULL;
+
+   vp = bintext;
+   *bytealen = len;
+
+   for (i = binlen; i > 0; i--, vp++)
+   {
+       if (*vp == 0 || *vp >= 0x80)
+       {
+           (void) sprintf(rp, "\\\\%03o", *vp);
+           rp += 5;
+       }
+       else if (*vp == '\'')
+       {
+           rp[0] = '\\';
+           rp[1] = '\'';
+           rp += 2;
+       }
+       else if (*vp == '\\')
+       {
+           rp[0] = '\\';
+           rp[1] = '\\';
+           rp[2] = '\\';
+           rp[3] = '\\';
+           rp += 4;
+       }
+       else
+           *rp++ = *vp;
+   }
+   *rp = '\0';
+
+   return result;
+}
+
+/*
+ *     PQunescapeBytea - converts the null terminated string representation
+ *     of a bytea, strtext, into binary, filling a buffer. It returns a
+ *     pointer to the buffer which is NULL on error, and the size of the
+ *     buffer in retbuflen. The pointer may subsequently be used as an
+ *     argument to the function free(3). It is the reverse of PQescapeBytea.
+ *
+ *     The following transformations are reversed:
+ *     '\0' == ASCII  0 == \000
+ *     '\'' == ASCII 39 == \'
+ *     '\\' == ASCII 92 == \\
+ *
+ *     States:
+ *     0   normal      0->1->2->3->4
+ *     1   \              1->5
+ *     2   \0             1->6
+ *     3   \00
+ *     4   \000
+ *     5   \'
+ *     6   \\
+ */
+unsigned char *
+PQunescapeBytea(const unsigned char *strtext, size_t *retbuflen)
+{
+   size_t      buflen;
+   unsigned char *buffer,
+              *bp;
+   const unsigned char *sp;
+   unsigned int state = 0;
+
+   if (strtext == NULL)
+       return NULL;
+   buflen = strlen(strtext);   /* will shrink, also we discover if
+                                * strtext */
+   buffer = (unsigned char *) malloc(buflen);  /* isn't NULL terminated */
+   if (buffer == NULL)
+       return NULL;
+   for (bp = buffer, sp = strtext; *sp != '\0'; bp++, sp++)
+   {
+       switch (state)
+       {
+           case 0:
+               if (*sp == '\\')
+                   state = 1;
+               *bp = *sp;
+               break;
+           case 1:
+               if (*sp == '\'')    /* state=5 */
+               {               /* replace \' with 39 */
+                   bp--;
+                   *bp = '\'';
+                   buflen--;
+                   state = 0;
+               }
+               else if (*sp == '\\')   /* state=6 */
+               {               /* replace \\ with 92 */
+                   bp--;
+                   *bp = '\\';
+                   buflen--;
+                   state = 0;
+               }
+               else
+               {
+                   if (isdigit(*sp))
+                       state = 2;
+                   else
+                       state = 0;
+                   *bp = *sp;
+               }
+               break;
+           case 2:
+               if (isdigit(*sp))
+                   state = 3;
+               else
+                   state = 0;
+               *bp = *sp;
+               break;
+           case 3:
+               if (isdigit(*sp))       /* state=4 */
+               {
+                   int         v;
+
+                   bp -= 3;
+                   sscanf(sp - 2, "%03o", &v);
+                   *bp = v;
+                   buflen -= 3;
+                   state = 0;
+               }
+               else
+               {
+                   *bp = *sp;
+                   state = 0;
+               }
+               break;
+       }
+   }
+   buffer = realloc(buffer, buflen);
+   if (buffer == NULL)
+       return NULL;
+
+   *retbuflen = buflen;
+   return buffer;
+}
index 1a25105ead0523d2e89988e0dbf03ce58899b874..b65f47efbf7d8e9f7efe5ceecaac7b96814a78a3 100644 (file)
@@ -12,7 +12,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: libpq-int.h,v 1.68 2003/05/06 21:51:42 tgl Exp $
+ * $Id: libpq-int.h,v 1.69 2003/05/08 18:16:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -56,7 +56,7 @@ typedef int ssize_t;          /* ssize_t doesn't exist in VC (atleast
  * pqcomm.h describe what the backend knows, not what libpq knows.
  */
 
-#define PG_PROTOCOL_LIBPQ  PG_PROTOCOL(3,107) /* XXX temporary value */
+#define PG_PROTOCOL_LIBPQ  PG_PROTOCOL(3,108) /* XXX temporary value */
 
 /*
  * POSTGRES backend dependent Constants.