Second round of FE/BE protocol changes. Frontend->backend messages now
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 19 Apr 2003 00:02:30 +0000 (00:02 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 19 Apr 2003 00:02:30 +0000 (00:02 +0000)
have length counts, and COPY IN data is packetized into messages.

24 files changed:
doc/src/sgml/libpq.sgml
doc/src/sgml/protocol.sgml
src/backend/commands/copy.c
src/backend/lib/stringinfo.c
src/backend/libpq/auth.c
src/backend/libpq/be-secure.c
src/backend/libpq/pqcomm.c
src/backend/libpq/pqformat.c
src/backend/postmaster/postmaster.c
src/backend/tcop/dest.c
src/backend/tcop/fastpath.c
src/backend/tcop/postgres.c
src/include/lib/stringinfo.h
src/include/libpq/libpq.h
src/include/libpq/pqcomm.h
src/include/libpq/pqformat.h
src/include/tcop/dest.h
src/include/tcop/fastpath.h
src/interfaces/libpq/fe-auth.c
src/interfaces/libpq/fe-connect.c
src/interfaces/libpq/fe-exec.c
src/interfaces/libpq/fe-misc.c
src/interfaces/libpq/libpq-fe.h
src/interfaces/libpq/libpq-int.h

index a1f8d6b5f6b768cde94c3251dcb433eaaf1f680e..7a7bb48ff390d26a8ffa25b6ad5e4c76807b55e1 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.118 2003/04/17 22:26:00 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.119 2003/04/19 00:02:29 tgl Exp $
 -->
 
  <chapter id="libpq">
@@ -1749,9 +1749,10 @@ state will never end.
 <term><function>PQflush</function></term>
 <listitem>
 <para>
-Attempts to flush any data queued to the server,
-returns 0 if successful (or if the send queue is empty) or <symbol>EOF</symbol> if it failed for
-some reason.
+Attempts to flush any data queued to the server.
+Returns 0 if successful (or if the send queue is empty), -1 if it failed for
+some reason, or 1 if it was unable to send all the data in the send queue yet
+(this case can only occur if the connection is nonblocking).
 <synopsis>
 int PQflush(PGconn *conn);
 </synopsis>
index bb25eb74b144210d6a9542665bce4a2d2e310940..529baa1f31be37784e1b17dad8d805e27785b57c 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $Header: /cvsroot/pgsql/doc/src/sgml/protocol.sgml,v 1.27 2003/04/16 20:53:38 tgl Exp $ -->
+<!-- $Header: /cvsroot/pgsql/doc/src/sgml/protocol.sgml,v 1.28 2003/04/19 00:02:29 tgl Exp $ -->
 
 <chapter id="protocol">
  <title>Frontend/Backend Protocol</title>
     Copy-in mode (data transfer to the server) is initiated when the
     backend executes a <command>COPY FROM STDIN</> SQL statement.  The backend
     sends a CopyInResponse message to the frontend.  The frontend should
-    then send zero or more CopyDataRow messages, one per row to be loaded.
-    (For <command>COPY BINARY</>, send CopyBinaryRow messages instead.)
+    then send zero or more CopyData messages, forming a stream of input
+    data.  (The message boundaries are not required to have anything to do
+    with row boundaries, although that is often a reasonable choice.)
     The frontend can terminate the copy-in mode by sending either a CopyDone
     message (allowing successful termination) or a CopyFail message (which
     will cause the <command>COPY</> SQL statement to fail with an
    <para>
     In the event of a backend-detected error during copy-in mode (including
     receipt of a CopyFail message, or indeed any frontend message other than
-    CopyDataRow, CopyBinaryRow, or CopyDone), the backend will issue an
-    ErrorResponse 
+    CopyData or CopyDone), the backend will issue an ErrorResponse 
     message.  If the <command>COPY</> command was issued via an extended-query
     message, the backend will now discard frontend messages until a Sync
     message is received, then it will issue ReadyForQuery and return to normal
     processing.  If the <command>COPY</> command was issued in a simple
     Query message, the rest of that message is discarded and ReadyForQuery
-    is issued.  In either case, any subsequent CopyDataRow, CopyBinaryRow,
-    CopyDone, or CopyFail messages issued by the frontend will simply be
-    dropped.
+    is issued.  In either case, any subsequent CopyData, CopyDone, or CopyFail
+    messages issued by the frontend will simply be dropped.
    </para>
 
    <para>
     Copy-out mode (data transfer from the server) is initiated when the
     backend executes a <command>COPY TO STDOUT</> SQL statement.  The backend
     sends a CopyOutResponse message to the frontend, followed by
-    zero or more CopyDataRow messages, one per row, followed by CopyDone.
-    (For <command>COPY BINARY</>, CopyBinaryRow messages are sent instead.)
+    zero or more CopyData messages (always one per row), followed by CopyDone.
     The backend then reverts to the command-processing mode it was
     in before the <command>COPY</> started, and sends CommandComplete.
-    The frontend cannot abort
-    the transfer (short of closing the connection), but it can discard
-    unwanted CopyDataRow, CopyBinaryRow, and CopyDone messages.
+    The frontend cannot abort the transfer (short of closing the connection),
+    but it can discard unwanted CopyData and CopyDone messages.
    </para>
 
    <para>
     In the event of a backend-detected error during copy-out mode,
     the backend will issue an ErrorResponse message and revert to normal
     processing.  The frontend should treat receipt of ErrorResponse (or
-    indeed any message type other than CopyDataRow, CopyBinaryRow, or
-    CopyDone) as terminating the copy-out mode.
+    indeed any message type other than CopyData or CopyDone) as terminating
+    the copy-out mode.
    </para>
   </sect2>
 
@@ -1157,7 +1154,9 @@ indicate that it may be sent by a frontend (F), a backend (B), or both
 (F &amp; B).
 Notice that although each message includes a byte count at the beginning,
 the message format is defined so that the message end can be found without
-reference to the byte count.  This aids validity checking.
+reference to the byte count.  This aids validity checking.  (The CopyData
+message is an exception, because it forms part of a data stream; the contents
+may not be interpretable on their own.)
 </para>
 
 <VariableList>
@@ -2002,83 +2001,7 @@ CommandComplete (B)
 
 <VarListEntry>
 <Term>
-CopyBinaryRow (F &amp; B)
-</Term>
-<ListItem>
-<Para>
-<VariableList>
-<VarListEntry>
-<Term>
-        Byte1('b')
-</Term>
-<ListItem>
-<Para>
-                Identifies the message as binary COPY data.
-       Note that the message body format is identical to the
-       <command>COPY BINARY</> file-format representation for
-       a single row of data.
-</Para>
-</ListItem>
-</VarListEntry>
-<VarListEntry>
-<Term>
-        Int32
-</Term>
-<ListItem>
-<Para>
-                Length of message contents in bytes, including self.
-</Para>
-</ListItem>
-</VarListEntry>
-<VarListEntry>
-<Term>
-        Int16
-</Term>
-<ListItem>
-<Para>
-                Specifies the number of fields in the row (can be zero).
-</Para>
-</ListItem>
-</VarListEntry>
-</VariableList>
-        Then, for each field, there is the following:
-<VariableList>
-<VarListEntry>
-<Term>
-        Int16
-</Term>
-<ListItem>
-<Para>
-                Zero if the field is null, otherwise the <varname>typlen</>
-       for the field datatype.
-</Para>
-</ListItem>
-</VarListEntry>
-<VarListEntry>
-<Term>
-        Byte<Replaceable>n</Replaceable>
-</Term>
-<ListItem>
-<Para>
-                The value of the field itself in binary format.
-       Omitted if the field is null.
-       <Replaceable>n</Replaceable> is the <varname>typlen</>
-       value if <varname>typlen</> is positive.  If
-       <varname>typlen</> is -1 then the field value begins with
-       its own length as an Int32 (the length includes itself).
-</Para>
-</ListItem>
-</VarListEntry>
-</VariableList>
-
-</Para>
-</ListItem>
-</VarListEntry>
-
-
-<VarListEntry>
-<Term>
-CopyDataRow (F &amp; B)
+CopyData (F &amp; B)
 </Term>
 <ListItem>
 <Para>
@@ -2089,7 +2012,7 @@ CopyDataRow (F &amp; B)
 </Term>
 <ListItem>
 <Para>
-                Identifies the message as textual COPY data.
+                Identifies the message as COPY data.
 </Para>
 </ListItem>
 </VarListEntry>
@@ -2105,12 +2028,14 @@ CopyDataRow (F &amp; B)
 </VarListEntry>
 <VarListEntry>
 <Term>
-        String
+        Byte<Replaceable>n</Replaceable>
 </Term>
 <ListItem>
 <Para>
-                The textual representation of a single row of table data.
-       It should end with a newline.
+                Data that forms part of a COPY datastream.  Messages sent
+       from the backend will always correspond to single data rows,
+       but messages sent by frontends may divide the datastream
+       arbitrarily.
 </Para>
 </ListItem>
 </VarListEntry>
@@ -2236,8 +2161,7 @@ CopyInResponse (B)
 </Term>
 <ListItem>
 <Para>
-                0 for textual copy (CopyDataRow is expected), 1 for
-       binary copy (CopyBinaryRow is expected).
+                0 for textual copy, 1 for binary copy.
 </Para>
 </ListItem>
 </VarListEntry>
@@ -2283,8 +2207,7 @@ CopyOutResponse (B)
 </Term>
 <ListItem>
 <Para>
-                0 for textual copy (CopyDataRow will follow), 1 for
-       binary copy (CopyBinaryRow will follow).
+                0 for textual copy, 1 for binary copy.
 </Para>
 </ListItem>
 </VarListEntry>
@@ -3606,8 +3529,9 @@ StartupMessage (F)
 <ListItem>
 <Para>
                 The protocol version number.  The most significant 16 bits are
-                the major version number (3 for the format described here).
-       The least 16 significant bits are the minor version number.
+                the major version number (3 or more for the format described
+       here).
+       The least significant 16 bits are the minor version number.
 </Para>
 </ListItem>
 </VarListEntry>
@@ -3654,17 +3578,18 @@ StartupMessage (F)
 <ListItem>
 <Para>
                         Command-line arguments for the backend.  (This is
-           deprecated in favor of setting individual GUC
+           deprecated in favor of setting individual run-time
            parameters.)
 </Para>
 </ListItem>
 </VarListEntry>
 </VariableList>
 
-                In addition to the above, any GUC parameter that can be
+                In addition to the above, any run-time parameter that can be
        set at backend start time may be listed.  Such settings
        will be applied during backend start (after parsing the
-       command-line options if any).
+       command-line options if any).  The values will act as
+       session defaults.
 </Para>
 </ListItem>
 </VarListEntry>
@@ -3913,4 +3838,41 @@ not line breaks.
 </sect1>
 
 
+<Sect1 id="protocol-changes">
+<Title>Summary of Changes since Protocol 2.0</Title>
+
+<para>
+This section provides a quick checklist of changes, for the benefit of
+developers trying to update existing client libraries to protocol 3.0.
+</para>
+
+<para>
+The initial startup packet uses a flexible list-of-strings format
+instead of a fixed format.  Notice that session default values for run-time
+parameters can now be specified directly in the startup packet.  (Actually,
+you could do that before using the <literal>options</> field, but given the
+limited width of <literal>options</> and the lack of any way to quote
+whitespace in the values, it wasn't a very safe technique.)
+</para>
+
+<para>
+All messages now have a length count immediately following the message type
+byte (except for startup packets, which have no type byte).  Also note that
+PasswordMessage now has a type byte.
+</para>
+
+<para>
+COPY data is now encapsulated into CopyData and CopyDone messages.  There
+is a well-defined way to recover from errors during COPY.
+</para>
+
+<note>
+<para>
+Additional changes will be documented as they are implemented.
+</para>
+</note>
+
+</sect1>
+
+
 </Chapter>
index 0f20bfb2aeadbbdba6dc9aced3c7c7c2766622f1..fd85f48b592877ce2318e50e02c9b9c32a478adf 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.191 2003/04/04 20:42:11 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.192 2003/04/19 00:02:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,6 +31,7 @@
 #include "commands/trigger.h"
 #include "executor/executor.h"
 #include "libpq/libpq.h"
+#include "libpq/pqformat.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
 #define OCTVALUE(c) ((c) - '0')
 
+/*
+ * Represents the different source/dest cases we need to worry about at
+ * the bottom level
+ */
+typedef enum CopyDest
+{
+   COPY_FILE,                  /* to/from file */
+   COPY_OLD_FE,                /* to/from frontend (old protocol) */
+   COPY_NEW_FE                 /* to/from frontend (new protocol) */
+} CopyDest;
+
 /*
  * Represents the type of data returned by CopyReadAttribute()
  */
@@ -61,13 +73,13 @@ typedef enum CopyReadResult
 
 /* non-export function prototypes */
 static void CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
-      FILE *fp, char *delim, char *null_print);
+                  char *delim, char *null_print);
 static void CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
-        FILE *fp, char *delim, char *null_print);
+                    char *delim, char *null_print);
 static Oid GetInputFunction(Oid type);
 static Oid GetTypeElement(Oid type);
-static char *CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result);
-static void CopyAttributeOut(FILE *fp, char *string, char *delim);
+static char *CopyReadAttribute(const char *delim, CopyReadResult *result);
+static void CopyAttributeOut(char *string, char *delim);
 static List *CopyGetAttnums(Relation rel, List *attnamelist);
 
 static const char BinarySignature[12] = "PGBCOPY\n\377\r\n\0";
@@ -77,7 +89,11 @@ static const char BinarySignature[12] = "PGBCOPY\n\377\r\n\0";
  * never been reentrant...
  */
 int            copy_lineno = 0;    /* exported for use by elog() -- dz */
-static bool fe_eof;
+
+static CopyDest copy_dest;
+static FILE *copy_file;            /* if copy_dest == COPY_FILE */
+static StringInfo copy_msgbuf; /* if copy_dest == COPY_NEW_FE */
+static bool fe_eof;                /* true if detected end of copy data */
 
 /*
  * These static variables are used to avoid incurring overhead for each
@@ -96,98 +112,229 @@ static int    server_encoding;
 /*
  * Internal communications functions
  */
-static void CopySendData(void *databuf, int datasize, FILE *fp);
-static void CopySendString(const char *str, FILE *fp);
-static void CopySendChar(char c, FILE *fp);
-static void CopyGetData(void *databuf, int datasize, FILE *fp);
-static int CopyGetChar(FILE *fp);
-static int CopyGetEof(FILE *fp);
-static int CopyPeekChar(FILE *fp);
-static void CopyDonePeek(FILE *fp, int c, bool pickup);
+static void SendCopyBegin(bool binary);
+static void ReceiveCopyBegin(bool binary);
+static void SendCopyEnd(bool binary);
+static void CopySendData(void *databuf, int datasize);
+static void CopySendString(const char *str);
+static void CopySendChar(char c);
+static void CopyGetData(void *databuf, int datasize);
+static int CopyGetChar(void);
+#define CopyGetEof()  (fe_eof)
+static int CopyPeekChar(void);
+static void CopyDonePeek(int c, bool pickup);
 
 /*
- * CopySendData sends output data either to the file
- * specified by fp or, if fp is NULL, using the standard
- * backend->frontend functions
- *
- * CopySendString does the same for null-terminated strings
- * CopySendChar does the same for single characters
- *
- * NB: no data conversion is applied by these functions
+ * Send copy start/stop messages for frontend copies.  These have changed
+ * in past protocol redesigns.
  */
 static void
-CopySendData(void *databuf, int datasize, FILE *fp)
+SendCopyBegin(bool binary)
 {
-   if (!fp)
+   if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
+   {
+       pq_putbytes("H", 1);    /* new way */
+       /* XXX grottiness needed for old protocol */
+       pq_startcopyout();
+       copy_dest = COPY_NEW_FE;
+   }
+   else if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
    {
-       if (pq_putbytes((char *) databuf, datasize))
-           fe_eof = true;
+       pq_putbytes("H", 1);    /* old way */
+       /* grottiness needed for old protocol */
+       pq_startcopyout();
+       copy_dest = COPY_OLD_FE;
    }
    else
    {
-       fwrite(databuf, datasize, 1, fp);
-       if (ferror(fp))
-           elog(ERROR, "CopySendData: %m");
+       pq_putbytes("B", 1);    /* very old way */
+       /* grottiness needed for old protocol */
+       pq_startcopyout();
+       copy_dest = COPY_OLD_FE;
    }
 }
 
 static void
