OSDN Git Service

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 35355b8..d5a29ef 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,13 +245,100 @@ 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;
        int                     natts = tuple->t_data->t_natts;
        int                     i,
@@ -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);
                }
        }
 
@@ -364,37 +468,22 @@ printatt(unsigned attributeId,
 }
 
 /* ----------------
- *             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 ec6e771..93d4b84 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 28a6e03..879a7e6 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 1c51a1b..f9e31c3 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 6e16853..433fd8e 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 3d1b950..8b720c1 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 2e040f7..cb85be6 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 e89e5f3..46d1e51 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 c3b3ec3..fe420ce 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 bcab815..3d8479f 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 dacfa93..6f574e3 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 bce7760..a590cff 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 65161c5..0720f4e 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,57 +149,73 @@ 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
  *
  * Performs catalog lookups to load a struct fp_info 'fip' for the
@@ -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 8956455..63b08dc 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;
@@ -686,12 +685,6 @@ exec_simple_query(const char *query_string)
                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
         * BEGIN/COMMIT/ABORT statement; we have to force a new xact command
@@ -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
@@ -804,11 +799,6 @@ exec_simple_query(const char *query_string)
                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);
 
        /*
@@ -1278,6 +1383,11 @@ exec_bind_message(StringInfo input_message)
        PortalStart(portal, params);
 
        /*
+        * Apply the result format requests to the portal.
+        */
+       PortalSetResultFormat(portal, numRFormats, rformats);
+
+       /*
         * Send BindComplete.
         */
        if (whereToSendOutput == Remote)
@@ -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))
@@ -1353,6 +1463,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 e3a37b7..bf63dcb 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;
                        /*
@@ -321,6 +314,53 @@ PortalStart(Portal portal, ParamListInfo params)
 }
 
 /*
+ * 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 9d10fe4..981b0f2 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 9c54ed1..5eecb53 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 998b3a3..e23b456 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 229de38..7167c6d 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 d2162e9..b5721f4 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 d9d6a62..ff9cc9d 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 e2617f3..7da359c 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 db513d6..7d0eb39 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 1a25105..b65f47e 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.