-CopySendString(const char *str, FILE *fp)
+ReceiveCopyBegin(bool binary)
 {
-   CopySendData((void *) str, strlen(str), fp);
+   if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
+   {
+       pq_putbytes("G", 1);    /* new way */
+       copy_dest = COPY_NEW_FE;
+       copy_msgbuf = makeStringInfo();
+   }
+   else if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
+   {
+       pq_putbytes("G", 1);    /* old way */
+       copy_dest = COPY_OLD_FE;
+   }
+   else
+   {
+       pq_putbytes("D", 1);    /* very old way */
+       copy_dest = COPY_OLD_FE;
+   }
+   /* We *must* flush here to ensure FE knows it can send. */
+   pq_flush();
 }
 
 static void
-CopySendChar(char c, FILE *fp)
+SendCopyEnd(bool binary)
 {
-   CopySendData(&c, 1, fp);
+   if (!binary)
+       CopySendData("\\.\n", 3);
+   pq_endcopyout(false);
 }
 
 /*
- * CopyGetData reads output data either from the file
- * specified by fp or, if fp is NULL, using the standard
- * backend->frontend functions
- *
- * CopyGetChar does the same for single characters
- * CopyGetEof checks if it's EOF on the input (or, check for EOF result
- *     from CopyGetChar)
+ * CopySendData sends output data to the destination (file or frontend)
+ * CopySendString does the same for null-terminated strings
+ * CopySendChar does the same for single characters
  *
  * NB: no data conversion is applied by these functions
  */
 static void
-CopyGetData(void *databuf, int datasize, FILE *fp)
+CopySendData(void *databuf, int datasize)
 {
-   if (!fp)
+   switch (copy_dest)
    {
-       if (pq_getbytes((char *) databuf, datasize))
-           fe_eof = true;
+       case COPY_FILE:
+           fwrite(databuf, datasize, 1, copy_file);
+           if (ferror(copy_file))
+               elog(ERROR, "CopySendData: %m");
+           break;
+       case COPY_OLD_FE:
+           if (pq_putbytes((char *) databuf, datasize))
+               fe_eof = true;
+           break;
+       case COPY_NEW_FE:
+           /* XXX fix later */
+           if (pq_putbytes((char *) databuf, datasize))
+               fe_eof = true;
+           break;
    }
-   else
-       fread(databuf, datasize, 1, fp);
 }
 
-static int
-CopyGetChar(FILE *fp)
+static void
+CopySendString(const char *str)
+{
+   CopySendData((void *) str, strlen(str));
+}
+
+static void
+CopySendChar(char c)
+{
+   CopySendData(&c, 1);
+}
+
+/*
+ * CopyGetData reads data from the source (file or frontend)
+ * CopyGetChar does the same for single characters
+ *
+ * CopyGetEof checks if EOF was detected by previous Get operation.
+ *
+ * Note: when copying from the frontend, we expect a proper EOF mark per
+ * protocol; if the frontend simply drops the connection, we raise error.
+ * It seems unwise to allow the COPY IN to complete normally in that case.
+ *
+ * NB: no data conversion is applied by these functions
+ */
+static void
+CopyGetData(void *databuf, int datasize)
 {
-   if (!fp)
+   switch (copy_dest)
    {
-       int         ch = pq_getbyte();
+       case COPY_FILE:
+           fread(databuf, datasize, 1, copy_file);
+           if (feof(copy_file))
+               fe_eof = true;
+           break;
+       case COPY_OLD_FE:
+           if (pq_getbytes((char *) databuf, datasize))
+           {
+               /* Only a \. terminator is legal EOF in old protocol */
+               elog(ERROR, "unexpected EOF on client connection");
+           }
+           break;
+       case COPY_NEW_FE:
+           while (datasize > 0 && !fe_eof)
+           {
+               int     avail;
 
-       if (ch == EOF)
-           fe_eof = true;
-       return ch;
+               while (copy_msgbuf->cursor >= copy_msgbuf->len)
+               {
+                   /* Try to receive another message */
+                   int         mtype;
+
+                   mtype = pq_getbyte();
+                   if (mtype == EOF)
+                       elog(ERROR, "unexpected EOF on client connection");
+                   if (pq_getmessage(copy_msgbuf, 0))
+                       elog(ERROR, "unexpected EOF on client connection");
+                   switch (mtype)
+                   {
+                       case 'd': /* CopyData */
+                           break;
+                       case 'c': /* CopyDone */
+                           /* COPY IN correctly terminated by frontend */
+                           fe_eof = true;
+                           return;
+                       case 'f': /* CopyFail */
+                           elog(ERROR, "COPY IN failed: %s",
+                                pq_getmsgstring(copy_msgbuf));
+                           break;
+                       default:
+                           elog(ERROR, "unexpected message type %c during COPY IN",
+                                mtype);
+                           break;
+                   }
+               }
+               avail = copy_msgbuf->len - copy_msgbuf->cursor;
+               if (avail > datasize)
+                   avail = datasize;
+               pq_copymsgbytes(copy_msgbuf, databuf, avail);
+               databuf = (void *) ((char *) databuf + avail);
+               datasize =- avail;
+           }
+           break;
    }
-   else
-       return getc(fp);
 }
 
 static int
-CopyGetEof(FILE *fp)
+CopyGetChar(void)
 {
-   if (!fp)
-       return fe_eof;
-   else
-       return feof(fp);
+   int     ch;
+
+   switch (copy_dest)
+   {
+       case COPY_FILE:
+           ch = getc(copy_file);
+           break;
+       case COPY_OLD_FE:
+           ch = pq_getbyte();
+           if (ch == EOF)
+           {
+               /* Only a \. terminator is legal EOF in old protocol */
+               elog(ERROR, "unexpected EOF on client connection");
+           }
+           break;
+       case COPY_NEW_FE:
+       {
+           unsigned char   cc;
+
+           CopyGetData(&cc, 1);
+           if (fe_eof)
+               ch = EOF;
+           else
+               ch = cc;
+           break;
+       }
+       default:
+           ch = EOF;
+           break;
+   }
+   if (ch == EOF)
+       fe_eof = true;
+   return ch;
 }
 
 /*
@@ -200,40 +347,74 @@ CopyGetEof(FILE *fp)
  * (if pickup is true) or leave it on the stream (if pickup is false).
  */
 static int
-CopyPeekChar(FILE *fp)
+CopyPeekChar(void)
 {
-   if (!fp)
+   int     ch;
+
+   switch (copy_dest)
    {
-       int         ch = pq_peekbyte();
+       case COPY_FILE:
+           ch = getc(copy_file);
+           break;
+       case COPY_OLD_FE:
+           ch = pq_peekbyte();
+           if (ch == EOF)
+           {
+               /* Only a \. terminator is legal EOF in old protocol */
+               elog(ERROR, "unexpected EOF on client connection");
+           }
+           break;
+       case COPY_NEW_FE:
+       {
+           unsigned char   cc;
 
-       if (ch == EOF)
-           fe_eof = true;
-       return ch;
+           CopyGetData(&cc, 1);
+           if (fe_eof)
+               ch = EOF;
+           else
+               ch = cc;
+           break;
+       }
+       default:
+           ch = EOF;
+           break;
    }
-   else
-       return getc(fp);
+   if (ch == EOF)
+       fe_eof = true;
+   return ch;
 }
 
 static void
-CopyDonePeek(FILE *fp, int c, bool pickup)
+CopyDonePeek(int c, bool pickup)
 {
-   if (!fp)
-   {
-       if (pickup)
-       {
-           /* We want to pick it up */
-           (void) pq_getbyte();
-       }
-       /* If we didn't want to pick it up, just leave it where it sits */
-   }
-   else
+   if (fe_eof)
+       return;                 /* can't unget an EOF */
+   switch (copy_dest)
    {
-       if (!pickup)
-       {
-           /* We don't want to pick it up - so put it back in there */
-           ungetc(c, fp);
-       }
-       /* If we wanted to pick it up, it's already done */
+       case COPY_FILE:
+           if (!pickup) 
+           {
+               /* We don't want to pick it up - so put it back in there */
+               ungetc(c, copy_file);
+           }
+           /* If we wanted to pick it up, it's already done */
+           break;
+       case COPY_OLD_FE:
+           if (pickup)
+           {
+               /* We want to pick it up */
+               (void) pq_getbyte();
+           }
+           /* If we didn't want to pick it up, just leave it where it sits */
+           break;
+       case COPY_NEW_FE:
+           if (!pickup)
+           {
+               /* We don't want to pick it up - so put it back in there */
+               copy_msgbuf->cursor--;
+           }
+           /* If we wanted to pick it up, it's already done */
+           break;
    }
 }
 
@@ -287,7 +468,6 @@ DoCopy(const CopyStmt *stmt)
    bool        oids = false;
    char       *delim = NULL;
    char       *null_print = NULL;
-   FILE       *fp;
    Relation    rel;
    AclMode     required_access = (is_from ? ACL_INSERT : ACL_SELECT);
    AclResult   aclresult;
@@ -397,6 +577,11 @@ DoCopy(const CopyStmt *stmt)
    client_encoding = pg_get_client_encoding();
    server_encoding = GetDatabaseEncoding();
 
+   copy_dest = COPY_FILE;      /* default */
+   copy_file = NULL;
+   copy_msgbuf = NULL;
+   fe_eof = false;
+
    if (is_from)
    {                           /* copy from file to database */
        if (rel->rd_rel->relkind != RELKIND_RELATION)
@@ -414,33 +599,30 @@ DoCopy(const CopyStmt *stmt)
        if (pipe)
        {
            if (IsUnderPostmaster)
-           {
-               ReceiveCopyBegin();
-               fp = NULL;
-           }
+               ReceiveCopyBegin(binary);
            else
-               fp = stdin;
+               copy_file = stdin;
        }
        else
        {
            struct stat st;
 
-           fp = AllocateFile(filename, PG_BINARY_R);
+           copy_file = AllocateFile(filename, PG_BINARY_R);
 
-           if (fp == NULL)
+           if (copy_file == NULL)
                elog(ERROR, "COPY command, running in backend with "
                     "effective uid %d, could not open file '%s' for "
                     "reading.  Errno = %s (%d).",
                     (int) geteuid(), filename, strerror(errno), errno);
 
-           fstat(fileno(fp), &st);
+           fstat(fileno(copy_file), &st);
            if (S_ISDIR(st.st_mode))
            {
-               FreeFile(fp);
+               FreeFile(copy_file);
                elog(ERROR, "COPY: %s is a directory", filename);
            }
        }
-       CopyFrom(rel, attnumlist, binary, oids, fp, delim, null_print);
+       CopyFrom(rel, attnumlist, binary, oids, delim, null_print);
    }
    else
    {                           /* copy from database to file */
@@ -459,13 +641,9 @@ DoCopy(const CopyStmt *stmt)
        if (pipe)
        {
            if (IsUnderPostmaster)
-           {
-               SendCopyBegin();
-               pq_startcopyout();
-               fp = NULL;
-           }
+               SendCopyBegin(binary);
            else
-               fp = stdout;
+               copy_file = stdout;
        }
        else
        {
@@ -481,33 +659,28 @@ DoCopy(const CopyStmt *stmt)
                     " COPY command");
 
            oumask = umask((mode_t) 022);
-           fp = AllocateFile(filename, PG_BINARY_W);
+           copy_file = AllocateFile(filename, PG_BINARY_W);
            umask(oumask);
 
-           if (fp == NULL)
+           if (copy_file == NULL)
                elog(ERROR, "COPY command, running in backend with "
                     "effective uid %d, could not open file '%s' for "
                     "writing.  Errno = %s (%d).",
                     (int) geteuid(), filename, strerror(errno), errno);
-           fstat(fileno(fp), &st);
+           fstat(fileno(copy_file), &st);
            if (S_ISDIR(st.st_mode))
            {
-               FreeFile(fp);
+               FreeFile(copy_file);
                elog(ERROR, "COPY: %s is a directory", filename);
            }
        }
-       CopyTo(rel, attnumlist, binary, oids, fp, delim, null_print);
+       CopyTo(rel, attnumlist, binary, oids, delim, null_print);
    }
 
    if (!pipe)
-       FreeFile(fp);
-   else if (!is_from)
-   {
-       if (!binary)
-           CopySendData("\\.\n", 3, fp);
-       if (IsUnderPostmaster)
-           pq_endcopyout(false);
-   }
+       FreeFile(copy_file);
+   else if (IsUnderPostmaster && !is_from)
+       SendCopyEnd(binary);
    pfree(attribute_buf.data);
 
    /*
@@ -525,7 +698,7 @@ DoCopy(const CopyStmt *stmt)
  */
 static void
 CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
-      FILE *fp, char *delim, char *null_print)
+      char *delim, char *null_print)
 {
    HeapTuple   tuple;
    TupleDesc   tupDesc;
@@ -589,18 +762,18 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
        int32       tmp;
 
        /* Signature */
-       CopySendData((char *) BinarySignature, 12, fp);
+       CopySendData((char *) BinarySignature, 12);
        /* Integer layout field */
        tmp = 0x01020304;
-       CopySendData(&tmp, sizeof(int32), fp);
+       CopySendData(&tmp, sizeof(int32));
        /* Flags field */
        tmp = 0;
        if (oids)
            tmp |= (1 << 16);
-       CopySendData(&tmp, sizeof(int32), fp);
+       CopySendData(&tmp, sizeof(int32));
        /* No header extension */
        tmp = 0;
-       CopySendData(&tmp, sizeof(int32), fp);
+       CopySendData(&tmp, sizeof(int32));
    }
 
    mySnapshot = CopyQuerySnapshot();
@@ -621,15 +794,15 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
            /* Binary per-tuple header */
            int16       fld_count = attr_count;
 
-           CopySendData(&fld_count, sizeof(int16), fp);
+           CopySendData(&fld_count, sizeof(int16));
            /* Send OID if wanted --- note fld_count doesn't include it */
            if (oids)
            {
                Oid         oid = HeapTupleGetOid(tuple);
 
                fld_size = sizeof(Oid);
-               CopySendData(&fld_size, sizeof(int16), fp);
-               CopySendData(&oid, sizeof(Oid), fp);
+               CopySendData(&fld_size, sizeof(int16));
+               CopySendData(&oid, sizeof(Oid));
            }
        }
        else
@@ -639,7 +812,7 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
            {
                string = DatumGetCString(DirectFunctionCall1(oidout,
                              ObjectIdGetDatum(HeapTupleGetOid(tuple))));
-               CopySendString(string, fp);
+               CopySendString(string);
                need_delim = true;
            }
        }
@@ -655,7 +828,7 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
            if (!binary)
            {
                if (need_delim)
-                   CopySendChar(delim[0], fp);
+                   CopySendChar(delim[0]);
                need_delim = true;
            }
 
@@ -663,12 +836,12 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
            {
                if (!binary)
                {
-                   CopySendString(null_print, fp);     /* null indicator */
+                   CopySendString(null_print);     /* null indicator */
                }
                else
                {
                    fld_size = 0;       /* null marker */
-                   CopySendData(&fld_size, sizeof(int16), fp);
+                   CopySendData(&fld_size, sizeof(int16));
                }
            }
            else
@@ -679,12 +852,12 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
                                                           value,
                                  ObjectIdGetDatum(elements[attnum - 1]),
                            Int32GetDatum(attr[attnum - 1]->atttypmod)));
-                   CopyAttributeOut(fp, string, delim);
+                   CopyAttributeOut(string, delim);
                }
                else
                {
                    fld_size = attr[attnum - 1]->attlen;
-                   CopySendData(&fld_size, sizeof(int16), fp);
+                   CopySendData(&fld_size, sizeof(int16));
                    if (isvarlena[attnum - 1])
                    {
                        /* varlena */
@@ -694,16 +867,14 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
                        value = PointerGetDatum(PG_DETOAST_DATUM(value));
 
                        CopySendData(DatumGetPointer(value),
-                                    VARSIZE(value),
-                                    fp);
+                                    VARSIZE(value));
                    }
                    else if (!attr[attnum - 1]->attbyval)
                    {
                        /* fixed-length pass-by-reference */
                        Assert(fld_size > 0);
                        CopySendData(DatumGetPointer(value),
-                                    fld_size,
-                                    fp);
+                                    fld_size);
                    }
                    else
                    {
@@ -717,15 +888,14 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
                         */
                        store_att_byval(&datumBuf, value, fld_size);
                        CopySendData(&datumBuf,
-                                    fld_size,
-                                    fp);
+                                    fld_size);
                    }
                }
            }
        }
 
        if (!binary)
-           CopySendChar('\n', fp);
+           CopySendChar('\n');
 
        MemoryContextSwitchTo(oldcontext);
    }
@@ -737,7 +907,7 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
        /* Generate trailer for a binary copy */
        int16       fld_count = -1;
 
-       CopySendData(&fld_count, sizeof(int16), fp);
+       CopySendData(&fld_count, sizeof(int16));
    }
 
    MemoryContextDelete(mycontext);
@@ -753,7 +923,7 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
  */
 static void
 CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
-        FILE *fp, char *delim, char *null_print)
+        char *delim, char *null_print)
 {
    HeapTuple   tuple;
    TupleDesc   tupDesc;
@@ -905,30 +1075,30 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
        int32       tmp;
 
        /* Signature */
-       CopyGetData(readSig, 12, fp);
-       if (CopyGetEof(fp) || memcmp(readSig, BinarySignature, 12) != 0)
+       CopyGetData(readSig, 12);
+       if (CopyGetEof() || memcmp(readSig, BinarySignature, 12) != 0)
            elog(ERROR, "COPY BINARY: file signature not recognized");
        /* Integer layout field */
-       CopyGetData(&tmp, sizeof(int32), fp);
-       if (CopyGetEof(fp) || tmp != 0x01020304)
+       CopyGetData(&tmp, sizeof(int32));
+       if (CopyGetEof() || tmp != 0x01020304)
            elog(ERROR, "COPY BINARY: incompatible integer layout");
        /* Flags field */
-       CopyGetData(&tmp, sizeof(int32), fp);
-       if (CopyGetEof(fp))
+       CopyGetData(&tmp, sizeof(int32));
+       if (CopyGetEof())
            elog(ERROR, "COPY BINARY: bogus file header (missing flags)");
        file_has_oids = (tmp & (1 << 16)) != 0;
        tmp &= ~(1 << 16);
        if ((tmp >> 16) != 0)
            elog(ERROR, "COPY BINARY: unrecognized critical flags in header");
        /* Header extension length */
-       CopyGetData(&tmp, sizeof(int32), fp);
-       if (CopyGetEof(fp) || tmp < 0)
+       CopyGetData(&tmp, sizeof(int32));
+       if (CopyGetEof() || tmp < 0)
            elog(ERROR, "COPY BINARY: bogus file header (missing length)");
        /* Skip extension header, if present */
        while (tmp-- > 0)
        {
-           CopyGetData(readSig, 1, fp);
-           if (CopyGetEof(fp))
+           CopyGetData(readSig, 1);
+           if (CopyGetEof())
                elog(ERROR, "COPY BINARY: bogus file header (wrong length)");
        }
    }
@@ -936,6 +1106,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
    values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
    nulls = (char *) palloc(num_phys_attrs * sizeof(char));
 
+   /* Initialize static variables */
    copy_lineno = 0;
    fe_eof = false;
 
@@ -970,7 +1141,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
 
            if (file_has_oids)
            {
-               string = CopyReadAttribute(fp, delim, &result);
+               string = CopyReadAttribute(delim, &result);
 
                if (result == END_OF_FILE && *string == '\0')
                {
@@ -1006,7 +1177,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
                    elog(ERROR, "Missing data for column \"%s\"",
                         NameStr(attr[m]->attname));
 
-               string = CopyReadAttribute(fp, delim, &result);
+               string = CopyReadAttribute(delim, &result);
 
                if (result == END_OF_FILE && *string == '\0' &&
                    cur == attnumlist && !file_has_oids)
@@ -1051,8 +1222,8 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
            int16       fld_count,
                        fld_size;
 
-           CopyGetData(&fld_count, sizeof(int16), fp);
-           if (CopyGetEof(fp) || fld_count == -1)
+           CopyGetData(&fld_count, sizeof(int16));
+           if (CopyGetEof() || fld_count == -1)
            {
                done = true;
                break;
@@ -1064,14 +1235,14 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
 
            if (file_has_oids)
            {
-               CopyGetData(&fld_size, sizeof(int16), fp);
-               if (CopyGetEof(fp))
+               CopyGetData(&fld_size, sizeof(int16));
+               if (CopyGetEof())
                    elog(ERROR, "COPY BINARY: unexpected EOF");
                if (fld_size != (int16) sizeof(Oid))
                    elog(ERROR, "COPY BINARY: sizeof(Oid) is %d, expected %d",
                         (int) fld_size, (int) sizeof(Oid));
-               CopyGetData(&loaded_oid, sizeof(Oid), fp);
-               if (CopyGetEof(fp))
+               CopyGetData(&loaded_oid, sizeof(Oid));
+               if (CopyGetEof())
                    elog(ERROR, "COPY BINARY: unexpected EOF");
                if (loaded_oid == InvalidOid)
                    elog(ERROR, "COPY BINARY: Invalid Oid");
@@ -1085,8 +1256,8 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
 
                i++;
 
-               CopyGetData(&fld_size, sizeof(int16), fp);
-               if (CopyGetEof(fp))
+               CopyGetData(&fld_size, sizeof(int16));
+               if (CopyGetEof())
                    elog(ERROR, "COPY BINARY: unexpected EOF");
                if (fld_size == 0)
                    continue;   /* it's NULL; nulls[attnum-1] already set */
@@ -1099,17 +1270,16 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
                    int32       varlena_size;
                    Pointer     varlena_ptr;
 
-                   CopyGetData(&varlena_size, sizeof(int32), fp);
-                   if (CopyGetEof(fp))
+                   CopyGetData(&varlena_size, sizeof(int32));
+                   if (CopyGetEof())
                        elog(ERROR, "COPY BINARY: unexpected EOF");
                    if (varlena_size < (int32) sizeof(int32))
                        elog(ERROR, "COPY BINARY: bogus varlena length");
                    varlena_ptr = (Pointer) palloc(varlena_size);
                    VARATT_SIZEP(varlena_ptr) = varlena_size;
                    CopyGetData(VARDATA(varlena_ptr),
-                               varlena_size - sizeof(int32),
-                               fp);
-                   if (CopyGetEof(fp))
+                               varlena_size - sizeof(int32));
+                   if (CopyGetEof())
                        elog(ERROR, "COPY BINARY: unexpected EOF");
                    values[m] = PointerGetDatum(varlena_ptr);
                }
@@ -1120,8 +1290,8 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
 
                    Assert(fld_size > 0);
                    refval_ptr = (Pointer) palloc(fld_size);
-                   CopyGetData(refval_ptr, fld_size, fp);
-                   if (CopyGetEof(fp))
+                   CopyGetData(refval_ptr, fld_size);
+                   if (CopyGetEof())
                        elog(ERROR, "COPY BINARY: unexpected EOF");
                    values[m] = PointerGetDatum(refval_ptr);
                }
@@ -1135,8 +1305,8 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
                     * how shorter data values are aligned within a Datum.
                     */
                    Assert(fld_size > 0 && fld_size <= sizeof(Datum));
-                   CopyGetData(&datumBuf, fld_size, fp);
-                   if (CopyGetEof(fp))
+                   CopyGetData(&datumBuf, fld_size);
+                   if (CopyGetEof())
                        elog(ERROR, "COPY BINARY: unexpected EOF");
                    values[m] = fetch_att(&datumBuf, true, fld_size);
                }
@@ -1324,7 +1494,7 @@ GetTypeElement(Oid type)
  */
 
 static char *
-CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
+CopyReadAttribute(const char *delim, CopyReadResult *result)
 {
    int         c;
    int         delimc = (unsigned char) delim[0];
@@ -1344,7 +1514,7 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
 
    for (;;)
    {
-       c = CopyGetChar(fp);
+       c = CopyGetChar();
        if (c == EOF)
        {
            *result = END_OF_FILE;
@@ -1359,7 +1529,7 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
            break;
        if (c == '\\')
        {
-           c = CopyGetChar(fp);
+           c = CopyGetChar();
            if (c == EOF)
            {
                *result = END_OF_FILE;
@@ -1379,16 +1549,16 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
                        int         val;
 
                        val = OCTVALUE(c);
-                       c = CopyPeekChar(fp);
+                       c = CopyPeekChar();
                        if (ISOCTAL(c))
                        {
                            val = (val << 3) + OCTVALUE(c);
-                           CopyDonePeek(fp, c, true /* pick up */ );
-                           c = CopyPeekChar(fp);
+                           CopyDonePeek(c, true /* pick up */ );
+                           c = CopyPeekChar();
                            if (ISOCTAL(c))
                            {
                                val = (val << 3) + OCTVALUE(c);
-                               CopyDonePeek(fp, c, true /* pick up */ );
+                               CopyDonePeek(c, true /* pick up */ );
                            }
                            else
                            {
@@ -1397,7 +1567,7 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
                                    *result = END_OF_FILE;
                                    goto copy_eof;
                                }
-                               CopyDonePeek(fp, c, false /* put back */ );
+                               CopyDonePeek(c, false /* put back */ );
                            }
                        }
                        else
@@ -1407,7 +1577,7 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
                                *result = END_OF_FILE;
                                goto copy_eof;
                            }
-                           CopyDonePeek(fp, c, false /* put back */ );
+                           CopyDonePeek(c, false /* put back */ );
                        }
                        c = val & 0377;
                    }
@@ -1441,9 +1611,21 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
                    c = '\v';
                    break;
                case '.':
-                   c = CopyGetChar(fp);
+                   c = CopyGetChar();
                    if (c != '\n')
                        elog(ERROR, "CopyReadAttribute: end of record marker corrupted");
+                   /*
+                    * In protocol version 3, we should ignore anything after
+                    * \. up to the protocol end of copy data.  (XXX maybe
+                    * better not to treat \. as special?)
+                    */
+                   if (copy_dest == COPY_NEW_FE)
+                   {
+                       while (c != EOF)
+                       {
+                           c = CopyGetChar();
+                       }
+                   }
                    *result = END_OF_FILE;
                    goto copy_eof;
            }
@@ -1458,7 +1640,7 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
            mblen = pg_encoding_mblen(client_encoding, s);
            for (j = 1; j < mblen; j++)
            {
-               c = CopyGetChar(fp);
+               c = CopyGetChar();
                if (c == EOF)
                {
                    *result = END_OF_FILE;
@@ -1488,7 +1670,7 @@ copy_eof:
 }
 
 static void
-CopyAttributeOut(FILE *fp, char *server_string, char *delim)
+CopyAttributeOut(char *server_string, char *delim)
 {
    char       *string;
    char        c;
@@ -1511,30 +1693,30 @@ CopyAttributeOut(FILE *fp, char *server_string, char *delim)
        switch (c)
        {
            case '\b':
-               CopySendString("\\b", fp);
+               CopySendString("\\b");
                break;
            case '\f':
-               CopySendString("\\f", fp);
+               CopySendString("\\f");
                break;
            case '\n':
-               CopySendString("\\n", fp);
+               CopySendString("\\n");
                break;
            case '\r':
-               CopySendString("\\r", fp);
+               CopySendString("\\r");
                break;
            case '\t':
-               CopySendString("\\t", fp);
+               CopySendString("\\t");
                break;
            case '\v':
-               CopySendString("\\v", fp);
+               CopySendString("\\v");
                break;
            case '\\':
-               CopySendString("\\\\", fp);
+               CopySendString("\\\\");
                break;
            default:
                if (c == delimc)
-                   CopySendChar('\\', fp);
-               CopySendChar(c, fp);
+                   CopySendChar('\\');
+               CopySendChar(c);
 
                /*
                 * We can skip pg_encoding_mblen() overhead when encoding
@@ -1546,7 +1728,7 @@ CopyAttributeOut(FILE *fp, char *server_string, char *delim)
                    /* send additional bytes of the char, if any */
                    mblen = pg_encoding_mblen(client_encoding, string);
                    for (i = 1; i < mblen; i++)
-                       CopySendChar(string[i], fp);
+                       CopySendChar(string[i]);
                }
                break;
        }
index 9b9fc3d180078aa951695113432ec771a78bc55d..0f758b1bd2dda4c8a168e35853f2365719be1ac2 100644 (file)
@@ -9,15 +9,15 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *   $Id: stringinfo.c,v 1.32 2002/09/04 20:31:18 momjian Exp $
+ *   $Id: stringinfo.c,v 1.33 2003/04/19 00:02:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
-
-
 #include "postgres.h"
+
 #include "lib/stringinfo.h"
 
+
 /*
  * makeStringInfo
  *
@@ -50,41 +50,7 @@ initStringInfo(StringInfo str)
    str->maxlen = size;
    str->len = 0;
    str->data[0] = '\0';
-}
-
-/*
- * enlargeStringInfo
- *
- * Internal routine: make sure there is enough space for 'needed' more bytes
- * ('needed' does not include the terminating null).
- *
- * NB: because we use repalloc() to enlarge the buffer, the string buffer
- * will remain allocated in the same memory context that was current when
- * initStringInfo was called, even if another context is now current.
- * This is the desired and indeed critical behavior!
- */
-static void
-enlargeStringInfo(StringInfo str, int needed)
-{
-   int         newlen;
-
-   needed += str->len + 1;     /* total space required now */
-   if (needed <= str->maxlen)
-       return;                 /* got enough space already */
-
-   /*
-    * We don't want to allocate just a little more space with each
-    * append; for efficiency, double the buffer size each time it
-    * overflows. Actually, we might need to more than double it if
-    * 'needed' is big...
-    */
-   newlen = 2 * str->maxlen;
-   while (needed > newlen)
-       newlen = 2 * newlen;
-
-   str->data = (char *) repalloc(str->data, newlen);
-
-   str->maxlen = newlen;
+   str->cursor = 0;
 }
 
 /*
@@ -147,8 +113,9 @@ appendStringInfo(StringInfo str, const char *fmt,...)
    }
 }
 
-/*------------------------
+/*
  * appendStringInfoChar
+ *
  * Append a single byte to str.
  * Like appendStringInfo(str, "%c", ch) but much faster.
  */
@@ -189,3 +156,44 @@ appendBinaryStringInfo(StringInfo str, const char *data, int datalen)
     */
    str->data[str->len] = '\0';
 }
+
+/*
+ * enlargeStringInfo
+ *
+ * Make sure there is enough space for 'needed' more bytes
+ * ('needed' does not include the terminating null).
+ *
+ * External callers need not concern themselves with this, since all
+ * stringinfo.c routines do it automatically.  However, if a caller
+ * knows that a StringInfo will eventually become X bytes large, it
+ * can save some palloc overhead by enlarging the buffer before starting
+ * to store data in it.
+ *
+ * NB: because we use repalloc() to enlarge the buffer, the string buffer
+ * will remain allocated in the same memory context that was current when
+ * initStringInfo was called, even if another context is now current.
+ * This is the desired and indeed critical behavior!
+ */
+void
+enlargeStringInfo(StringInfo str, int needed)
+{
+   int         newlen;
+
+   needed += str->len + 1;     /* total space required now */
+   if (needed <= str->maxlen)
+       return;                 /* got enough space already */
+
+   /*
+    * We don't want to allocate just a little more space with each
+    * append; for efficiency, double the buffer size each time it
+    * overflows. Actually, we might need to more than double it if
+    * 'needed' is big...
+    */
+   newlen = 2 * str->maxlen;
+   while (needed > newlen)
+       newlen = 2 * newlen;
+
+   str->data = (char *) repalloc(str->data, newlen);
+
+   str->maxlen = newlen;
+}
index 5396cc47c18435b27eb275a9c7567daa0f238701..a5dc8eff2da02191773fc3439a6f60fa7c6cd83e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.98 2003/04/17 22:26:01 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.99 2003/04/19 00:02:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -37,6 +37,7 @@
 
 static void sendAuthRequest(Port *port, AuthRequest areq);
 static void auth_failed(Port *port, int status);
+static char *recv_password_packet(Port *port);
 static int recv_and_check_password_packet(Port *port);
 
 char      *pg_krb_server_keyfile;
@@ -539,11 +540,9 @@ sendAuthRequest(Port *port, AuthRequest areq)
  */
 
 static int
-pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg, struct pam_response ** resp, void *appdata_ptr)
+pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg,
+                    struct pam_response ** resp, void *appdata_ptr)
 {
-   StringInfoData buf;
-   int32       len;
-
    if (num_msg != 1 || msg[0]->msg_style != PAM_PROMPT_ECHO_OFF)
    {
        switch (msg[0]->msg_style)
@@ -574,23 +573,20 @@ pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg, struct pam_re
     */
    if (strlen(appdata_ptr) == 0)
    {
-       sendAuthRequest(pam_port_cludge, AUTH_REQ_PASSWORD);
-       if (pq_eof() == EOF || pq_getint(&len, 4) == EOF)
-           return PAM_CONV_ERR;    /* client didn't want to send password */
+       char       *passwd;
 
-       initStringInfo(&buf);
-       if (pq_getstr_bounded(&buf, 1000) == EOF)
-           return PAM_CONV_ERR;    /* EOF while reading password */
+       sendAuthRequest(pam_port_cludge, AUTH_REQ_PASSWORD);
+       passwd = recv_password_packet(pam_port_cludge);
 
-       /* Do not echo failed password to logs, for security. */
-       elog(DEBUG5, "received PAM packet");
+       if (passwd == NULL)
+           return PAM_CONV_ERR;    /* client didn't want to send password */
 
-       if (strlen(buf.data) == 0)
+       if (strlen(passwd) == 0)
        {
            elog(LOG, "pam_passwd_conv_proc: no password");
            return PAM_CONV_ERR;
        }
-       appdata_ptr = buf.data;
+       appdata_ptr = passwd;
    }
 
    /*
@@ -601,8 +597,6 @@ pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg, struct pam_re
    if (!*resp)
    {
        elog(LOG, "pam_passwd_conv_proc: Out of memory!");
-       if (buf.data)
-           pfree(buf.data);
        return PAM_CONV_ERR;
    }
 
@@ -708,42 +702,87 @@ CheckPAMAuth(Port *port, char *user, char *password)
 
 
 /*
- * Called when we have received the password packet.
+ * Collect password response packet from frontend.
+ *
+ * Returns NULL if couldn't get password, else palloc'd string.
  */
-static int
-recv_and_check_password_packet(Port *port)
+static char *
+recv_password_packet(Port *port)
 {
    StringInfoData buf;
-   int32       len;
-   int         result;
 
-   if (pq_eof() == EOF || pq_getint(&len, 4) == EOF)
-       return STATUS_EOF;      /* client didn't want to send password */
+   if (PG_PROTOCOL_MAJOR(port->proto) >= 3)
+   {
+       /* Expect 'p' message type */
+       int     mtype;
+
+       mtype = pq_getbyte();
+       if (mtype != 'p')
+       {
+           /*
+            * If the client just disconnects without offering a password,
+            * don't make a log entry.  This is legal per protocol spec and
+            * in fact commonly done by psql, so complaining just clutters
+            * the log.
+            */
+           if (mtype != EOF)
+               elog(COMMERROR, "Expected password response, got %c", mtype);
+           return NULL;        /* EOF or bad message type */
+       }
+   }
+   else
+   {
+       /* For pre-3.0 clients, avoid log entry if they just disconnect */
+       if (pq_peekbyte() == EOF)
+           return NULL;        /* EOF */
+   }
 
    initStringInfo(&buf);
-   if (pq_getstr_bounded(&buf, 1000) == EOF) /* receive password */
+   if (pq_getmessage(&buf, 1000)) /* receive password */
    {
+       /* EOF - pq_getmessage already logged a suitable message */
        pfree(buf.data);
-       return STATUS_EOF;
+       return NULL;
    }
 
    /*
-    * We don't actually use the password packet length the frontend sent
-    * us; however, it's a reasonable sanity check to ensure that we
-    * actually read as much data as we expected to.
-    *
-    * The password packet size is the length of the buffer, plus the size
-    * field itself (4 bytes), plus a 1-byte terminator.
+    * Apply sanity check: password packet length should agree with length
+    * of contained string.  Note it is safe to use strlen here because
+    * StringInfo is guaranteed to have an appended '\0'.
     */
-   if (len != (buf.len + 4 + 1))
-       elog(LOG, "unexpected password packet size: read %d, expected %d",
-            buf.len + 4 + 1, len);
+   if (strlen(buf.data) + 1 != buf.len)
+       elog(COMMERROR, "bogus password packet size");
 
    /* Do not echo password to logs, for security. */
    elog(DEBUG5, "received password packet");
 
-   result = md5_crypt_verify(port, port->user_name, buf.data);
+   /*
+    * Return the received string.  Note we do not attempt to do any
+    * character-set conversion on it; since we don't yet know the
+    * client's encoding, there wouldn't be much point.
+    */
+   return buf.data;
+}
+
+
+/*
+ * Called when we have sent an authorization request for a password.
+ * Get the response and check it.
+ */
+static int
+recv_and_check_password_packet(Port *port)
+{
+   char       *passwd;
+   int         result;
+
+   passwd = recv_password_packet(port);
+
+   if (passwd == NULL)
+       return STATUS_EOF;      /* client wouldn't send password */
+
+   result = md5_crypt_verify(port, port->user_name, passwd);
+
+   pfree(passwd);
 
-   pfree(buf.data);
    return result;
 }
index 923f38a98c9b505140107d01af2e045401e2e9e4..e7674c7807cc07afc4d66398bda1aecd91d35dfd 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/libpq/be-secure.c,v 1.29 2003/04/10 23:03:08 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/libpq/be-secure.c,v 1.30 2003/04/19 00:02:29 tgl Exp $
  *
  *   Since the server static private key ($DataDir/server.key)
  *   will normally be stored unencrypted so that the database
 extern void ExitPostmaster(int);
 extern void postmaster_error(const char *fmt,...);
 
-int            secure_initialize(void);
-void       secure_destroy(void);
-int            secure_open_server(Port *);
-void       secure_close(Port *);
-ssize_t        secure_read(Port *, void *ptr, size_t len);
-ssize_t        secure_write(Port *, void *ptr, size_t len);
-
 #ifdef USE_SSL
 static DH  *load_dh_file(int keylength);
 static DH  *load_dh_buffer(const char *, size_t);
index cc06347e45d7599fa27b0475566d97971ec3d805..9a4f51b7786939355d45e123ecbe1df133df4a3c 100644 (file)
@@ -6,8 +6,8 @@
  * These routines handle the low-level details of communication between
  * frontend and backend.  They just shove data across the communication
  * channel, and are ignorant of the semantics of the data --- or would be,
- * except for major brain damage in the design of the COPY OUT protocol.
- * Unfortunately, COPY OUT is designed to commandeer the communication
+ * except for major brain damage in the design of the old COPY OUT protocol.
+ * Unfortunately, COPY OUT was designed to commandeer the communication
  * channel (it just transfers data without wrapping it into messages).
  * No other messages can be sent while COPY OUT is in progress; and if the
  * copy is aborted by an elog(ERROR), we need to close out the copy so that
@@ -29,7 +29,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pqcomm.c,v 1.149 2003/04/02 00:49:28 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/libpq/pqcomm.c,v 1.150 2003/04/19 00:02:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
  * low-level I/O:
  *     pq_getbytes     - get a known number of bytes from connection
  *     pq_getstring    - get a null terminated string from connection
+ *     pq_getmessage   - get a message with length word from connection
  *     pq_getbyte      - get next byte from connection
  *     pq_peekbyte     - peek at next byte from connection
  *     pq_putbytes     - send bytes to connection (not flushed until pq_flush)
  *     pq_flush        - flush pending output
  *
- * message-level I/O (and COPY OUT cruft):
+ * message-level I/O (and old-style-COPY-OUT cruft):
  *     pq_putmessage   - send a normal message (suppressed in COPY OUT mode)
  *     pq_startcopyout - inform libpq that a COPY OUT transfer is beginning
  *     pq_endcopyout   - end a COPY OUT transfer
@@ -85,9 +86,6 @@
 #include "miscadmin.h"
 #include "storage/ipc.h"
 
-extern void secure_close(Port *);
-extern ssize_t secure_read(Port *, void *, size_t);
-extern ssize_t secure_write(Port *, const void *, size_t);
 
 static void pq_close(void);
 
@@ -562,8 +560,10 @@ pq_recvbuf(void)
        }
        if (r == 0)
        {
-           /* as above, only write to postmaster log */
-           elog(COMMERROR, "pq_recvbuf: unexpected EOF on client connection");
+           /*
+            * EOF detected.  We used to write a log message here, but it's
+            * better to expect the ultimate caller to do that.
+            */
            return EOF;
        }
        /* r contains number of bytes read, so just incr length */
@@ -636,35 +636,29 @@ pq_getbytes(char *s, size_t len)
 /* --------------------------------
  *     pq_getstring    - get a null terminated string from connection
  *
- *     The return value is placed in an expansible StringInfo.
- *     Note that space allocation comes from the current memory context!
+ *     The return value is placed in an expansible StringInfo, which has
+ *     already been initialized by the caller.
  *
- *     If maxlen is not zero, it is an upper limit on the length of the
- *     string we are willing to accept.  We abort the connection (by
- *     returning EOF) if client tries to send more than that.  Note that
- *     since we test maxlen in the outer per-bufferload loop, the limit
- *     is fuzzy: we might accept up to PQ_BUFFER_SIZE more bytes than
- *     specified.  This is fine for the intended purpose, which is just
- *     to prevent DoS attacks from not-yet-authenticated clients.
- *
- *     NOTE: this routine does not do any character set conversion,
- *     even though it is presumably useful only for text, because
- *     no code in this module should depend on the encoding.
- *     See pq_getstr_bounded in pqformat.c for that.
+ *     This is used only for dealing with old-protocol clients.  The idea
+ *     is to produce a StringInfo that looks the same as we would get from
+ *     pq_getmessage() with a newer client; we will then process it with
+ *     pq_getmsgstring.  Therefore, no character set conversion is done here,
+ *     even though this is presumably useful only for text.
  *
  *     returns 0 if OK, EOF if trouble
  * --------------------------------
  */
 int
-pq_getstring(StringInfo s, int maxlen)
+pq_getstring(StringInfo s)
 {
    int         i;
 
    /* Reset string to empty */
    s->len = 0;
    s->data[0] = '\0';
+   s->cursor = 0;
 
-   /* Read until we get the terminating '\0' or overrun maxlen */
+   /* Read until we get the terminating '\0' */
    for (;;)
    {
        while (PqRecvPointer >= PqRecvLength)
@@ -677,9 +671,9 @@ pq_getstring(StringInfo s, int maxlen)
        {
            if (PqRecvBuffer[i] == '\0')
            {
-               /* does not copy the \0 */
+               /* include the '\0' in the copy */
                appendBinaryStringInfo(s, PqRecvBuffer + PqRecvPointer,
-                                      i - PqRecvPointer);
+                                      i - PqRecvPointer + 1);
                PqRecvPointer = i + 1;  /* advance past \0 */
                return 0;
            }
@@ -689,11 +683,70 @@ pq_getstring(StringInfo s, int maxlen)
        appendBinaryStringInfo(s, PqRecvBuffer + PqRecvPointer,
                               PqRecvLength - PqRecvPointer);
        PqRecvPointer = PqRecvLength;
+   }
+}
+
+
+/* --------------------------------
+ *     pq_getmessage   - get a message with length word from connection
+ *
+ *     The return value is placed in an expansible StringInfo, which has
+ *     already been initialized by the caller.
+ *     Only the message body is placed in the StringInfo; the length word
+ *     is removed.  Also, s->cursor is initialized to zero for convenience
+ *     in scanning the message contents.
+ *
+ *     If maxlen is not zero, it is an upper limit on the length of the
+ *     message we are willing to accept.  We abort the connection (by
+ *     returning EOF) if client tries to send more than that.
+ *
+ *     returns 0 if OK, EOF if trouble
+ * --------------------------------
+ */
+int
+pq_getmessage(StringInfo s, int maxlen)
+{
+   int32       len;
+
+   /* Reset message buffer to empty */
+   s->len = 0;
+   s->data[0] = '\0';
+   s->cursor = 0;
 
-       /* If maxlen is specified, check for overlength input. */
-       if (maxlen > 0 && s->len > maxlen)
+   /* Read message length word */
+   if (pq_getbytes((char *) &len, 4) == EOF)
+   {
+       elog(COMMERROR, "unexpected EOF within message length word");
+       return EOF;
+   }
+
+   len = ntohl(len);
+   len -= 4;                   /* discount length itself */
+
+   if (len < 0 ||
+       (maxlen > 0 && len > maxlen))
+   {
+       elog(COMMERROR, "invalid message length");
+       return EOF;
+   }
+
+   if (len > 0)
+   {
+       /* Allocate space for message */
+       enlargeStringInfo(s, len);
+
+       /* And grab the message */
+       if (pq_getbytes(s->data, len) == EOF)
+       {
+           elog(COMMERROR, "incomplete client message");
            return EOF;
+       }
+       s->len = len;
+       /* Place a trailing null per StringInfo convention */
+       s->data[len] = '\0';
    }
+
+   return 0;
 }
 
 
@@ -781,34 +834,10 @@ pq_flush(void)
 }
 
 
-/*
- * Return EOF if the connection has been broken, else 0.
- */
-int
-pq_eof(void)
-{
-   char        x;
-   int         res;
-
-   res = recv(MyProcPort->sock, &x, 1, MSG_PEEK);
-
-   if (res < 0)
-   {
-       /* can log to postmaster log only */
-       elog(COMMERROR, "pq_eof: recv() failed: %m");
-       return EOF;
-   }
-   if (res == 0)
-       return EOF;
-   else
-       return 0;
-}
-
-
 /* --------------------------------
  * Message-level I/O routines begin here.
  *
- * These routines understand about COPY OUT protocol.
+ * These routines understand about the old-style COPY OUT protocol.
  * --------------------------------
  */
 
@@ -840,7 +869,8 @@ pq_putmessage(char msgtype, const char *s, size_t len)
 }
 
 /* --------------------------------
- *     pq_startcopyout - inform libpq that a COPY OUT transfer is beginning
+ *     pq_startcopyout - inform libpq that an old-style COPY OUT transfer
+ *         is beginning
  * --------------------------------
  */
 void
index 50f17977d3eb9196c9bae2b754bc17385d4bbb93..80ca3190999c5f5eb30f1b4f810ae4dbb2dce9ec 100644 (file)
@@ -8,15 +8,17 @@
  * formatting/conversion routines that are needed to produce valid messages.
  * Note in particular the distinction between "raw data" and "text"; raw data
  * is message protocol characters and binary values that are not subject to
- * character set conversion, while text is converted by character encoding rules.
+ * character set conversion, while text is converted by character encoding
+ * rules.
  *
- * Incoming messages are read directly off the wire, as it were, but there
- * are still data-conversion tasks to be performed.
+ * Incoming messages are similarly read into a StringInfo buffer, via
+ * pq_getmessage, and then parsed and converted from that using the routines
+ * in this module.
  *
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pqformat.c,v 1.26 2003/04/02 00:49:28 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/libpq/pqformat.c,v 1.27 2003/04/19 00:02:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
  * Special-case message output:
  *     pq_puttextmessage - generate a character set-converted message in one step
  *
- * Message input:
- *     pq_getint           - get an integer from connection
- *     pq_getstr_bounded   - get a null terminated string from connection
- * pq_getstr_bounded performs character set conversion on the collected
- * string.  Use the raw pqcomm.c routines pq_getstring or pq_getbytes
- * to fetch data without conversion.
+ * Message parsing after input:
+ *     pq_getmsgbyte   - get a raw byte from a message buffer
+ *     pq_getmsgint    - get a binary integer from a message buffer
+ *     pq_getmsgbytes  - get raw data from a message buffer
+ *     pq_copymsgbytes - copy raw data from a message buffer
+ *     pq_getmsgstring - get a null-terminated text string (with conversion)
+ *     pq_getmsgend    - verify message fully consumed
  */
 
 #include "postgres.h"
@@ -206,16 +209,29 @@ pq_puttextmessage(char msgtype, const char *str)
    return pq_putmessage(msgtype, str, slen + 1);
 }
 
+
 /* --------------------------------
- *     pq_getint - get an integer from connection
- *
- *     returns 0 if OK, EOF if trouble
+ *     pq_getmsgbyte   - get a raw byte from a message buffer
  * --------------------------------
  */
 int
-pq_getint(int *result, int b)
+pq_getmsgbyte(StringInfo msg)
+{
+   if (msg->cursor >= msg->len)
+       elog(ERROR, "pq_getmsgbyte: no data left in message");
+   return (unsigned char) msg->data[msg->cursor++];
+}
+
+/* --------------------------------
+ *     pq_getmsgint    - get a binary integer from a message buffer
+ *
+ *     Values are treated as unsigned.
+ * --------------------------------
+ */
+unsigned int
+pq_getmsgint(StringInfo msg, int b)
 {
-   int         status;
+   unsigned int result;
    unsigned char n8;
    uint16      n16;
    uint32      n32;
@@ -223,59 +239,93 @@ pq_getint(int *result, int b)
    switch (b)
    {
        case 1:
-           status = pq_getbytes((char *) &n8, 1);
-           *result = (int) n8;
+           pq_copymsgbytes(msg, (char *) &n8, 1);
+           result = n8;
            break;
        case 2:
-           status = pq_getbytes((char *) &n16, 2);
-           *result = (int) (ntohs(n16));
+           pq_copymsgbytes(msg, (char *) &n16, 2);
+           result = ntohs(n16);
            break;
        case 4:
-           status = pq_getbytes((char *) &n32, 4);
-           *result = (int) (ntohl(n32));
+           pq_copymsgbytes(msg, (char *) &n32, 4);
+           result = ntohl(n32);
            break;
        default:
-
-           /*
-            * if we elog(ERROR) here, we will lose sync with the
-            * frontend, so just complain to postmaster log instead...
-            */
-           elog(COMMERROR, "pq_getint: unsupported size %d", b);
-           status = EOF;
-           *result = 0;
+           elog(ERROR, "pq_getmsgint: unsupported size %d", b);
+           result = 0;         /* keep compiler quiet */
            break;
    }
-   return status;
+   return result;
 }
 
 /* --------------------------------
- *     pq_getstr_bounded - get a null terminated string from connection
+ *     pq_getmsgbytes  - get raw data from a message buffer
  *
- *     The return value is placed in an expansible StringInfo.
- *     Note that space allocation comes from the current memory context!
+ *     Returns a pointer directly into the message buffer; note this
+ *     may not have any particular alignment.
+ * --------------------------------
+ */
+const char *
+pq_getmsgbytes(StringInfo msg, int datalen)
+{
+   const char *result;
+
+   if (datalen > (msg->len - msg->cursor))
+       elog(ERROR, "pq_getmsgbytes: insufficient data left in message");
+   result = &msg->data[msg->cursor];
+   msg->cursor += datalen;
+   return result;
+}
+
+/* --------------------------------
+ *     pq_copymsgbytes - copy raw data from a message buffer
  *
- *     The maxlen parameter is interpreted as per pq_getstring.
+ *     Same as above, except data is copied to caller's buffer.
+ * --------------------------------
+ */
+void
+pq_copymsgbytes(StringInfo msg, char *buf, int datalen)
+{
+   if (datalen > (msg->len - msg->cursor))
+       elog(ERROR, "pq_copymsgbytes: insufficient data left in message");
+   memcpy(buf, &msg->data[msg->cursor], datalen);
+   msg->cursor += datalen;
+}
+
+/* --------------------------------
+ *     pq_getmsgstring - get a null-terminated text string (with conversion)
  *
- *     returns 0 if OK, EOF if trouble
+ *     May return a pointer directly into the message buffer, or a pointer
+ *     to a palloc'd conversion result.
  * --------------------------------
  */
-int
-pq_getstr_bounded(StringInfo s, int maxlen)
+const char *
+pq_getmsgstring(StringInfo msg)
 {
-   int         result;
-   char       *p;
+   char   *str;
+   int     slen;
 
-   result = pq_getstring(s, maxlen);
+   str = &msg->data[msg->cursor];
+   /*
+    * It's safe to use strlen() here because a StringInfo is guaranteed
+    * to have a trailing null byte.  But check we found a null inside
+    * the message.
+    */
+   slen = strlen(str);
+   if (msg->cursor + slen >= msg->len)
+       elog(ERROR, "pq_getmsgstring: invalid string in message");
+   msg->cursor += slen + 1;
 
-   p = (char *) pg_client_to_server((unsigned char *) s->data, s->len);
-   if (p != s->data)           /* actual conversion has been done? */
-   {
-       /* reset s to empty, and append the new string p */
-       s->len = 0;
-       s->data[0] = '\0';
-       appendBinaryStringInfo(s, p, strlen(p));
-       pfree(p);
-   }
+   return (const char *) pg_client_to_server((unsigned char *) str, slen);
+}
 
-   return result;
+/* --------------------------------
+ *     pq_getmsgend    - verify message fully consumed
+ * --------------------------------
+ */
+void
+pq_getmsgend(StringInfo msg)
+{
+   if (msg->cursor != msg->len)
+       elog(ERROR, "pq_getmsgend: invalid message format");
 }
index 499c4f25ca25250c71e48d53a363ecdf190de20d..d6beb0fc1a6262a133518fd009bc2ef407656bf2 100644 (file)
@@ -37,7 +37,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.312 2003/04/18 01:03:42 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.313 2003/04/19 00:02:29 tgl Exp $
  *
  * NOTES
  *
@@ -1118,7 +1118,7 @@ ProcessStartupPacket(Port *port, bool SSLdone)
 
    if (pq_getbytes((char *) &len, 4) == EOF)
    {
-       elog(LOG, "incomplete startup packet");
+       elog(COMMERROR, "incomplete startup packet");
        return STATUS_ERROR;
    }
 
@@ -1142,7 +1142,7 @@ ProcessStartupPacket(Port *port, bool SSLdone)
 
    if (pq_getbytes(buf, len) == EOF)
    {
-       elog(LOG, "incomplete startup packet");
+       elog(COMMERROR, "incomplete startup packet");
        return STATUS_ERROR;
    }
 
@@ -1189,6 +1189,16 @@ ProcessStartupPacket(Port *port, bool SSLdone)
    /* Could add additional special packet types here */
 
 
+   /*
+    * XXX temporary for 3.0 protocol development: we are using the minor
+    * number as a test-version number.  Insist it match exactly so people
+    * don't get burnt by using yesterday's libpq with today's server.
+    * XXX this must go away before release!!!
+    */
+   if (PG_PROTOCOL_MAJOR(proto) == 3 &&
+       PG_PROTOCOL_MINOR(proto) != PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST))
+       elog(FATAL, "Your development libpq is out of sync with the server");
+
    /* Check we can handle the protocol the frontend is using. */
 
    if (PG_PROTOCOL_MAJOR(proto) < PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST) ||
@@ -1201,16 +1211,6 @@ ProcessStartupPacket(Port *port, bool SSLdone)
             PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST),
             PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST));
 
-   /*
-    * XXX temporary for 3.0 protocol development: we are using the minor
-    * number as a test-version number.  Insist it match exactly so people
-    * don't get burnt by using yesterday's libpq with today's server.
-    * XXX this must go away before release!!!
-    */
-   if (PG_PROTOCOL_MAJOR(proto) == 3 &&
-       PG_PROTOCOL_MINOR(proto) != PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST))
-       elog(FATAL, "Your development libpq is out of sync with the server");
-
    /*
     * Now fetch parameters out of startup packet and save them into the
     * Port structure.  All data structures attached to the Port struct
index ad9d2327717ed849f4ee1bdaa00c6602f0c513bb..07e4614e799be2c030a32320d49c07d2e19cc496 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.51 2003/03/27 16:51:29 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.52 2003/04/19 00:02:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -134,37 +134,6 @@ EndCommand(const char *commandTag, CommandDest dest)
    }
 }
 
-/*
- * These are necessary to sync communications between fe/be processes doing
- * COPY rel TO stdout
- *
- * or
- *
- * COPY rel FROM stdin
- *
- * NOTE: the message code letters are changed at protocol version 2.0
- * to eliminate possible confusion with data tuple messages.
- */
-void
-SendCopyBegin(void)
-{
-   if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
-       pq_putbytes("H", 1);    /* new way */
-   else
-       pq_putbytes("B", 1);    /* old way */
-}
-
-void
-ReceiveCopyBegin(void)
-{
-   if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
-       pq_putbytes("G", 1);    /* new way */
-   else
-       pq_putbytes("D", 1);    /* old way */
-   /* We *must* flush here to ensure FE knows it can send. */
-   pq_flush();
-}
-
 /* ----------------
  *     NullCommand - tell dest that an empty query string was recognized
  *
index 53b3a09ba2731efdfbe03e195a520434df67c277..eeddea6f6eb8cb82c6dc63f6e3754bbe20f4b983 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.57 2003/01/09 18:00:23 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.58 2003/04/19 00:02:29 tgl Exp $
  *
  * NOTES
  *   This cruft is the server side of PQfn.
  *   back to the frontend.  If the return returns by reference,
  *   send down only the data portion and set the return size appropriately.
  *
- *  OLD COMMENTS FOLLOW
- *
- *   The VAR_LENGTH_{ARGS,RESULT} stuff is limited to MAX_STRING_LENGTH
- *   (see src/backend/tmp/fastpath.h) for no obvious reason.  Since its
- *   primary use (for us) is for Inversion path names, it should probably
- *   be increased to 256 (MAXPATHLEN for Inversion, hidden in pg_type
- *   as well as utils/adt/filename.c).
- *
- *   Quoth PMA on 08/15/93:
- *
- *   This code has been almost completely rewritten with an eye to
- *   keeping it as compatible as possible with the previous (broken)
- *   implementation.
- *
- *   The previous implementation would assume (1) that any value of
- *   length <= 4 bytes was passed-by-value, and that any other value
- *   was a struct varlena (by-reference).  There was NO way to pass a
- *   fixed-length by-reference argument (like name) or a struct
- *   varlena of size <= 4 bytes.
- *
- *   The new implementation checks the catalogs to determine whether
- *   a value is by-value (type "0" is null-delimited character string,
- *   as it is for, e.g., the parser).  The only other item obtained
- *   from the catalogs is whether or not the value should be placed in
- *   a struct varlena or not.  Otherwise, the size given by the
- *   frontend is assumed to be correct (probably a bad decision, but
- *   we do strange things in the name of compatibility).
- *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
 #include "access/xact.h"
 #include "catalog/pg_proc.h"
 #include "libpq/libpq.h"
 #include "utils/tqual.h"
 
 
+/* ----------------
+ *     GetOldFunctionMessage
+ *
+ * In pre-3.0 protocol, there is no length word on the message, so we have
+ * to have code that understands the message layout to absorb the message
+ * into a buffer.  We want to do this before we start execution, so that
+ * we do not lose sync with the frontend if there's an error.
+ *
+ * The caller should already have initialized buf to empty.
+ * ----------------
+ */
+static int
+GetOldFunctionMessage(StringInfo buf)
+{
+   int32       ibuf;
+   int         nargs;
+
+   /* Dummy string argument */
+   if (pq_getstring(buf))
+       return EOF;
+   /* Function OID */
+   if (pq_getbytes((char *) &ibuf, 4))
+       return EOF;
+   appendBinaryStringInfo(buf, (char *) &ibuf, 4);
+   /* Number of arguments */
+   if (pq_getbytes((char *) &ibuf, 4))
+       return EOF;
+   appendBinaryStringInfo(buf, (char *) &ibuf, 4);
+   nargs = ntohl(ibuf);
+   /* For each argument ... */
+   while (nargs-- > 0)
+   {
+       int         argsize;
+
+       /* argsize */
+       if (pq_getbytes((char *) &ibuf, 4))
+           return EOF;
+       appendBinaryStringInfo(buf, (char *) &ibuf, 4);
+       argsize = ntohl(ibuf);
+       if (argsize < 0)
+       {
+           /* FATAL here since no hope of regaining message sync */
+           elog(FATAL, "HandleFunctionRequest: bogus argsize %d",
+                argsize);
+       }
+       /* and arg contents */
+       if (argsize > 0)
+       {
+           /* Allocate space for arg */
+           enlargeStringInfo(buf, argsize);
+           /* And grab it */
+           if (pq_getbytes(buf->data + buf->len, argsize))
+               return EOF;
+           buf->len += argsize;
+           /* Place a trailing null per StringInfo convention */
+           buf->data[buf->len] = '\0';
+       }
+   }
+   return 0;
+}
+
 /* ----------------
  *     SendFunctionResult
  *
@@ -205,6 +241,12 @@ fetch_fp_info(Oid func_id, struct fp_info * fip)
  * Server side of PQfn (fastpath function calls from the frontend).
  * This corresponds to the libpq protocol symbol "F".
  *
+ * INPUT:
+ *     In protocol version 3, postgres.c has already read the message body
+ *     and will pass it in msgBuf.
+ *     In old protocol, the passed msgBuf is empty and we must read the
+ *     message here.
+ * 
  * RETURNS:
  *     0 if successful completion, EOF if frontend connection lost.
  *
@@ -218,54 +260,44 @@ fetch_fp_info(Oid func_id, struct fp_info * fip)
  * control returns to PostgresMain.
  */
 int
-HandleFunctionRequest(void)
+HandleFunctionRequest(StringInfo msgBuf)
 {
    Oid         fid;
-   int         argsize;
    int         nargs;
-   int         tmp;
    AclResult   aclresult;
    FunctionCallInfoData fcinfo;
    Datum       retval;
    int         i;
-   char       *p;
    struct fp_info my_fp;
    struct fp_info *fip;
 
    /*
-    * XXX FIXME: This protocol is misdesigned.
-    *
-    * We really do not want to elog() before having swallowed all of the
-    * frontend's fastpath message; otherwise we will lose sync with the
-    * input datastream.  What should happen is we absorb all of the input
-    * message per protocol syntax, and *then* do error checking
-    * (including lookup of the given function ID) and elog if
-    * appropriate.  Unfortunately, because we cannot even read the
-    * message properly without knowing whether the data types are
-    * pass-by-ref or pass-by-value, it's not all that easy to do :-(. The
-    * protocol should require the client to supply what it thinks is the
-    * typbyval and typlen value for each arg, so that we can read the
-    * data without having to do any lookups.  Then after we've read the
-    * message, we should do the lookups, verify agreement of the actual
-    * function arg types with what we received, and finally call the
-    * function.
-    *
-    * As things stand, not only will we lose sync for an invalid message
-    * (such as requested function OID doesn't exist), but we may lose
-    * sync for a perfectly valid message if we are in transaction-aborted
-    * state! This can happen because our database lookup attempts may
-    * fail entirely in abort state.
-    *
-    * Unfortunately I see no way to fix this without breaking a lot of
-    * existing clients.  Maybe do it as part of next protocol version
-    * change.
+    * Read message contents if not already done.
     */
+   if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
+   {
+       if (GetOldFunctionMessage(msgBuf))
+       {
+           elog(COMMERROR, "unexpected EOF on client connection");
+           return EOF;
+       }
+   }
 
-   if (pq_getint(&tmp, 4))     /* function oid */
-       return EOF;
-   fid = (Oid) tmp;
-   if (pq_getint(&nargs, 4))   /* # of arguments */
-       return EOF;
+   /*
+    * Now that we've eaten the input message, check to see if we actually
+    * want to do the function call or not.  It's now safe to elog(); we won't
+    * lose sync with the frontend.
+    */
+   if (IsAbortedTransactionBlockState())
+       elog(ERROR, "current transaction is aborted, "
+            "queries ignored until end of transaction block");
+
+   /*
+    * Parse the buffer contents.
+    */
+   (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
@@ -274,11 +306,14 @@ HandleFunctionRequest(void)
    fip = &my_fp;
    fetch_fp_info(fid, fip);
 
+   /* Check permission to call function */
+   aclresult = pg_proc_aclcheck(fid, GetUserId(), ACL_EXECUTE);
+   if (aclresult != ACLCHECK_OK)
+       aclcheck_error(aclresult, get_func_name(fid));
+
    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;
@@ -286,21 +321,21 @@ HandleFunctionRequest(void)
 
    /*
     * Copy supplied arguments into arg vector.  Note there is no way for
-    * frontend to specify a NULL argument --- more misdesign.
+    * frontend to specify a NULL argument --- this protocol is misdesigned.
     */
    for (i = 0; i < nargs; ++i)
    {
-       if (pq_getint(&argsize, 4))
-           return EOF;
+       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] ? */
-           if (pq_getint(&tmp, argsize))
-               return EOF;
-           fcinfo.arg[i] = (Datum) tmp;
+           fcinfo.arg[i] = (Datum) pq_getmsgint(msgBuf, argsize);
        }
        else
        {                       /* by-reference ... */
@@ -309,13 +344,9 @@ HandleFunctionRequest(void)
                if (argsize < 0)
                    elog(ERROR, "HandleFunctionRequest: bogus argsize %d",
                         argsize);
-               /* I suspect this +1 isn't really needed - tgl 5/2000 */
-               p = palloc(argsize + VARHDRSZ + 1);     /* Added +1 to solve
-                                                        * memory leak - Peter
-                                                        * 98 Jan 6 */
+               p = palloc(argsize + VARHDRSZ);
                VARATT_SIZEP(p) = argsize + VARHDRSZ;
-               if (pq_getbytes(VARDATA(p), argsize))
-                   return EOF;
+               pq_copymsgbytes(msgBuf, VARDATA(p), argsize);
            }
            else
            {                   /* ... fixed */
@@ -323,29 +354,12 @@ HandleFunctionRequest(void)
                    elog(ERROR, "HandleFunctionRequest: bogus argsize %d, should be %d",
                         argsize, fip->arglen[i]);
                p = palloc(argsize + 1);        /* +1 in case argsize is 0 */
-               if (pq_getbytes(p, argsize))
-                   return EOF;
+               pq_copymsgbytes(msgBuf, p, argsize);
            }
            fcinfo.arg[i] = PointerGetDatum(p);
        }
    }
 
-   /*
-    * Now that we've eaten the input message, check to see if we actually
-    * want to do the function call or not.
-    *
-    * Currently, we report an error if in ABORT state, or return a dummy
-    * NULL response if fastpath support has been compiled out.
-    */
-   if (IsAbortedTransactionBlockState())
-       elog(ERROR, "current transaction is aborted, "
-            "queries ignored until end of transaction block");
-
-   /* Check permission to call function */
-   aclresult = pg_proc_aclcheck(fid, GetUserId(), ACL_EXECUTE);
-   if (aclresult != ACLCHECK_OK)
-       aclcheck_error(aclresult, get_func_name(fid));
-
    /*
     * Set up a query snapshot in case function needs one.  (It is not safe
     * to do this if we are in transaction-abort state, so we have to postpone
index 1048d2fa1c6816386299b49df1ddb69ba56593d6..fcc6591f7c017fa12aa4f9b394280618b6c26a5b 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.321 2003/04/17 22:26:01 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.322 2003/04/19 00:02:29 tgl Exp $
  *
  * NOTES
  *   this is the "main" module of the postgres backend and
@@ -133,7 +133,9 @@ static const char *CreateCommandTag(Node *parsetree);
 
 /* ----------------
  * InteractiveBackend() is called for user interactive connections
- * the string entered by the user is placed in its parameter inBuf.
+ *
+ * the string entered by the user is placed in its parameter inBuf,
+ * and we act like a Q message was received.
  *
  * EOF is returned if end-of-file input is seen; time to shut down.
  * ----------------
@@ -155,6 +157,7 @@ InteractiveBackend(StringInfo inBuf)
    /* Reset inBuf to empty */
    inBuf->len = 0;
    inBuf->data[0] = '\0';
+   inBuf->cursor = 0;
 
    for (;;)
    {
@@ -214,6 +217,9 @@ InteractiveBackend(StringInfo inBuf)
        break;
    }
 
+   /* Add '\0' to make it look the same as message case. */
+   appendStringInfoChar(inBuf, (char) '\0');
+
    /*
     * if the query echo flag was given, print the query..
     */
@@ -227,66 +233,79 @@ InteractiveBackend(StringInfo inBuf)
 /* ----------------
  * SocketBackend()     Is called for frontend-backend connections
  *
- * If the input is a query (case 'Q') then the string entered by
- * the user is placed in its parameter inBuf.
- *
- * If the input is a fastpath function call (case 'F') then
- * the function call is processed in HandleFunctionRequest()
- * (now called from PostgresMain()).
+ * Returns the message type code, and loads message body data into inBuf.
  *
  * EOF is returned if the connection is lost.
  * ----------------
  */
-
 static int
 SocketBackend(StringInfo inBuf)
 {
    int         qtype;
 
    /*
-    * get input from the frontend
+    * Get message type code from the frontend.
     */
    qtype = pq_getbyte();
 
+   if (qtype == EOF)           /* frontend disconnected */
+   {
+       elog(COMMERROR, "unexpected EOF on client connection");
+       return qtype;
+   }
+
+   /*
+    * Validate message type code before trying to read body; if we have
+    * lost sync, better to say "command unknown" than to run out of memory
+    * because we used garbage as a length word.
+    */
    switch (qtype)
    {
-       case EOF:
-           /* frontend disconnected */
+       case 'Q':               /* simple query */
+           if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
+           {
+               /* old style without length word; convert */
+               if (pq_getstring(inBuf))
+               {
+                   elog(COMMERROR, "unexpected EOF on client connection");
+                   return EOF;
+               }
+           }
            break;
 
-           /*
-            * 'Q': user entered a query
-            */
-       case 'Q':
-           if (pq_getstr(inBuf))
-               return EOF;
+       case 'F':               /* fastpath function call */
            break;
 
-           /*
-            * 'F':  calling user/system functions
-            */
-       case 'F':
-           if (pq_getstr(inBuf))
-               return EOF;     /* ignore "string" at start of F message */
+       case 'X':               /* terminate */
            break;
 
-           /*
-            * 'X':  frontend is exiting
-            */
-       case 'X':
+       case 'd':               /* copy data */
+       case 'c':               /* copy done */
+       case 'f':               /* copy fail */
+           /* Accept but ignore these messages, per protocol spec */
            break;
 
+       default:
            /*
-            * otherwise we got garbage from the frontend.
-            *
-            * XXX are we certain that we want to do an elog(FATAL) here?
-            * -cim 1/24/90
+            * Otherwise we got garbage from the frontend.  We treat this
+            * as fatal because we have probably lost message boundary sync,
+            * and there's no good way to recover.
             */
-       default:
            elog(FATAL, "Socket command type %c unknown", qtype);
            break;
    }
 
+   /*
+    * In protocol version 3, all frontend messages have a length word
+    * next after the type code; we can read the message contents
+    * independently of the type.
+    */
+   if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
+   {
+       if (pq_getmessage(inBuf, 0))
+           return EOF;         /* suitable message already logged */
+   }
+
    return qtype;
 }
 
@@ -1220,19 +1239,17 @@ int
 PostgresMain(int argc, char *argv[], const char *username)
 {
    int         flag;
-
    const char *DBName = NULL;
+   char       *potential_DataDir = NULL;
    bool        secure;
    int         errs = 0;
    int         debug_flag = 0;
    GucContext  ctx;
    GucSource   gucsource;
    char       *tmp;
-
    int         firstchar;
    StringInfo  parser_input;
-
-   char       *potential_DataDir = NULL;
+   bool        send_rfq;
 
    /*
     * Catch standard options before doing much else.  This even works on
@@ -1815,7 +1832,7 @@ PostgresMain(int argc, char *argv[], const char *username)
    if (!IsUnderPostmaster)
    {
        puts("\nPOSTGRES backend interactive interface ");
-       puts("$Revision: 1.321 $ $Date: 2003/04/17 22:26:01 $\n");
+       puts("$Revision: 1.322 $ $Date: 2003/04/19 00:02:29 $\n");
    }
 
    /*
@@ -1902,6 +1919,8 @@ PostgresMain(int argc, char *argv[], const char *username)
 
    PG_SETMASK(&UnBlockSig);
 
+   send_rfq = true;            /* initially, or after error */
+
    /*
     * Non-error queries loop here.
     */
@@ -1922,7 +1941,11 @@ PostgresMain(int argc, char *argv[], const char *username)
         *
         * Note: this includes fflush()'ing the last of the prior output.
         */
-       ReadyForQuery(whereToSendOutput);
+       if (send_rfq)
+       {
+           ReadyForQuery(whereToSendOutput);
+           send_rfq = false;
+       }
 
        /* ----------
         * Tell the statistics collector what we've collected
@@ -1986,20 +2009,36 @@ PostgresMain(int argc, char *argv[], const char *username)
         */
        switch (firstchar)
        {
+           case 'Q':           /* simple query */
                /*
-                * 'F' indicates a fastpath call.
-                */
-           case 'F':
-               /* ----------
-                * Tell the collector what we're doing
-                * ----------
+                * Process the query string.
+                *
+                * Note: transaction command start/end is now done within
+                * pg_exec_query_string(), not here.
                 */
+               if (log_statement_stats)
+                   ResetUsage();
+
+               pgstat_report_activity(parser_input->data);
+
+               pg_exec_query_string(parser_input,
+                                    whereToSendOutput,
+                                    QueryContext);
+
+               if (log_statement_stats)
+                   ShowUsage("QUERY STATISTICS");
+
+               send_rfq = true;
+               break;
+
+           case 'F':           /* fastpath function call */
+               /* Tell the collector what we're doing */
                pgstat_report_activity("<FASTPATH> function call");
 
                /* start an xact for this function invocation */
                start_xact_command();
 
-               if (HandleFunctionRequest() == EOF)
+               if (HandleFunctionRequest(parser_input) == EOF)
                {
                    /* lost frontend connection during F message input */
 
@@ -2015,29 +2054,8 @@ PostgresMain(int argc, char *argv[], const char *username)
 
                /* commit the function-invocation transaction */
                finish_xact_command(false);
-               break;
-
-               /*
-                * 'Q' indicates a user query
-                */
-           case 'Q':
-               /*
-                * otherwise, process the input string.
-                *
-                * Note: transaction command start/end is now done within
-                * pg_exec_query_string(), not here.
-                */
-               if (log_statement_stats)
-                   ResetUsage();
 
-               pgstat_report_activity(parser_input->data);
-
-               pg_exec_query_string(parser_input,
-                                    whereToSendOutput,
-                                    QueryContext);
-
-               if (log_statement_stats)
-                   ShowUsage("QUERY STATISTICS");
+               send_rfq = true;
                break;
 
                /*
@@ -2064,8 +2082,18 @@ PostgresMain(int argc, char *argv[], const char *username)
                 */
                proc_exit(0);
 
+           case 'd':               /* copy data */
+           case 'c':               /* copy done */
+           case 'f':               /* copy fail */
+               /*
+                * Accept but ignore these messages, per protocol spec;
+                * we probably got here because a COPY failed, and the
+                * frontend is still sending data.
+                */
+               break;
+
            default:
-               elog(ERROR, "unknown frontend message was received");
+               elog(FATAL, "Socket command type %c unknown", firstchar);
        }
 
 #ifdef MEMORY_CONTEXT_CHECKING
index 051ce239a7a76ab1e24fff9cb674842940ed4463..c49a73c0eb18cb7a19683cd7b764ee080a136baf 100644 (file)
@@ -10,7 +10,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: stringinfo.h,v 1.24 2002/06/20 20:29:49 momjian Exp $
+ * $Id: stringinfo.h,v 1.25 2003/04/19 00:02:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -27,6 +27,9 @@
  *             string size (including the terminating '\0' char) that we can
  *             currently store in 'data' without having to reallocate
  *             more space.  We must always have maxlen > len.
+ *     cursor  is initialized to zero by makeStringInfo or initStringInfo,
+ *             but is not otherwise touched by the stringinfo.c routines.
+ *             Some routines use it to scan through a StringInfo.
  *-------------------------
  */
 typedef struct StringInfoData
@@ -34,6 +37,7 @@ typedef struct StringInfoData
    char       *data;
    int         len;
    int         maxlen;
+   int         cursor;
 } StringInfoData;
 
 typedef StringInfoData *StringInfo;
@@ -111,4 +115,10 @@ extern void appendStringInfoChar(StringInfo str, char ch);
 extern void appendBinaryStringInfo(StringInfo str,
                       const char *data, int datalen);
 
+/*------------------------
+ * enlargeStringInfo
+ * Make sure a StringInfo's buffer can hold at least 'needed' more bytes.
+ */
+extern void enlargeStringInfo(StringInfo str, int needed);
+
 #endif   /* STRINGINFO_H */
index 04248b5c95c95b4b61c9c6292191d7dad9708a7d..cbe0b646e74e3092fcfa6836c9e27f76d9ae262d 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: libpq.h,v 1.56 2003/01/25 05:19:47 tgl Exp $
+ * $Id: libpq.h,v 1.57 2003/04/19 00:02:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -52,14 +52,24 @@ extern void StreamClose(int sock);
 extern void TouchSocketFile(void);
 extern void pq_init(void);
 extern int pq_getbytes(char *s, size_t len);
-extern int pq_getstring(StringInfo s, int maxlen);
+extern int pq_getstring(StringInfo s);
+extern int pq_getmessage(StringInfo s, int maxlen);
 extern int pq_getbyte(void);
 extern int pq_peekbyte(void);
 extern int pq_putbytes(const char *s, size_t len);
 extern int pq_flush(void);
-extern int pq_eof(void);
 extern int pq_putmessage(char msgtype, const char *s, size_t len);
 extern void pq_startcopyout(void);
 extern void pq_endcopyout(bool errorAbort);
 
+/*
+ * prototypes for functions in be-secure.c
+ */
+extern int     secure_initialize(void);
+extern void        secure_destroy(void);
+extern int     secure_open_server(Port *port);
+extern void        secure_close(Port *port);
+extern ssize_t secure_read(Port *port, void *ptr, size_t len);
+extern ssize_t secure_write(Port *port, void *ptr, size_t len);
+
 #endif   /* LIBPQ_H */
index fabfb0cb2534d311788df194dab7a0f998d72dd9..61aa695e27217356e9fdc99a1647c1b3f216cda5 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.76 2003/04/17 22:26:01 tgl Exp $
+ * $Id: pqcomm.h,v 1.77 2003/04/19 00:02:29 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,100) /* XXX temporary value */
+#define PG_PROTOCOL_LATEST     PG_PROTOCOL(3,101) /* XXX temporary value */
 
 typedef uint32 ProtocolVersion; /* FE/BE protocol version number */
 
index 829727c38f0dd756b966ceaad131881923845fdc..cb80ec2c2014dbee579aa3c4e223e0831eea8077 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.13 2002/09/04 23:31:35 tgl Exp $
+ * $Id: pqformat.h,v 1.14 2003/04/19 00:02:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -26,9 +26,11 @@ extern void pq_endmessage(StringInfo buf);
 
 extern int pq_puttextmessage(char msgtype, const char *str);
 
-extern int pq_getint(int *result, int b);
-extern int pq_getstr_bounded(StringInfo s, int maxlen);
-
-#define pq_getstr(s)   pq_getstr_bounded(s, 0)
+extern int pq_getmsgbyte(StringInfo msg);
+extern unsigned int pq_getmsgint(StringInfo msg, int b);
+extern const char *pq_getmsgbytes(StringInfo msg, int datalen);
+extern void pq_copymsgbytes(StringInfo msg, char *buf, int datalen);
+extern const char *pq_getmsgstring(StringInfo msg);
+extern void pq_getmsgend(StringInfo msg);
 
 #endif   /* PQFORMAT_H */
index bbf86ef4ca60b03a39af8fd1f0761da1e0309c9f..39063af6e16e1d251b76e41e7643f89e17d3e03b 100644 (file)
@@ -44,7 +44,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.33 2003/03/27 16:51:29 momjian Exp $
+ * $Id: dest.h,v 1.34 2003/04/19 00:02:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -102,8 +102,6 @@ extern void EndCommand(const char *commandTag, CommandDest dest);
 
 /* Additional functions that go with destination management, more or less. */
 
-extern void SendCopyBegin(void);
-extern void ReceiveCopyBegin(void);
 extern void NullCommand(CommandDest dest);
 extern void ReadyForQuery(CommandDest dest);
 
index e9b961d8f0533f9eda938bbf47dd664ab69dda66..9e0f5e6bbb9a02c1044f6a9d938bd7ad925af465 100644 (file)
@@ -6,13 +6,15 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: fastpath.h,v 1.13 2002/06/20 20:29:52 momjian Exp $
+ * $Id: fastpath.h,v 1.14 2003/04/19 00:02:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef FASTPATH_H
 #define FASTPATH_H
 
-extern int HandleFunctionRequest(void);
+#include "lib/stringinfo.h"
+
+extern int HandleFunctionRequest(StringInfo msgBuf);
 
 #endif   /* FASTPATH_H */
index fca2d2e3035d8d665348cce360d37e0f8f9eb8de..10e2ee15f137b5fbdaf69983cb55e39dd6ec56f0 100644 (file)
@@ -10,7 +10,7 @@
  * exceed INITIAL_EXPBUFFER_SIZE (currently 256 bytes).
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.75 2003/04/17 22:26:01 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.76 2003/04/19 00:02:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -559,7 +559,7 @@ pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
        default:
            return STATUS_ERROR;
    }
-   ret = pqPacketSend(conn, 0, crypt_pwd, strlen(crypt_pwd) + 1);
+   ret = pqPacketSend(conn, 'p', crypt_pwd, strlen(crypt_pwd) + 1);
    if (areq == AUTH_REQ_MD5)
        free(crypt_pwd);
    return ret;
index 9f5c8714a68caa9b90bb9f2181b31f1377da5a12..a322d8a73d132da84a37f51b043861ade6f7a9d5 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.232 2003/04/17 22:26:02 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.233 2003/04/19 00:02:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1820,11 +1820,11 @@ makeEmptyPGconn(void)
 #endif
 
    /*
-    * The output buffer size is set to 8K, which is the usual size of
-    * pipe buffers on Unix systems.  That way, when we are sending a
+    * We try to send at least 8K at a time, which is the usual size
+    * of pipe buffers on Unix systems.  That way, when we are sending a
     * large amount of data, we avoid incurring extra kernel context swaps
-    * for partial bufferloads.  Note that we currently don't ever enlarge
-    * the output buffer.
+    * for partial bufferloads.  The output buffer is initially made 16K
+    * in size, and we try to dump it after accumulating 8K.
     *
     * With the same goal of minimizing context swaps, the input buffer will
     * be enlarged anytime it has less than 8K free, so we initially
@@ -1832,7 +1832,7 @@ makeEmptyPGconn(void)
     */
    conn->inBufSize = 16 * 1024;
    conn->inBuffer = (char *) malloc(conn->inBufSize);
-   conn->outBufSize = 8 * 1024;
+   conn->outBufSize = 16 * 1024;
    conn->outBuffer = (char *) malloc(conn->outBufSize);
    conn->nonblocking = FALSE;
    initPQExpBuffer(&conn->errorMessage);
@@ -1918,11 +1918,10 @@ closePGconn(PGconn *conn)
    {
        /*
         * Try to send "close connection" message to backend. Ignore any
-        * error. Note: this routine used to go to substantial lengths to
-        * avoid getting SIGPIPE'd if the connection were already closed.
-        * Now we rely on pqFlush to avoid the signal.
+        * error.
         */
-       pqPutc('X', conn);
+       pqPutMsgStart('X', conn);
+       pqPutMsgEnd(conn);
        pqFlush(conn);
    }
 
@@ -2152,7 +2151,7 @@ cancel_errReturn:
 
 
 /*
- * pqPacketSend() -- send a single-packet message.
+ * pqPacketSend() -- convenience routine to send a message to server.
  *
  * pack_type: the single-byte message type code.  (Pass zero for startup
  * packets, which have no message type code.)
@@ -2167,19 +2166,18 @@ int
 pqPacketSend(PGconn *conn, char pack_type,
             const void *buf, size_t buf_len)
 {
-   /* Send the message type. */
-   if (pack_type != 0)
-       if (pqPutc(pack_type, conn))
-           return STATUS_ERROR;
-           
-   /* Send the (self-inclusive) message length word. */
-   if (pqPutInt(buf_len + 4, 4, conn))
+   /* Start the message. */
+   if (pqPutMsgStart(pack_type, conn))
        return STATUS_ERROR;
 
    /* Send the message body. */
    if (pqPutnchar(buf, buf_len, conn))
        return STATUS_ERROR;
 
+   /* Finish the message. */
+   if (pqPutMsgEnd(conn))
+       return STATUS_ERROR;
+
    /* Flush to ensure backend gets it. */
    if (pqFlush(conn))
        return STATUS_ERROR;
@@ -2624,7 +2622,7 @@ build_startup_packet(const PGconn *conn, char *packet)
    packet_len += sizeof(ProtocolVersion);
 
    /* Add user name, database name, options */
-   if (conn->pguser)
+   if (conn->pguser && conn->pguser[0])
    {
        if (packet)
            strcpy(packet + packet_len, "user");
@@ -2633,7 +2631,7 @@ build_startup_packet(const PGconn *conn, char *packet)
            strcpy(packet + packet_len, conn->pguser);
        packet_len += strlen(conn->pguser) + 1;
    }
-   if (conn->dbName)
+   if (conn->dbName && conn->dbName[0])
    {
        if (packet)
            strcpy(packet + packet_len, "database");
@@ -2642,7 +2640,7 @@ build_startup_packet(const PGconn *conn, char *packet)
            strcpy(packet + packet_len, conn->dbName);
        packet_len += strlen(conn->dbName) + 1;
    }
-   if (conn->pgoptions)
+   if (conn->pgoptions && conn->pgoptions[0])
    {
        if (packet)
            strcpy(packet + packet_len, "options");
index 9e86b3aa6726c0ae1e8bbe2e36bd88d849ae995a..487acff83dfcdca4be8a9a0c4eef8f92ff41f038 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.128 2003/03/25 02:44:36 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.129 2003/04/19 00:02:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -734,7 +734,6 @@ addTuple(PGresult *res, PGresAttValue * tup)
  * Returns: 1 if successfully submitted
  *         0 if error (conn->errorMessage is set)
  */
-
 int
 PQsendQuery(PGconn *conn, const char *query)
 {
@@ -770,51 +769,24 @@ PQsendQuery(PGconn *conn, const char *query)
    conn->result = NULL;
    conn->curTuple = NULL;
 
-   /* send the query to the backend; */
+   /* construct the outgoing Query message */
+   if (pqPutMsgStart('Q', conn) < 0 ||
+       pqPuts(query, conn) < 0 ||
+       pqPutMsgEnd(conn) < 0)
+   {
+       handleSendFailure(conn);
+       return 0;
+   }
 
    /*
-    * in order to guarantee that we don't send a partial query where we
-    * would become out of sync with the backend and/or block during a
-    * non-blocking connection we must first flush the send buffer before
-    * sending more data
-    *
-    * an alternative is to implement 'queue reservations' where we are able
-    * to roll up a transaction (the 'Q' along with our query) and make
-    * sure we have enough space for it all in the send buffer.
+    * Give the data a push.  In nonblock mode, don't complain if we're
+    * unable to send it all; PQconsumeInput() will do any additional flushing
+    * needed.
     */
-   if (pqIsnonblocking(conn))
+   if (pqFlush(conn) < 0)
    {
-       /*
-        * the buffer must have emptied completely before we allow a new
-        * query to be buffered
-        */
-       if (pqFlush(conn))
-           return 0;
-       /* 'Q' == queries */
-       /* XXX: if we fail here we really ought to not block */
-       if (pqPutc('Q', conn) != 0 || pqPuts(query, conn) != 0)
-       {
-           handleSendFailure(conn);
-           return 0;
-       }
-
-       /*
-        * give the data a push, ignore the return value as ConsumeInput()
-        * will do any additional flushing if needed
-        */
-       pqFlush(conn);
-   }
-   else
-   {
-       /*
-        * the frontend-backend protocol uses 'Q' to designate queries
-        */
-       if (pqPutc('Q', conn) != 0 || pqPuts(query, conn) != 0 ||
-           pqFlush(conn) != 0)
-       {
-           handleSendFailure(conn);
-           return 0;
-       }
+       handleSendFailure(conn);
+       return 0;
    }
 
    /* OK, it's launched! */
@@ -830,7 +802,6 @@ PQsendQuery(PGconn *conn, const char *query)
  *
  * NOTE: this routine should only be called in PGASYNC_IDLE state.
  */
-
 static void
 handleSendFailure(PGconn *conn)
 {
@@ -854,13 +825,23 @@ handleSendFailure(PGconn *conn)
  * 0 return: some kind of trouble
  * 1 return: no problem
  */
-
 int
 PQconsumeInput(PGconn *conn)
 {
    if (!conn)
        return 0;
 
+   /*
+    * for non-blocking connections try to flush the send-queue,
+    * otherwise we may never get a response for something that may
+    * not have already been sent because it's in our write buffer!
+    */
+   if (pqIsnonblocking(conn))
+   {
+       if (pqFlush(conn) < 0)
+           return 0;
+   }
+
    /*
     * Load more data, if available. We do this no matter what state we
     * are in, since we are probably getting called because the
@@ -868,16 +849,8 @@ PQconsumeInput(PGconn *conn)
     * we will NOT block waiting for more input.
     */
    if (pqReadData(conn) < 0)
-   {
-       /*
-        * for non-blocking connections try to flush the send-queue
-        * otherwise we may never get a responce for something that may
-        * not have already been sent because it's in our write buffer!
-        */
-       if (pqIsnonblocking(conn))
-           (void) pqFlush(conn);
        return 0;
-   }
+
    /* Parsing of the data waits till later. */
    return 1;
 }
@@ -1733,14 +1706,13 @@ PQgetlineAsync(PGconn *conn, char *buffer, int bufsize)
  * PQputline -- sends a string to the backend.
  * Returns 0 if OK, EOF if not.
  *
- * Chiefly here so that applications can use "COPY <rel> from stdin".
+ * This exists to support "COPY <rel> from stdin".  The backend will ignore
+ * the string if not doing COPY.
  */
 int
 PQputline(PGconn *conn, const char *s)
 {
-   if (!conn || conn->sock < 0)
-       return EOF;
-   return pqPutnchar(s, strlen(s), conn);
+   return PQputnbytes(conn, s, strlen(s));
 }
 
 /*
@@ -1752,7 +1724,14 @@ PQputnbytes(PGconn *conn, const char *buffer, int nbytes)
 {
    if (!conn || conn->sock < 0)
        return EOF;
-   return pqPutnchar(buffer, nbytes, conn);
+   if (nbytes > 0)
+   {
+       if (pqPutMsgStart('d', conn) < 0 ||
+           pqPutnchar(buffer, nbytes, conn) < 0 ||
+           pqPutMsgEnd(conn) < 0)
+           return EOF;
+   }
+   return 0;
 }
 
 /*
@@ -1780,6 +1759,14 @@ PQendcopy(PGconn *conn)
        return 1;
    }
 
+   /* Send the CopyDone message if needed */
+   if (conn->asyncStatus == PGASYNC_COPY_IN)
+   {
+       if (pqPutMsgStart('c', conn) < 0 ||
+           pqPutMsgEnd(conn) < 0)
+           return 1;
+   }
+
    /*
     * make sure no data is waiting to be sent, abort if we are
     * non-blocking and the flush fails
@@ -1884,9 +1871,10 @@ PQfn(PGconn *conn,
        return NULL;
    }
 
-   if (pqPuts("F ", conn) != 0 ||      /* function */
-       pqPutInt(fnid, 4, conn) != 0 || /* function id */
-       pqPutInt(nargs, 4, conn) != 0)  /* # of args */
+   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 */
    {
        handleSendFailure(conn);
        return NULL;
@@ -1917,7 +1905,9 @@ PQfn(PGconn *conn,
            }
        }
    }
-   if (pqFlush(conn))
+
+   if (pqPutMsgEnd(conn) < 0 ||
+       pqFlush(conn))
    {
        handleSendFailure(conn);
        return NULL;
@@ -2409,7 +2399,6 @@ PQgetisnull(const PGresult *res, int tup_num, int field_num)
 int
 PQsetnonblocking(PGconn *conn, int arg)
 {
-
    arg = (arg == TRUE) ? 1 : 0;
    /* early out if the socket is already in the state requested */
    if (arg == conn->nonblocking)
@@ -2437,7 +2426,6 @@ PQsetnonblocking(PGconn *conn, int arg)
 int
 PQisnonblocking(const PGconn *conn)
 {
-
    return (pqIsnonblocking(conn));
 }
 
@@ -2445,18 +2433,9 @@ PQisnonblocking(const PGconn *conn)
 int
 PQflush(PGconn *conn)
 {
-
    return (pqFlush(conn));
 }
 
-/* try to force data out, really only useful for non-blocking users.
- * This implementation actually works for non-blocking connections */
-int
-PQsendSome(PGconn *conn)
-{
-   return pqSendSome(conn);
-}
-
 /*
  * PQfreeNotify - free's the memory associated with a PGnotify
  *
@@ -2473,5 +2452,3 @@ PQfreeNotify(PGnotify *notify)
 {
    PQfreemem(notify);
 }
-
-
index 0f971343ccce35dc34f81d4a54e564f255c6e11e..dfc46fdf5987ce9f031cae23bffc7cf62b143b69 100644 (file)
  * will cause repeat printouts.
  *
  * We must speak the same transmitted data representations as the backend
- * routines.  Note that this module supports *only* network byte order
- * for transmitted ints, whereas the backend modules (as of this writing)
- * still handle either network or little-endian byte order.
+ * routines.
+ *
  *
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.88 2003/04/02 00:49:28 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.89 2003/04/19 00:02:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #define DONOTICE(conn,message) \
    ((*(conn)->noticeHook) ((conn)->noticeArg, (message)))
 
+static int pqPutMsgBytes(const void *buf, size_t len, PGconn *conn);
+static int pqSendSome(PGconn *conn, int len);
 static int pqSocketCheck(PGconn *conn, int forRead, int forWrite,
                          time_t end_time);
 static int pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time);
-static int pqPutBytes(const char *s, size_t nbytes, PGconn *conn);
 
 
 /*
- * pqGetc:
- * get a character from the connection
+ * pqGetc: get 1 character from the connection
  *
  * All these routines return 0 on success, EOF on error.
  * Note that for the Get routines, EOF only means there is not enough
@@ -93,12 +91,12 @@ pqGetc(char *result, PGconn *conn)
 
 
 /*
- * write 1 char to the connection
+ * pqPutc: write 1 char to the current message
  */
 int
 pqPutc(char c, PGconn *conn)
 {
-   if (pqPutBytes(&c, 1, conn) == EOF)
+   if (pqPutMsgBytes(&c, 1, conn))
        return EOF;
 
    if (conn->Pfdebug)
@@ -108,93 +106,6 @@ pqPutc(char c, PGconn *conn)
 }
 
 
-/*
- * pqPutBytes: local routine to write N bytes to the connection,
- * with buffering
- */
-static int
-pqPutBytes(const char *s, size_t nbytes, PGconn *conn)
-{
-   /*
-    * Strategy to handle blocking and non-blocking connections: Fill the
-    * output buffer and flush it repeatedly until either all data has
-    * been sent or is at least queued in the buffer.
-    *
-    * For non-blocking connections, grow the buffer if not all data fits
-    * into it and the buffer can't be sent because the socket would
-    * block.
-    */
-
-   while (nbytes)
-   {
-       size_t      avail,
-                   remaining;
-
-       /* fill the output buffer */
-       avail = Max(conn->outBufSize - conn->outCount, 0);
-       remaining = Min(avail, nbytes);
-       memcpy(conn->outBuffer + conn->outCount, s, remaining);
-       conn->outCount += remaining;
-       s += remaining;
-       nbytes -= remaining;
-
-       /*
-        * if the data didn't fit completely into the buffer, try to flush
-        * the buffer
-        */
-       if (nbytes)
-       {
-           int         send_result = pqSendSome(conn);
-
-           /* if there were errors, report them */
-           if (send_result < 0)
-               return EOF;
-
-           /*
-            * if not all data could be sent, increase the output buffer,
-            * put the rest of s into it and return successfully. This
-            * case will only happen in a non-blocking connection
-            */
-           if (send_result > 0)
-           {
-               /*
-                * try to grow the buffer. FIXME: The new size could be
-                * chosen more intelligently.
-                */
-               size_t      buflen = (size_t) conn->outCount + nbytes;
-
-               if (buflen > (size_t) conn->outBufSize)
-               {
-                   char       *newbuf = realloc(conn->outBuffer, buflen);
-
-                   if (!newbuf)
-                   {
-                       /* realloc failed. Probably out of memory */
-                       printfPQExpBuffer(&conn->errorMessage,
-                          "cannot allocate memory for output buffer\n");
-                       return EOF;
-                   }
-                   conn->outBuffer = newbuf;
-                   conn->outBufSize = buflen;
-               }
-               /* put the data into it */
-               memcpy(conn->outBuffer + conn->outCount, s, nbytes);
-               conn->outCount += nbytes;
-
-               /* report success. */
-               return 0;
-           }
-       }
-
-       /*
-        * pqSendSome was able to send all data. Continue with the next
-        * chunk of s.
-        */
-   }                           /* while */
-
-   return 0;
-}
-
 /*
  * pqGets:
  * get a null-terminated string from the connection,
@@ -232,14 +143,17 @@ pqGets(PQExpBuffer buf, PGconn *conn)
 }
 
 
+/*
+ * pqPuts: write a null-terminated string to the current message
+ */
 int
 pqPuts(const char *s, PGconn *conn)
 {
-   if (pqPutBytes(s, strlen(s) + 1, conn))
+   if (pqPutMsgBytes(s, strlen(s) + 1, conn))
        return EOF;
 
    if (conn->Pfdebug)
-       fprintf(conn->Pfdebug, "To backend> %s\n", s);
+       fprintf(conn->Pfdebug, "To backend> '%s'\n", s);
 
    return 0;
 }
@@ -267,12 +181,12 @@ pqGetnchar(char *s, size_t len, PGconn *conn)
 
 /*
  * pqPutnchar:
- * send a string of exactly len bytes, no null termination needed
+ * write exactly len bytes to the current message
  */
 int
 pqPutnchar(const char *s, size_t len, PGconn *conn)
 {
-   if (pqPutBytes(s, len, conn))
+   if (pqPutMsgBytes(s, len, conn))
        return EOF;
 
    if (conn->Pfdebug)
@@ -325,7 +239,7 @@ pqGetInt(int *result, size_t bytes, PGconn *conn)
 
 /*
  * pgPutInt
- * send an integer of 2 or 4 bytes, converting from host byte order
+ * write an integer of 2 or 4 bytes, converting from host byte order
  * to network byte order.
  */
 int
@@ -339,12 +253,12 @@ pqPutInt(int value, size_t bytes, PGconn *conn)
    {
        case 2:
            tmp2 = htons((uint16) value);
-           if (pqPutBytes((const char *) &tmp2, 2, conn))
+           if (pqPutMsgBytes((const char *) &tmp2, 2, conn))
                return EOF;
            break;
        case 4:
            tmp4 = htonl((uint32) value);
-           if (pqPutBytes((const char *) &tmp4, 4, conn))
+           if (pqPutMsgBytes((const char *) &tmp4, 4, conn))
                return EOF;
            break;
        default:
@@ -362,24 +276,162 @@ pqPutInt(int value, size_t bytes, PGconn *conn)
 }
 
 /*
- * pqReadReady: is select() saying the file is ready to read?
- * JAB: -or- if SSL is enabled and used, is it buffering bytes?
- * Returns -1 on failure, 0 if not ready, 1 if ready.
+ * Make sure conn's output buffer can hold bytes_needed bytes (caller must
+ * include existing outCount into the value!)
+ *
+ * Returns 0 on success, EOF on error
+ */
+static int
+checkOutBufferSpace(int bytes_needed, PGconn *conn)
+{
+   int         newsize = conn->outBufSize;
+   char       *newbuf;
+
+   if (bytes_needed <= newsize)
+       return 0;
+   /*
+    * If we need to enlarge the buffer, we first try to double it in size;
+    * if that doesn't work, enlarge in multiples of 8K.  This avoids
+    * thrashing the malloc pool by repeated small enlargements.
+    *
+    * Note: tests for newsize > 0 are to catch integer overflow.
+    */
+   do {
+       newsize *= 2;
+   } while (bytes_needed > newsize && newsize > 0);
+
+   if (bytes_needed <= newsize)
+   {
+       newbuf = realloc(conn->outBuffer, newsize);
+       if (newbuf)
+       {
+           /* realloc succeeded */
+           conn->outBuffer = newbuf;
+           conn->outBufSize = newsize;
+           return 0;
+       }
+   }
+
+   newsize = conn->outBufSize;
+   do {
+       newsize += 8192;
+   } while (bytes_needed > newsize && newsize > 0);
+
+   if (bytes_needed <= newsize)
+   {
+       newbuf = realloc(conn->outBuffer, newsize);
+       if (newbuf)
+       {
+           /* realloc succeeded */
+           conn->outBuffer = newbuf;
+           conn->outBufSize = newsize;
+           return 0;
+       }
+   }
+
+   /* realloc failed. Probably out of memory */
+   printfPQExpBuffer(&conn->errorMessage,
+                     "cannot allocate memory for output buffer\n");
+   return EOF;
+}
+
+/*
+ * pqPutMsgStart: begin construction of a message to the server
+ *
+ * msg_type is the message type byte, or 0 for a message without type byte
+ * (only startup messages have no type byte)
+ *
+ * Returns 0 on success, EOF on error
+ *
+ * The idea here is that we construct the message in conn->outBuffer,
+ * beginning just past any data already in outBuffer (ie, at
+ * outBuffer+outCount).  We enlarge the buffer as needed to hold the message.
+ * When the message is complete, we fill in the length word and then advance
+ * outCount past the message, making it eligible to send.  The state
+ * variable conn->outMsgStart points to the incomplete message's length word
+ * (it is either outCount or outCount+1 depending on whether there is a
+ * type byte).  The state variable conn->outMsgEnd is the end of the data
+ * collected so far.
  */
 int
-pqReadReady(PGconn *conn)
+pqPutMsgStart(char msg_type, PGconn *conn)
 {
-   return pqSocketCheck(conn, 1, 0, (time_t) 0);
+   int         lenPos;
+
+   /* where the message length word will go */
+   if (msg_type)
+       lenPos = conn->outCount + 1;
+   else
+       lenPos = conn->outCount;
+   /* make sure there is room for it */
+   if (checkOutBufferSpace(lenPos + 4, conn))
+       return EOF;
+   /* okay, save the message type byte if any */
+   if (msg_type)
+       conn->outBuffer[conn->outCount] = msg_type;
+   /* set up the message pointers */
+   conn->outMsgStart = lenPos;
+   conn->outMsgEnd = lenPos + 4;
+   /* length word will be filled in by pqPutMsgEnd */
+
+   if (conn->Pfdebug)
+       fprintf(conn->Pfdebug, "To backend> Msg %c\n",
+               msg_type ? msg_type : ' ');
+
+   return 0;
 }
 
 /*
- * pqWriteReady: is select() saying the file is ready to write?
- * Returns -1 on failure, 0 if not ready, 1 if ready.
+ * pqPutMsgBytes: add bytes to a partially-constructed message
+ *
+ * Returns 0 on success, EOF on error
+ */
+static int
+pqPutMsgBytes(const void *buf, size_t len, PGconn *conn)
+{
+   /* make sure there is room for it */
+   if (checkOutBufferSpace(conn->outMsgEnd + len, conn))
+       return EOF;
+   /* okay, save the data */
+   memcpy(conn->outBuffer + conn->outMsgEnd, buf, len);
+   conn->outMsgEnd += len;
+   /* no Pfdebug call here, caller should do it */
+   return 0;
+}
+
+/*
+ * pqPutMsgEnd: finish constructing a message and possibly send it
+ *
+ * Returns 0 on success, EOF on error
+ *
+ * We don't actually send anything here unless we've accumulated at least
+ * 8K worth of data (the typical size of a pipe buffer on Unix systems).
+ * This avoids sending small partial packets.  The caller must use pqFlush
+ * when it's important to flush all the data out to the server.
  */
 int
-pqWriteReady(PGconn *conn)
+pqPutMsgEnd(PGconn *conn)
 {
-   return pqSocketCheck(conn, 0, 1, (time_t) 0);
+   uint32      msgLen = conn->outMsgEnd - conn->outMsgStart;
+
+   if (conn->Pfdebug)
+       fprintf(conn->Pfdebug, "To backend> Msg complete, length %u\n",
+               msgLen);
+
+   msgLen = htonl(msgLen);
+   memcpy(conn->outBuffer + conn->outMsgStart, &msgLen, 4);
+   conn->outCount = conn->outMsgEnd;
+
+   if (conn->outCount >= 8192)
+   {
+       int     toSend = conn->outCount - (conn->outCount % 8192);
+
+       if (pqSendSome(conn, toSend) < 0)
+           return EOF;
+       /* in nonblock mode, don't complain if unable to send it all */
+   }
+
+   return 0;
 }
 
 /* ----------
@@ -580,16 +632,20 @@ definitelyFailed:
 }
 
 /*
- * pqSendSome: send any data waiting in the output buffer.
+ * pqSendSome: send data waiting in the output buffer.
+ *
+ * len is how much to try to send (typically equal to outCount, but may
+ * be less).
  *
- * Return 0 on sucess, -1 on failure and 1 when data remains because the
- * socket would block and the connection is non-blocking.
+ * Return 0 on success, -1 on failure and 1 when not all data could be sent
+ * because the socket would block and the connection is non-blocking.
  */
-int
-pqSendSome(PGconn *conn)
+static int
+pqSendSome(PGconn *conn, int len)
 {
    char       *ptr = conn->outBuffer;
-   int         len = conn->outCount;
+   int         remaining = conn->outCount;
+   int         result = 0;
 
    if (conn->sock < 0)
    {
@@ -598,13 +654,6 @@ pqSendSome(PGconn *conn)
        return -1;
    }
 
-   /*
-    * don't try to send zero data, allows us to use this function without
-    * too much worry about overhead
-    */
-   if (len == 0)
-       return (0);
-
    /* while there's still data to send */
    while (len > 0)
    {
@@ -648,8 +697,9 @@ pqSendSome(PGconn *conn)
                     * (typically, a NOTICE message from the backend
                     * telling us it's committing hara-kiri...).  Leave
                     * the socket open until pqReadData finds no more data
-                    * can be read.
+                    * can be read.  But abandon attempt to send data.
                     */
+                   conn->outCount = 0;
                    return -1;
 
                default:
@@ -657,6 +707,7 @@ pqSendSome(PGconn *conn)
                    libpq_gettext("could not send data to server: %s\n"),
                                      SOCK_STRERROR(SOCK_ERRNO));
                    /* We don't assume it's a fatal error... */
+                   conn->outCount = 0;
                    return -1;
            }
        }
@@ -664,6 +715,7 @@ pqSendSome(PGconn *conn)
        {
            ptr += sent;
            len -= sent;
+           remaining -= sent;
        }
 
        if (len > 0)
@@ -681,46 +733,49 @@ pqSendSome(PGconn *conn)
 #endif
                if (pqIsnonblocking(conn))
                {
-                   /* shift the contents of the buffer */
-                   memmove(conn->outBuffer, ptr, len);
-                   conn->outCount = len;
-                   return 1;
+                   result = 1;
+                   break;
                }
 #ifdef USE_SSL
            }
 #endif
 
            if (pqWait(FALSE, TRUE, conn))
-               return -1;
+           {
+               result = -1;
+               break;
+           }
        }
    }
 
-   conn->outCount = 0;
+   /* shift the remaining contents of the buffer */
+   if (remaining > 0)
+       memmove(conn->outBuffer, ptr, remaining);
+   conn->outCount = remaining;
 
-   if (conn->Pfdebug)
-       fflush(conn->Pfdebug);
-
-   return 0;
+   return result;
 }
 
 
-
 /*
  * pqFlush: send any data waiting in the output buffer
  *
- * Implemented in terms of pqSendSome to recreate the old behavior which
- * returned 0 if all data was sent or EOF. EOF was sent regardless of
- * whether an error occurred or not all data was sent on a non-blocking
- * socket.
+ * Return 0 on success, -1 on failure and 1 when not all data could be sent
+ * because the socket would block and the connection is non-blocking.
  */
 int
 pqFlush(PGconn *conn)
 {
-   if (pqSendSome(conn))
-       return EOF;
+   if (conn->Pfdebug)
+       fflush(conn->Pfdebug);
+
+   if (conn->outCount > 0)
+       return pqSendSome(conn, conn->outCount);
+
    return 0;
 }
 
+
 /*
  * pqWait: wait until we can read or write the connection socket
  *
@@ -766,10 +821,31 @@ pqWaitTimed(int forRead, int forWrite, PGconn *conn, time_t finish_time)
    return 0;
 }
 
+/*
+ * pqReadReady: is select() saying the file is ready to read?
+ * Returns -1 on failure, 0 if not ready, 1 if ready.
+ */
+int
+pqReadReady(PGconn *conn)
+{
+   return pqSocketCheck(conn, 1, 0, (time_t) 0);
+}
+
+/*
+ * pqWriteReady: is select() saying the file is ready to write?
+ * Returns -1 on failure, 0 if not ready, 1 if ready.
+ */
+int
+pqWriteReady(PGconn *conn)
+{
+   return pqSocketCheck(conn, 0, 1, (time_t) 0);
+}
+
 /*
  * Checks a socket, using poll or select, for data to be read, written,
  * or both.  Returns >0 if one or more conditions are met, 0 if it timed
  * out, -1 if an error occurred.
+ *
  * If SSL is in use, the SSL buffer is checked prior to checking the socket
  * for read data directly.
  */
@@ -787,8 +863,8 @@ pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time)
        return -1;
    }
 
-/* JAB: Check for SSL library buffering read bytes */
 #ifdef USE_SSL
+   /* Check for SSL library buffering read bytes */
    if (forRead && conn->ssl && SSL_pending(conn->ssl) > 0)
    {
        /* short-circuit the select */
@@ -819,6 +895,7 @@ pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time)
  * If neither forRead nor forWrite are set, immediately return a timeout
  * condition (without waiting).  Return >0 if condition is met, 0
  * if a timeout occurred, -1 if an error or interrupt occurred.
+ *
  * Timeout is infinite if end_time is -1.  Timeout is immediate (no blocking)
  * if end_time is 0 (or indeed, any time before now).
  */
@@ -830,16 +907,17 @@ pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time)
    struct pollfd input_fd;
    int           timeout_ms;
 
+   if (!forRead && !forWrite)
+       return 0;
+
    input_fd.fd      = sock;
-   input_fd.events  = 0;
+   input_fd.events  = POLLERR;
    input_fd.revents = 0;
 
    if (forRead)
        input_fd.events |= POLLIN;
    if (forWrite)
        input_fd.events |= POLLOUT;
-   if (!input_fd.events)
-       return 0;
 
    /* Compute appropriate timeout interval */
    if (end_time == ((time_t) -1))
index d32b6fdeea37f7123ac3f8d1e2b97107bdf86189..a86b63eadae1815c9535c9fdc19a5bd9ddc03984 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: libpq-fe.h,v 1.91 2003/03/25 02:44:36 momjian Exp $
+ * $Id: libpq-fe.h,v 1.92 2003/04/19 00:02:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -284,7 +284,6 @@ extern int  PQisnonblocking(const PGconn *conn);
 
 /* Force the write buffer to be written (or at least try) */
 extern int PQflush(PGconn *conn);
-extern int PQsendSome(PGconn *conn);
 
 /*
  * "Fast path" interface --- not really recommended for application
index 43c3bd11c56864e088f237ca3671301fe71da499..8671922547d1cfceb518ccb61ae777831977bdf8 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.61 2003/04/17 22:26:02 tgl Exp $
+ * $Id: libpq-int.h,v 1.62 2003/04/19 00:02:30 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,100) /* XXX temporary value */
+#define PG_PROTOCOL_LIBPQ  PG_PROTOCOL(3,101) /* XXX temporary value */
 
 /*
  * POSTGRES backend dependent Constants.
@@ -266,6 +266,10 @@ struct pg_conn
    int         outBufSize;     /* allocated size of buffer */
    int         outCount;       /* number of chars waiting in buffer */
 
+   /* State for constructing messages in outBuffer */
+   int         outMsgStart;    /* offset to msg start (length word) */
+   int         outMsgEnd;      /* offset to msg end (so far) */
+
    /* Status for asynchronous result construction */
    PGresult   *result;         /* result being constructed */
    PGresAttValue *curTuple;    /* tuple currently being read */
@@ -334,9 +338,10 @@ extern int pqGetnchar(char *s, size_t len, PGconn *conn);
 extern int pqPutnchar(const char *s, size_t len, PGconn *conn);
 extern int pqGetInt(int *result, size_t bytes, PGconn *conn);
 extern int pqPutInt(int value, size_t bytes, PGconn *conn);
+extern int pqPutMsgStart(char msg_type, PGconn *conn);
+extern int pqPutMsgEnd(PGconn *conn);
 extern int pqReadData(PGconn *conn);
 extern int pqFlush(PGconn *conn);
-extern int pqSendSome(PGconn *conn);
 extern int pqWait(int forRead, int forWrite, PGconn *conn);
 extern int pqWaitTimed(int forRead, int forWrite, PGconn *conn, 
                        time_t finish_time);