aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2003-04-22 00:08:07 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2003-04-22 00:08:07 +0000
commit5ed27e35f35f6c354b1a7120ec3a3ce57f93e73e (patch)
tree9ed912fbf02c36160a88881764735f8eab6103b9 /src
parentca944bd2d41814712cb4a4810ab4aa490f23a853 (diff)
downloadpostgresql-5ed27e35f35f6c354b1a7120ec3a3ce57f93e73e.tar.gz
postgresql-5ed27e35f35f6c354b1a7120ec3a3ce57f93e73e.zip
Another round of protocol changes. Backend-to-frontend messages now all
have length words. COPY OUT reimplemented per new protocol: it doesn't need \. anymore, thank goodness. COPY BINARY to/from frontend works, at least as far as the backend is concerned --- libpq's PQgetline API is not up to snuff, and will have to be replaced with something that is null-safe. libpq uses message length words for performance improvement (no cycles wasted rescanning long messages), but not yet for error recovery.
Diffstat (limited to 'src')
-rw-r--r--src/backend/access/common/printtup.c32
-rw-r--r--src/backend/commands/async.c10
-rw-r--r--src/backend/commands/copy.c151
-rw-r--r--src/backend/libpq/auth.c5
-rw-r--r--src/backend/libpq/pqcomm.c44
-rw-r--r--src/backend/libpq/pqformat.c44
-rw-r--r--src/backend/postmaster/postmaster.c22
-rw-r--r--src/backend/tcop/dest.c16
-rw-r--r--src/backend/tcop/fastpath.c5
-rw-r--r--src/backend/tcop/postgres.c7
-rw-r--r--src/backend/utils/error/elog.c21
-rw-r--r--src/include/libpq/pqcomm.h4
-rw-r--r--src/include/libpq/pqformat.h8
-rw-r--r--src/interfaces/libpq/fe-connect.c94
-rw-r--r--src/interfaces/libpq/fe-exec.c444
-rw-r--r--src/interfaces/libpq/fe-misc.c84
-rw-r--r--src/interfaces/libpq/libpq-int.h20
-rw-r--r--src/test/regress/expected/alter_table.out1
-rw-r--r--src/test/regress/expected/copy2.out4
-rw-r--r--src/test/regress/expected/domain.out3
20 files changed, 702 insertions, 317 deletions
diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c
index f1f96f18868..c88dedd93fd 100644
--- a/src/backend/access/common/printtup.c
+++ b/src/backend/access/common/printtup.c
@@ -9,7 +9,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.65 2002/09/04 20:31:08 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.66 2003/04/22 00:08:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -77,15 +77,18 @@ static void
printtup_setup(DestReceiver *self, int operation,
const char *portalName, TupleDesc typeinfo)
{
- /*
- * Send portal name to frontend.
- *
- * If portal name not specified, use "blank" portal.
- */
- if (portalName == NULL)
- portalName = "blank";
-
- pq_puttextmessage('P', portalName);
+ if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
+ {
+ /*
+ * Send portal name to frontend (obsolete cruft, gone in proto 3.0)
+ *
+ * If portal name not specified, use "blank" portal.
+ */
+ if (portalName == NULL)
+ portalName = "blank";
+
+ pq_puttextmessage('P', portalName);
+ }
/*
* if this is a retrieve, then we send back the tuple descriptor of
@@ -98,8 +101,7 @@ printtup_setup(DestReceiver *self, int operation,
int i;
StringInfoData buf;
- pq_beginmessage(&buf);
- pq_sendbyte(&buf, 'T'); /* tuple descriptor message type */
+ pq_beginmessage(&buf, 'T'); /* tuple descriptor message type */
pq_sendint(&buf, natts, 2); /* # of attrs in tuples */
for (i = 0; i < natts; ++i)
@@ -174,8 +176,7 @@ printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
/*
* tell the frontend to expect new tuple data (in ASCII style)
*/
- pq_beginmessage(&buf);
- pq_sendbyte(&buf, 'D');
+ pq_beginmessage(&buf, 'D');
/*
* send a bitmap of which attributes are not null
@@ -388,8 +389,7 @@ printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
/*
* tell the frontend to expect new tuple data (in binary style)
*/
- pq_beginmessage(&buf);
- pq_sendbyte(&buf, 'B');
+ pq_beginmessage(&buf, 'B');
/*
* send a bitmap of which attributes are not null
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index 169c4ce278c..1d9fbf65809 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/async.c,v 1.92 2003/02/18 02:53:29 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/async.c,v 1.93 2003/04/22 00:08:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -847,10 +847,14 @@ NotifyMyFrontEnd(char *relname, int32 listenerPID)
{
StringInfoData buf;
- pq_beginmessage(&buf);
- pq_sendbyte(&buf, 'A');
+ pq_beginmessage(&buf, 'A');
pq_sendint(&buf, listenerPID, sizeof(int32));
pq_sendstring(&buf, relname);
+ if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
+ {
+ /* XXX Add parameter string here later */
+ pq_sendstring(&buf, "");
+ }
pq_endmessage(&buf);
/*
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 32e2362e99b..40948e3a3b5 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.194 2003/04/19 20:36:03 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.195 2003/04/22 00:08:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -50,13 +50,6 @@
#define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
#define OCTVALUE(c) ((c) - '0')
-/* Default line termination */
-#ifndef WIN32
-#define PGEOL "\n"
-#else
-#define PGEOL "\r\n"
-#endif
-
/*
* Represents the different source/dest cases we need to worry about at
* the bottom level
@@ -92,7 +85,7 @@ typedef enum EolType
/* non-export function prototypes */
static void CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
- bool pipe, char *delim, char *null_print);
+ char *delim, char *null_print);
static void CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
char *delim, char *null_print);
static Oid GetInputFunction(Oid type);
@@ -101,8 +94,7 @@ static char *CopyReadAttribute(const char *delim, CopyReadResult *result);
static void CopyAttributeOut(char *string, char *delim);
static List *CopyGetAttnums(Relation rel, List *attnamelist);
-/* The trailing null is part of the signature */
-static const char BinarySignature[] = "PGBCOPY\n\377\r\n";
+static const char BinarySignature[12] = "PGBCOPY\n\377\r\n\0";
/*
* Static communication variables ... pretty grotty, but COPY has
@@ -135,10 +127,11 @@ static int server_encoding;
*/
static void SendCopyBegin(bool binary);
static void ReceiveCopyBegin(bool binary);
-static void SendCopyEnd(bool binary, bool pipe);
+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 CopySendEndOfRow(bool binary);
static void CopyGetData(void *databuf, int datasize);
static int CopyGetChar(void);
#define CopyGetEof() (fe_eof)
@@ -154,22 +147,32 @@ SendCopyBegin(bool binary)
{
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
{
- pq_putbytes("H", 1); /* new way */
- /* XXX grottiness needed for old protocol */
- pq_startcopyout();
+ /* new way */
+ StringInfoData buf;
+
+ pq_beginmessage(&buf, 'H');
+ pq_sendbyte(&buf, binary ? 1 : 0);
+ pq_endmessage(&buf);
copy_dest = COPY_NEW_FE;
+ copy_msgbuf = makeStringInfo();
}
else if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
{
- pq_putbytes("H", 1); /* old way */
- /* grottiness needed for old protocol */
+ /* old way */
+ if (binary)
+ elog(ERROR, "COPY BINARY is not supported to stdout or from stdin");
+ pq_putemptymessage('H');
+ /* grottiness needed for old COPY OUT protocol */
pq_startcopyout();
copy_dest = COPY_OLD_FE;
}
else
{
- pq_putbytes("B", 1); /* very old way */
- /* grottiness needed for old protocol */
+ /* very old way */
+ if (binary)
+ elog(ERROR, "COPY BINARY is not supported to stdout or from stdin");
+ pq_putemptymessage('B');
+ /* grottiness needed for old COPY OUT protocol */
pq_startcopyout();
copy_dest = COPY_OLD_FE;
}
@@ -180,18 +183,29 @@ ReceiveCopyBegin(bool binary)
{
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
{
- pq_putbytes("G", 1); /* new way */
+ /* new way */
+ StringInfoData buf;
+
+ pq_beginmessage(&buf, 'G');
+ pq_sendbyte(&buf, binary ? 1 : 0);
+ pq_endmessage(&buf);
copy_dest = COPY_NEW_FE;
copy_msgbuf = makeStringInfo();
}
else if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
{
- pq_putbytes("G", 1); /* old way */
+ /* old way */
+ if (binary)
+ elog(ERROR, "COPY BINARY is not supported to stdout or from stdin");
+ pq_putemptymessage('G');
copy_dest = COPY_OLD_FE;
}
else
{
- pq_putbytes("D", 1); /* very old way */
+ /* very old way */
+ if (binary)
+ elog(ERROR, "COPY BINARY is not supported to stdout or from stdin");
+ pq_putemptymessage('D');
copy_dest = COPY_OLD_FE;
}
/* We *must* flush here to ensure FE knows it can send. */
@@ -199,22 +213,39 @@ ReceiveCopyBegin(bool binary)
}
static void
-SendCopyEnd(bool binary, bool pipe)
+SendCopyEnd(bool binary)
{
- if (!binary)
+ if (copy_dest == COPY_NEW_FE)
{
- CopySendString("\\.");
- CopySendString(!pipe ? PGEOL : "\n");
+ if (binary)
+ {
+ /* Need to flush out file trailer word */
+ CopySendEndOfRow(true);
+ }
+ else
+ {
+ /* Shouldn't have any unsent data */
+ Assert(copy_msgbuf->len == 0);
+ }
+ /* Send Copy Done message */
+ pq_putemptymessage('c');
+ }
+ else
+ {
+ /* The FE/BE protocol uses \n as newline for all platforms */
+ CopySendData("\\.\n", 3);
+ pq_endcopyout(false);
}
- pq_endcopyout(false);
}
-/*
+/*----------
* 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
+ * CopySendEndOfRow does the appropriate thing at end of each data row
*
* NB: no data conversion is applied by these functions
+ *----------
*/
static void
CopySendData(void *databuf, int datasize)
@@ -228,12 +259,13 @@ CopySendData(void *databuf, int datasize)
break;
case COPY_OLD_FE:
if (pq_putbytes((char *) databuf, datasize))
- fe_eof = true;
+ {
+ /* no hope of recovering connection sync, so FATAL */
+ elog(FATAL, "CopySendData: connection lost");
+ }
break;
case COPY_NEW_FE:
- /* XXX fix later */
- if (pq_putbytes((char *) databuf, datasize))
- fe_eof = true;
+ appendBinaryStringInfo(copy_msgbuf, (char *) databuf, datasize);
break;
}
}
@@ -250,6 +282,40 @@ CopySendChar(char c)
CopySendData(&c, 1);
}
+static void
+CopySendEndOfRow(bool binary)
+{
+ switch (copy_dest)
+ {
+ case COPY_FILE:
+ if (!binary)
+ {
+ /* Default line termination depends on platform */
+#ifndef WIN32
+ CopySendChar('\n');
+#else
+ CopySendString("\r\n");
+#endif
+ }
+ break;
+ case COPY_OLD_FE:
+ /* The FE/BE protocol uses \n as newline for all platforms */
+ if (!binary)
+ CopySendChar('\n');
+ break;
+ case COPY_NEW_FE:
+ /* The FE/BE protocol uses \n as newline for all platforms */
+ if (!binary)
+ CopySendChar('\n');
+ /* Dump the accumulated row as one CopyData message */
+ (void) pq_putmessage('d', copy_msgbuf->data, copy_msgbuf->len);
+ /* Reset copy_msgbuf to empty */
+ copy_msgbuf->len = 0;
+ copy_msgbuf->data[0] = '\0';
+ break;
+ }
+}
+
/*
* CopyGetData reads data from the source (file or frontend)
* CopyGetChar does the same for single characters
@@ -569,13 +635,6 @@ DoCopy(const CopyStmt *stmt)
"from stdin. Psql's \\copy command also works for anyone.");
/*
- * This restriction is unfortunate, but necessary until the frontend
- * COPY protocol is redesigned to be binary-safe...
- */
- if (pipe && binary)
- elog(ERROR, "COPY BINARY is not supported to stdout or from stdin");
-
- /*
* Presently, only single-character delimiter strings are supported.
*/
if (strlen(delim) != 1)
@@ -698,13 +757,13 @@ DoCopy(const CopyStmt *stmt)
elog(ERROR, "COPY: %s is a directory", filename);
}
}
- CopyTo(rel, attnumlist, binary, oids, pipe, delim, null_print);
+ CopyTo(rel, attnumlist, binary, oids, delim, null_print);
}
if (!pipe)
FreeFile(copy_file);
else if (IsUnderPostmaster && !is_from)
- SendCopyEnd(binary, pipe);
+ SendCopyEnd(binary);
pfree(attribute_buf.data);
/*
@@ -721,7 +780,7 @@ DoCopy(const CopyStmt *stmt)
* Copy from relation TO file.
*/
static void
-CopyTo(Relation rel, List *attnumlist, bool binary, bool oids, bool pipe,
+CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
char *delim, char *null_print)
{
HeapTuple tuple;
@@ -786,7 +845,7 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids, bool pipe,
int32 tmp;
/* Signature */
- CopySendData((char *) BinarySignature, sizeof(BinarySignature));
+ CopySendData((char *) BinarySignature, 12);
/* Integer layout field */
tmp = 0x01020304;
CopySendData(&tmp, sizeof(int32));
@@ -918,8 +977,7 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids, bool pipe,
}
}
- if (!binary)
- CopySendString(!pipe ? PGEOL : "\n");
+ CopySendEndOfRow(binary);
MemoryContextSwitchTo(oldcontext);
}
@@ -1100,8 +1158,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
/* Signature */
CopyGetData(readSig, 12);
- if (CopyGetEof() || memcmp(readSig, BinarySignature,
- sizeof(BinarySignature)) != 0)
+ if (CopyGetEof() || memcmp(readSig, BinarySignature, 12) != 0)
elog(ERROR, "COPY BINARY: file signature not recognized");
/* Integer layout field */
CopyGetData(&tmp, sizeof(int32));
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index a5dc8eff2da..2edc919c6d2 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.99 2003/04/19 00:02:29 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.100 2003/04/22 00:08:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -512,8 +512,7 @@ sendAuthRequest(Port *port, AuthRequest areq)
{
StringInfoData buf;
- pq_beginmessage(&buf);
- pq_sendbyte(&buf, 'R');
+ pq_beginmessage(&buf, 'R');
pq_sendint(&buf, (int32) areq, sizeof(int32));
/* Add the salt for encrypted passwords. */
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index 9a4f51b7786..2cf2a36b7b3 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -12,15 +12,16 @@
* 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
* the frontend gets back into sync. Therefore, these routines have to be
- * aware of COPY OUT state.
+ * aware of COPY OUT state. (New COPY-OUT is message-based and does *not*
+ * set the DoingCopyOut flag.)
*
* NOTE: generally, it's a bad idea to emit outgoing messages directly with
* pq_putbytes(), especially if the message would require multiple calls
* to send. Instead, use the routines in pqformat.c to construct the message
- * in a buffer and then emit it in one call to pq_putmessage. This helps
- * ensure that the channel will not be clogged by an incomplete message
- * if execution is aborted by elog(ERROR) partway through the message.
- * The only non-libpq code that should call pq_putbytes directly is COPY OUT.
+ * in a buffer and then emit it in one call to pq_putmessage. This ensures
+ * that the channel will not be clogged by an incomplete message if execution
+ * is aborted by elog(ERROR) partway through the message. The only non-libpq
+ * code that should call pq_putbytes directly is old-style COPY OUT.
*
* At one time, libpq was shared between frontend and backend, but now
* the backend's "backend/libpq" is quite separate from "interfaces/libpq".
@@ -29,7 +30,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Header: /cvsroot/pgsql/src/backend/libpq/pqcomm.c,v 1.150 2003/04/19 00:02:29 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/libpq/pqcomm.c,v 1.151 2003/04/22 00:08:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -846,13 +847,17 @@ pq_flush(void)
* pq_putmessage - send a normal message (suppressed in COPY OUT mode)
*
* If msgtype is not '\0', it is a message type code to place before
- * the message body (len counts only the body size!).
- * If msgtype is '\0', then the buffer already includes the type code.
+ * the message body. If msgtype is '\0', then the message has no type
+ * code (this is only valid in pre-3.0 protocols).
*
- * All normal messages are suppressed while COPY OUT is in progress.
- * (In practice only a few messages might get emitted then; dropping
- * them is annoying, but at least they will still appear in the
- * postmaster log.)
+ * len is the length of the message body data at *s. In protocol 3.0
+ * and later, a message length word (equal to len+4 because it counts
+ * itself too) is inserted by this routine.
+ *
+ * All normal messages are suppressed while old-style COPY OUT is in
+ * progress. (In practice only a few notice messages might get emitted
+ * then; dropping them is annoying, but at least they will still appear
+ * in the postmaster log.)
*
* returns 0 if OK, EOF if trouble
* --------------------------------
@@ -865,6 +870,14 @@ pq_putmessage(char msgtype, const char *s, size_t len)
if (msgtype)
if (pq_putbytes(&msgtype, 1))
return EOF;
+ if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
+ {
+ uint32 n32;
+
+ n32 = htonl((uint32) (len + 4));
+ if (pq_putbytes((char *) &n32, 4))
+ return EOF;
+ }
return pq_putbytes(s, len);
}
@@ -880,12 +893,13 @@ pq_startcopyout(void)
}
/* --------------------------------
- * pq_endcopyout - end a COPY OUT transfer
+ * pq_endcopyout - end an old-style COPY OUT transfer
*
* If errorAbort is indicated, we are aborting a COPY OUT due to an error,
* and must send a terminator line. Since a partial data line might have
* been emitted, send a couple of newlines first (the first one could
- * get absorbed by a backslash...)
+ * get absorbed by a backslash...) Note that old-style COPY OUT does
+ * not allow binary transfers, so a textual terminator is always correct.
* --------------------------------
*/
void
@@ -893,8 +907,8 @@ pq_endcopyout(bool errorAbort)
{
if (!DoingCopyOut)
return;
+ DoingCopyOut = false;
if (errorAbort)
pq_putbytes("\n\n\\.\n", 5);
/* in non-error case, copy.c will have emitted the terminator line */
- DoingCopyOut = false;
}
diff --git a/src/backend/libpq/pqformat.c b/src/backend/libpq/pqformat.c
index 80ca3190999..dacfa93ecc7 100644
--- a/src/backend/libpq/pqformat.c
+++ b/src/backend/libpq/pqformat.c
@@ -18,7 +18,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Header: /cvsroot/pgsql/src/backend/libpq/pqformat.c,v 1.27 2003/04/19 00:02:29 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/libpq/pqformat.c,v 1.28 2003/04/22 00:08:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -38,6 +38,7 @@
*
* Special-case message output:
* pq_puttextmessage - generate a character set-converted message in one step
+ * pq_putemptymessage - convenience routine for message with empty body
*
* Message parsing after input:
* pq_getmsgbyte - get a raw byte from a message buffer
@@ -64,6 +65,22 @@
/* --------------------------------
+ * pq_beginmessage - initialize for sending a message
+ * --------------------------------
+ */
+void
+pq_beginmessage(StringInfo buf, char msgtype)
+{
+ initStringInfo(buf);
+ /*
+ * We stash the message type into the buffer's cursor field, expecting
+ * that the pq_sendXXX routines won't touch it. We could alternatively
+ * make it the first byte of the buffer contents, but this seems easier.
+ */
+ buf->cursor = msgtype;
+}
+
+/* --------------------------------
* pq_sendbyte - append a raw byte to a StringInfo buffer
* --------------------------------
*/
@@ -176,7 +193,8 @@ pq_sendint(StringInfo buf, int i, int b)
void
pq_endmessage(StringInfo buf)
{
- (void) pq_putmessage('\0', buf->data, buf->len);
+ /* msgtype was saved in cursor field */
+ (void) pq_putmessage(buf->cursor, buf->data, buf->len);
/* no need to complain about any failure, since pqcomm.c already did */
pfree(buf->data);
buf->data = NULL;
@@ -188,11 +206,9 @@ pq_endmessage(StringInfo buf)
* This is the same as the pqcomm.c routine pq_putmessage, except that
* the message body is a null-terminated string to which encoding
* conversion applies.
- *
- * returns 0 if OK, EOF if trouble
* --------------------------------
*/
-int
+void
pq_puttextmessage(char msgtype, const char *str)
{
int slen = strlen(str);
@@ -201,12 +217,22 @@ pq_puttextmessage(char msgtype, const char *str)
p = (char *) pg_server_to_client((unsigned char *) str, slen);
if (p != str) /* actual conversion has been done? */
{
- int result = pq_putmessage(msgtype, p, strlen(p) + 1);
-
+ (void) pq_putmessage(msgtype, p, strlen(p) + 1);
pfree(p);
- return result;
+ return;
}
- return pq_putmessage(msgtype, str, slen + 1);
+ (void) pq_putmessage(msgtype, str, slen + 1);
+}
+
+
+/* --------------------------------
+ * pq_putemptymessage - convenience routine for message with empty body
+ * --------------------------------
+ */
+void
+pq_putemptymessage(char msgtype)
+{
+ (void) pq_putmessage(msgtype, NULL, 0);
}
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index d6beb0fc1a6..834b03ab628 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -37,7 +37,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.313 2003/04/19 00:02:29 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.314 2003/04/22 00:08:06 tgl Exp $
*
* NOTES
*
@@ -1118,7 +1118,13 @@ ProcessStartupPacket(Port *port, bool SSLdone)
if (pq_getbytes((char *) &len, 4) == EOF)
{
- elog(COMMERROR, "incomplete startup packet");
+ /*
+ * EOF after SSLdone probably means the client didn't like our
+ * response to NEGOTIATE_SSL_CODE. That's not an error condition,
+ * so don't clutter the log with a complaint.
+ */
+ if (!SSLdone)
+ elog(COMMERROR, "incomplete startup packet");
return STATUS_ERROR;
}
@@ -1127,7 +1133,10 @@ ProcessStartupPacket(Port *port, bool SSLdone)
if (len < (int32) sizeof(ProtocolVersion) ||
len > MAX_STARTUP_PACKET_LENGTH)
- elog(FATAL, "invalid length of startup packet");
+ {
+ elog(COMMERROR, "invalid length of startup packet");
+ return STATUS_ERROR;
+ }
/*
* Allocate at least the size of an old-style startup packet, plus one
@@ -1173,7 +1182,7 @@ ProcessStartupPacket(Port *port, bool SSLdone)
#endif
if (send(port->sock, &SSLok, 1, 0) != 1)
{
- elog(LOG, "failed to send SSL negotiation response: %m");
+ elog(COMMERROR, "failed to send SSL negotiation response: %m");
return STATUS_ERROR; /* close the connection */
}
@@ -1188,6 +1197,11 @@ ProcessStartupPacket(Port *port, bool SSLdone)
/* Could add additional special packet types here */
+ /*
+ * Set FrontendProtocol now so that elog() knows what format to send
+ * if we fail during startup.
+ */
+ FrontendProtocol = proto;
/*
* XXX temporary for 3.0 protocol development: we are using the minor
diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c
index 07e4614e799..5ccaa60995c 100644
--- a/src/backend/tcop/dest.c
+++ b/src/backend/tcop/dest.c
@@ -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.52 2003/04/19 00:02:29 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.53 2003/04/22 00:08:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -141,7 +141,9 @@ EndCommand(const char *commandTag, CommandDest dest)
* libpq's crufty way of determining whether a multiple-command
* query string is done. In protocol 2.0 it's probably not really
* necessary to distinguish empty queries anymore, but we still do it
- * for backwards compatibility with 1.0.
+ * for backwards compatibility with 1.0. In protocol 3.0 it has some
+ * use again, since it ensures that there will be a recognizable end
+ * to the response to an Execute message.
* ----------------
*/
void
@@ -153,9 +155,13 @@ NullCommand(CommandDest dest)
case Remote:
/*
- * tell the fe that we saw an empty query string
+ * tell the fe that we saw an empty query string. In protocols
+ * before 3.0 this has a useless empty-string message body.
*/
- pq_putbytes("I", 2); /* note we send I and \0 */
+ if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
+ pq_putemptymessage('I');
+ else
+ pq_puttextmessage('I', "");
break;
case Debug:
@@ -184,7 +190,7 @@ ReadyForQuery(CommandDest dest)
case RemoteInternal:
case Remote:
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
- pq_putbytes("Z", 1);
+ pq_putemptymessage('Z');
/* Flush output at end of cycle in any case. */
pq_flush();
break;
diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c
index eeddea6f6eb..b8750957349 100644
--- a/src/backend/tcop/fastpath.c
+++ b/src/backend/tcop/fastpath.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.58 2003/04/19 00:02:29 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.59 2003/04/22 00:08:07 tgl Exp $
*
* NOTES
* This cruft is the server side of PQfn.
@@ -119,8 +119,7 @@ SendFunctionResult(Datum retval, bool retbyval, int retlen)
{
StringInfoData buf;
- pq_beginmessage(&buf);
- pq_sendbyte(&buf, 'V');
+ pq_beginmessage(&buf, 'V');
if (retlen != 0)
{
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index fcc6591f7c0..5c51a1056a2 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.322 2003/04/19 00:02:29 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.323 2003/04/22 00:08:07 tgl Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
@@ -1821,8 +1821,7 @@ PostgresMain(int argc, char *argv[], const char *username)
{
StringInfoData buf;
- pq_beginmessage(&buf);
- pq_sendbyte(&buf, 'K');
+ pq_beginmessage(&buf, 'K');
pq_sendint(&buf, (int32) MyProcPid, sizeof(int32));
pq_sendint(&buf, (int32) MyCancelKey, sizeof(int32));
pq_endmessage(&buf);
@@ -1832,7 +1831,7 @@ PostgresMain(int argc, char *argv[], const char *username)
if (!IsUnderPostmaster)
{
puts("\nPOSTGRES backend interactive interface ");
- puts("$Revision: 1.322 $ $Date: 2003/04/19 00:02:29 $\n");
+ puts("$Revision: 1.323 $ $Date: 2003/04/22 00:08:07 $\n");
}
/*
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 763024b5773..01250f9a2f0 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/error/elog.c,v 1.107 2003/03/20 03:34:56 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/error/elog.c,v 1.108 2003/04/22 00:08:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -406,20 +406,19 @@ elog(int lev, const char *fmt,...)
*/
oldcxt = MemoryContextSwitchTo(ErrorContext);
- if (lev <= WARNING)
- /* exclude the timestamp from msg sent to frontend */
- send_message_to_frontend(lev, msg_buf + timestamp_size);
- else
+ if (lev >= ERROR)
{
/*
* Abort any COPY OUT in progress when an error is detected.
- * This hack is necessary because of poor design of copy
- * protocol.
+ * This hack is necessary because of poor design of old-style
+ * copy protocol.
*/
pq_endcopyout(true);
- send_message_to_frontend(ERROR, msg_buf + timestamp_size);
}
+ /* Exclude the timestamp from msg sent to frontend */
+ send_message_to_frontend(lev, msg_buf + timestamp_size);
+
MemoryContextSwitchTo(oldcxt);
}
@@ -745,11 +744,9 @@ send_message_to_frontend(int type, const char *msg)
{
StringInfoData buf;
- AssertArg(type <= ERROR);
-
- pq_beginmessage(&buf);
/* 'N' (Notice) is for nonfatal conditions, 'E' is for errors */
- pq_sendbyte(&buf, type < ERROR ? 'N' : 'E');
+ pq_beginmessage(&buf, (type < ERROR) ? 'N' : 'E');
+ /* XXX more to do here */
pq_sendstring(&buf, msg);
pq_endmessage(&buf);
diff --git a/src/include/libpq/pqcomm.h b/src/include/libpq/pqcomm.h
index 61aa695e272..420f1e438e3 100644
--- a/src/include/libpq/pqcomm.h
+++ b/src/include/libpq/pqcomm.h
@@ -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.77 2003/04/19 00:02:29 tgl Exp $
+ * $Id: pqcomm.h,v 1.78 2003/04/22 00:08:07 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,101) /* XXX temporary value */
+#define PG_PROTOCOL_LATEST PG_PROTOCOL(3,102) /* XXX temporary value */
typedef uint32 ProtocolVersion; /* FE/BE protocol version number */
diff --git a/src/include/libpq/pqformat.h b/src/include/libpq/pqformat.h
index cb80ec2c201..229de38c9b3 100644
--- a/src/include/libpq/pqformat.h
+++ b/src/include/libpq/pqformat.h
@@ -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.14 2003/04/19 00:02:29 tgl Exp $
+ * $Id: pqformat.h,v 1.15 2003/04/22 00:08:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -15,8 +15,7 @@
#include "lib/stringinfo.h"
-#define pq_beginmessage(buf) initStringInfo(buf)
-
+extern void pq_beginmessage(StringInfo buf, char msgtype);
extern void pq_sendbyte(StringInfo buf, int byt);
extern void pq_sendbytes(StringInfo buf, const char *data, int datalen);
extern void pq_sendcountedtext(StringInfo buf, const char *str, int slen);
@@ -24,7 +23,8 @@ extern void pq_sendstring(StringInfo buf, const char *str);
extern void pq_sendint(StringInfo buf, int i, int b);
extern void pq_endmessage(StringInfo buf);
-extern int pq_puttextmessage(char msgtype, const char *str);
+extern void pq_puttextmessage(char msgtype, const char *str);
+extern void pq_putemptymessage(char msgtype);
extern int pq_getmsgbyte(StringInfo msg);
extern unsigned int pq_getmsgint(StringInfo msg, int b);
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index a322d8a73d1..086462094ff 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.233 2003/04/19 00:02:30 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.234 2003/04/22 00:08:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1328,6 +1328,8 @@ keep_going: /* We will come back to here until there
case CONNECTION_AWAITING_RESPONSE:
{
char beresp;
+ int msgLength;
+ int avail;
AuthRequest areq;
/*
@@ -1337,15 +1339,58 @@ keep_going: /* We will come back to here until there
*/
conn->inCursor = conn->inStart;
+ /* Read type byte */
if (pqGetc(&beresp, conn))
{
/* We'll come back when there is more data */
return PGRES_POLLING_READING;
}
- /* Handle errors. */
- if (beresp == 'E')
+ /*
+ * Validate message type: we expect only an authentication
+ * request or an error here. Anything else probably means
+ * it's not Postgres on the other end at all.
+ */
+ if (!(beresp == 'R' || beresp == 'E'))
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext(
+ "expected authentication request from "
+ "server, but received %c\n"
+ ),
+ beresp);
+ goto error_return;
+ }
+
+ /* Read message length word */
+ if (pqGetInt(&msgLength, 4, conn))
+ {
+ /* We'll come back when there is more data */
+ return PGRES_POLLING_READING;
+ }
+
+ /*
+ * Try to validate message length before using it.
+ * Authentication requests can't be very large. Errors
+ * can be a little larger, but not huge. If we see a large
+ * apparent length in an error, it means we're really talking
+ * to a pre-3.0-protocol server; cope.
+ */
+ if (beresp == 'R' && (msgLength < 8 || msgLength > 100))
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext(
+ "expected authentication request from "
+ "server, but received %c\n"
+ ),
+ beresp);
+ goto error_return;
+ }
+
+ if (beresp == 'E' && (msgLength < 8 || msgLength > 30000))
{
+ /* Handle error from a pre-3.0 server */
+ conn->inCursor = conn->inStart + 1; /* reread data */
if (pqGets(&conn->errorMessage, conn))
{
/* We'll come back when there is more data */
@@ -1363,18 +1408,45 @@ keep_going: /* We will come back to here until there
goto error_return;
}
- /* Otherwise it should be an authentication request. */
- if (beresp != 'R')
+ /*
+ * Can't process if message body isn't all here yet.
+ */
+ msgLength -= 4;
+ avail = conn->inEnd - conn->inCursor;
+ if (avail < msgLength)
{
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext(
- "expected authentication request from "
- "server, but received %c\n"
- ),
- beresp);
+ /*
+ * Before returning, try to enlarge the input buffer if
+ * needed to hold the whole message; see notes in
+ * parseInput.
+ */
+ if (pqCheckInBufferSpace(conn->inCursor + msgLength, conn))
+ goto error_return;
+ /* We'll come back when there is more data */
+ return PGRES_POLLING_READING;
+ }
+
+ /* Handle errors. */
+ if (beresp == 'E')
+ {
+ if (pqGets(&conn->errorMessage, conn))
+ {
+ /* We'll come back when there is more data */
+ return PGRES_POLLING_READING;
+ }
+ /* OK, we read the message; mark data consumed */
+ conn->inStart = conn->inCursor;
+
+ /*
+ * The postmaster typically won't end its message with
+ * a newline, so add one to conform to libpq
+ * conventions.
+ */
+ appendPQExpBufferChar(&conn->errorMessage, '\n');
goto error_return;
}
+ /* It is an authentication request. */
/* Get the type of request. */
if (pqGetInt((int *) &areq, 4, conn))
{
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 487acff83df..16e63f7f68f 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.129 2003/04/19 00:02:30 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.130 2003/04/22 00:08:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -51,6 +51,7 @@ static PGresult *prepareAsyncResult(PGconn *conn);
static int addTuple(PGresult *res, PGresAttValue * tup);
static void parseInput(PGconn *conn);
static void handleSendFailure(PGconn *conn);
+static void handleSyncLoss(PGconn *conn, char id, int msgLength);
static int getRowDescriptions(PGconn *conn);
static int getAnotherTuple(PGconn *conn, int binary);
static int getNotify(PGconn *conn);
@@ -866,6 +867,8 @@ static void
parseInput(PGconn *conn)
{
char id;
+ int msgLength;
+ int avail;
char noticeWorkspace[128];
/*
@@ -874,25 +877,63 @@ parseInput(PGconn *conn)
for (;;)
{
/*
- * Quit if in COPY_OUT state: we expect raw data from the server
- * until PQendcopy is called. Don't try to parse it according to
- * the normal protocol. (This is bogus. The data lines ought to
- * be part of the protocol and have identifying leading
- * characters.)
+ * Try to read a message. First get the type code and length.
+ * Return if not enough data.
*/
- if (conn->asyncStatus == PGASYNC_COPY_OUT)
+ conn->inCursor = conn->inStart;
+ if (pqGetc(&id, conn))
+ return;
+ if (pqGetInt(&msgLength, 4, conn))
return;
/*
- * OK to try to read a message type code.
+ * Try to validate message type/length here. A length less than 4
+ * is definitely broken. Large lengths should only be believed
+ * for a few message types.
*/
- conn->inCursor = conn->inStart;
- if (pqGetc(&id, conn))
+ if (msgLength < 4)
+ {
+ handleSyncLoss(conn, id, msgLength);
+ return;
+ }
+ if (msgLength > 30000 &&
+ !(id == 'T' || id == 'D' || id == 'B' || id == 'd'))
+ {
+ handleSyncLoss(conn, id, msgLength);
+ return;
+ }
+
+ /*
+ * Can't process if message body isn't all here yet.
+ */
+ msgLength -= 4;
+ avail = conn->inEnd - conn->inCursor;
+ if (avail < msgLength)
+ {
+ /*
+ * Before returning, enlarge the input buffer if needed to hold
+ * the whole message. This is better than leaving it to
+ * pqReadData because we can avoid multiple cycles of realloc()
+ * when the message is large; also, we can implement a reasonable
+ * recovery strategy if we are unable to make the buffer big
+ * enough.
+ */
+ if (pqCheckInBufferSpace(conn->inCursor + msgLength, conn))
+ {
+ /*
+ * XXX add some better recovery code... plan is to skip
+ * over the message using its length, then report an error.
+ * For the moment, just treat this like loss of sync (which
+ * indeed it might be!)
+ */
+ handleSyncLoss(conn, id, msgLength);
+ }
return;
+ }
/*
- * NOTIFY and NOTICE messages can happen in any state besides
- * COPY OUT; always process them right away.
+ * NOTIFY and NOTICE messages can happen in any state; always process
+ * them right away.
*
* Most other messages should only be processed while in BUSY state.
* (In particular, in READY state we hold off further parsing
@@ -936,9 +977,8 @@ parseInput(PGconn *conn)
libpq_gettext("message type 0x%02x arrived from server while idle\n"),
id);
DONOTICE(conn, noticeWorkspace);
- /* Discard the unexpected message; good idea?? */
- conn->inStart = conn->inEnd;
- break;
+ /* Discard the unexpected message */
+ conn->inCursor += msgLength;
}
}
else
@@ -969,16 +1009,6 @@ parseInput(PGconn *conn)
conn->asyncStatus = PGASYNC_IDLE;
break;
case 'I': /* empty query */
- /* read and throw away the closing '\0' */
- if (pqGetc(&id, conn))
- return;
- if (id != '\0')
- {
- snprintf(noticeWorkspace, sizeof(noticeWorkspace),
- libpq_gettext("unexpected character %c following empty query response (\"I\" message)\n"),
- id);
- DONOTICE(conn, noticeWorkspace);
- }
if (conn->result == NULL)
conn->result = PQmakeEmptyPGresult(conn,
PGRES_EMPTY_QUERY);
@@ -996,11 +1026,6 @@ parseInput(PGconn *conn)
if (pqGetInt(&(conn->be_key), 4, conn))
return;
break;
- case 'P': /* synchronous (normal) portal */
- if (pqGets(&conn->workBuffer, conn))
- return;
- /* We pretty much ignore this message type... */
- break;
case 'T': /* row descriptions (start of query
* results) */
if (conn->result == NULL)
@@ -1034,9 +1059,8 @@ parseInput(PGconn *conn)
snprintf(noticeWorkspace, sizeof(noticeWorkspace),
libpq_gettext("server sent data (\"D\" message) without prior row description (\"T\" message)\n"));
DONOTICE(conn, noticeWorkspace);
- /* Discard the unexpected message; good idea?? */
- conn->inStart = conn->inEnd;
- return;
+ /* Discard the unexpected message */
+ conn->inCursor += msgLength;
}
break;
case 'B': /* Binary data tuple */
@@ -1051,16 +1075,36 @@ parseInput(PGconn *conn)
snprintf(noticeWorkspace, sizeof(noticeWorkspace),
libpq_gettext("server sent binary data (\"B\" message) without prior row description (\"T\" message)\n"));
DONOTICE(conn, noticeWorkspace);
- /* Discard the unexpected message; good idea?? */
- conn->inStart = conn->inEnd;
- return;
+ /* Discard the unexpected message */
+ conn->inCursor += msgLength;
}
break;
case 'G': /* Start Copy In */
+ if (pqGetc(&conn->copy_is_binary, conn))
+ return;
conn->asyncStatus = PGASYNC_COPY_IN;
break;
case 'H': /* Start Copy Out */
+ if (pqGetc(&conn->copy_is_binary, conn))
+ return;
conn->asyncStatus = PGASYNC_COPY_OUT;
+ conn->copy_already_done = 0;
+ break;
+ case 'd': /* Copy Data */
+ /*
+ * If we see Copy Data, just silently drop it. This
+ * would only occur if application exits COPY OUT mode
+ * too early.
+ */
+ conn->inCursor += msgLength;
+ break;
+ case 'c': /* Copy Done */
+ /*
+ * If we see Copy Done, just silently drop it. This
+ * is the normal case during PQendcopy. We will keep
+ * swallowing data, expecting to see command-complete
+ * for the COPY command.
+ */
break;
default:
printfPQExpBuffer(&conn->errorMessage,
@@ -1069,17 +1113,54 @@ parseInput(PGconn *conn)
id);
/* build an error result holding the error message */
saveErrorResult(conn);
- /* Discard the unexpected message; good idea?? */
- conn->inStart = conn->inEnd;
conn->asyncStatus = PGASYNC_READY;
- return;
+ /* Discard the unexpected message */
+ conn->inCursor += msgLength;
+ break;
} /* switch on protocol character */
}
/* Successfully consumed this message */
- conn->inStart = conn->inCursor;
+ if (conn->inCursor == conn->inStart + 5 + msgLength)
+ {
+ /* Normal case: parsing agrees with specified length */
+ conn->inStart = conn->inCursor;
+ }
+ else
+ {
+ /* Trouble --- report it */
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("Message contents do not agree with length in message type \"%c\"\n"),
+ id);
+ /* build an error result holding the error message */
+ saveErrorResult(conn);
+ conn->asyncStatus = PGASYNC_READY;
+ /* trust the specified message length as what to skip */
+ conn->inStart += 5 + msgLength;
+ }
}
}
+/*
+ * handleSyncLoss: clean up after loss of message-boundary sync
+ *
+ * There isn't really a lot we can do here except abandon the connection.
+ */
+static void
+handleSyncLoss(PGconn *conn, char id, int msgLength)
+{
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext(
+ "lost synchronization with server: got message type \"%c\", length %d\n"),
+ id, msgLength);
+ conn->status = CONNECTION_BAD; /* No more connection to backend */
+ pqsecure_close(conn);
+#ifdef WIN32
+ closesocket(conn->sock);
+#else
+ close(conn->sock);
+#endif
+ conn->sock = -1;
+}
/*
* parseInput subroutine to read a 'T' (row descriptions) message.
@@ -1100,7 +1181,7 @@ getRowDescriptions(PGconn *conn)
result = PQmakeEmptyPGresult(conn, PGRES_TUPLES_OK);
- /* parseInput already read the 'T' label. */
+ /* parseInput already read the 'T' label and message length. */
/* the next two bytes are the number of fields */
if (pqGetInt(&(result->numAttributes), 2, conn))
{
@@ -1461,7 +1542,7 @@ errout:
/*
* Attempt to read a Notice response message.
* This is possible in several places, so we break it out as a subroutine.
- * Entry: 'N' flag character has already been consumed.
+ * Entry: 'N' message type and length have already been consumed.
* Exit: returns 0 if successfully consumed Notice message.
* returns EOF if not enough data.
*/
@@ -1489,7 +1570,7 @@ getNotice(PGconn *conn)
/*
* Attempt to read a Notify response message.
* This is possible in several places, so we break it out as a subroutine.
- * Entry: 'A' flag character has already been consumed.
+ * Entry: 'A' message type and length have already been consumed.
* Exit: returns 0 if successfully consumed Notify message.
* returns EOF if not enough data.
*/
@@ -1511,10 +1592,18 @@ getNotify(PGconn *conn)
*/
newNotify = (PGnotify *) malloc(sizeof(PGnotify) +
strlen(conn->workBuffer.data) +1);
- newNotify->relname = (char *) newNotify + sizeof(PGnotify);
- strcpy(newNotify->relname, conn->workBuffer.data);
- newNotify->be_pid = be_pid;
- DLAddTail(conn->notifyList, DLNewElem(newNotify));
+ if (newNotify)
+ {
+ newNotify->relname = (char *) newNotify + sizeof(PGnotify);
+ strcpy(newNotify->relname, conn->workBuffer.data);
+ newNotify->be_pid = be_pid;
+ DLAddTail(conn->notifyList, DLNewElem(newNotify));
+ }
+
+ /* Swallow extra string (not presently used) */
+ if (pqGets(&conn->workBuffer, conn))
+ return EOF;
+
return 0;
}
@@ -1556,6 +1645,9 @@ PQnotifies(PGconn *conn)
* Chiefly here so that applications can use "COPY <rel> to stdout"
* and read the output string. Returns a null-terminated string in s.
*
+ * XXX this routine is now deprecated, because it can't handle binary data.
+ * If called during a COPY BINARY we return EOF.
+ *
* PQgetline reads up to maxlen-1 characters (like fgets(3)) but strips
* the terminating \n (like gets(3)).
*
@@ -1563,7 +1655,7 @@ PQnotifies(PGconn *conn)
* (a line containing just "\.") when using this routine.
*
* RETURNS:
- * EOF if it is detected or invalid arguments are given
+ * EOF if error (eg, invalid arguments are given)
* 0 if EOL is reached (i.e., \n has been read)
* (this is required for backward-compatibility -- this
* routine used to always return EOF or 0, assuming that
@@ -1573,53 +1665,55 @@ PQnotifies(PGconn *conn)
int
PQgetline(PGconn *conn, char *s, int maxlen)
{
- int result = 1; /* return value if buffer overflows */
+ int status;
- if (!s || maxlen <= 0)
+ /* maxlen must be at least 3 to hold the \. terminator! */
+ if (!conn || !s || maxlen < 3)
return EOF;
- if (!conn || conn->sock < 0)
+ if (conn->sock < 0 ||
+ conn->asyncStatus != PGASYNC_COPY_OUT ||
+ conn->copy_is_binary)
{
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("PQgetline: not doing text COPY OUT\n"));
*s = '\0';
return EOF;
}
- /*
- * Since this is a purely synchronous routine, we don't bother to
- * maintain conn->inCursor; there is no need to back up.
- */
- while (maxlen > 1)
+ while ((status = PQgetlineAsync(conn, s, maxlen-1)) == 0)
{
- if (conn->inStart < conn->inEnd)
- {
- char c = conn->inBuffer[conn->inStart++];
-
- if (c == '\n')
- {
- result = 0; /* success exit */
- break;
- }
- *s++ = c;
- maxlen--;
- }
- else
+ /* need to load more data */
+ if (pqWait(TRUE, FALSE, conn) ||
+ pqReadData(conn) < 0)
{
- /* need to load more data */
- if (pqWait(TRUE, FALSE, conn) ||
- pqReadData(conn) < 0)
- {
- result = EOF;
- break;
- }
+ *s = '\0';
+ return EOF;
}
}
- *s = '\0';
- return result;
+ if (status < 0)
+ {
+ /* End of copy detected; gin up old-style terminator */
+ strcpy(s, "\\.");
+ return 0;
+ }
+
+ /* Add null terminator, and strip trailing \n if present */
+ if (s[status-1] == '\n')
+ {
+ s[status-1] = '\0';
+ return 0;
+ }
+ else
+ {
+ s[status] = '\0';
+ return 1;
+ }
}
/*
- * PQgetlineAsync - gets a newline-terminated string without blocking.
+ * PQgetlineAsync - gets a COPY data row without blocking.
*
* This routine is for applications that want to do "COPY <rel> to stdout"
* asynchronously, that is without blocking. Having issued the COPY command
@@ -1627,10 +1721,9 @@ PQgetline(PGconn *conn, char *s, int maxlen)
* and this routine until the end-of-data signal is detected. Unlike
* PQgetline, this routine takes responsibility for detecting end-of-data.
*
- * On each call, PQgetlineAsync will return data if a complete newline-
- * terminated data line is available in libpq's input buffer, or if the
- * incoming data line is too long to fit in the buffer offered by the caller.
- * Otherwise, no data is returned until the rest of the line arrives.
+ * On each call, PQgetlineAsync will return data if a complete data row
+ * is available in libpq's input buffer. Otherwise, no data is returned
+ * until the rest of the row arrives.
*
* If -1 is returned, the end-of-data signal has been recognized (and removed
* from libpq's input buffer). The caller *must* next call PQendcopy and
@@ -1640,66 +1733,73 @@ PQgetline(PGconn *conn, char *s, int maxlen)
* -1 if the end-of-copy-data marker has been recognized
* 0 if no data is available
* >0 the number of bytes returned.
- * The data returned will not extend beyond a newline character. If possible
- * a whole line will be returned at one time. But if the buffer offered by
- * the caller is too small to hold a line sent by the backend, then a partial
- * data line will be returned. This can be detected by testing whether the
- * last returned byte is '\n' or not.
- * The returned string is *not* null-terminated.
+ *
+ * The data returned will not extend beyond a data-row boundary. If possible
+ * a whole row will be returned at one time. But if the buffer offered by
+ * the caller is too small to hold a row sent by the backend, then a partial
+ * data row will be returned. In text mode this can be detected by testing
+ * whether the last returned byte is '\n' or not.
+ *
+ * The returned data is *not* null-terminated.
*/
int
PQgetlineAsync(PGconn *conn, char *buffer, int bufsize)
{
+ char id;
+ int msgLength;
int avail;
if (!conn || conn->asyncStatus != PGASYNC_COPY_OUT)
return -1; /* we are not doing a copy... */
/*
- * Move data from libpq's buffer to the caller's. We want to accept
- * data only in units of whole lines, not partial lines. This ensures
- * that we can recognize the terminator line "\\.\n". (Otherwise, if
- * it happened to cross a packet/buffer boundary, we might hand the
- * first one or two characters off to the caller, which we shouldn't.)
+ * Recognize the next input message. To make life simpler for async
+ * callers, we keep returning 0 until the next message is fully available
+ * even if it is not Copy Data. This should keep PQendcopy from blocking.
*/
-
conn->inCursor = conn->inStart;
+ if (pqGetc(&id, conn))
+ return 0;
+ if (pqGetInt(&msgLength, 4, conn))
+ return 0;
+ avail = conn->inEnd - conn->inCursor;
+ if (avail < msgLength - 4)
+ return 0;
- avail = bufsize;
- while (avail > 0 && conn->inCursor < conn->inEnd)
- {
- char c = conn->inBuffer[conn->inCursor++];
-
- *buffer++ = c;
- --avail;
- if (c == '\n')
- {
- /* Got a complete line; mark the data removed from libpq */
- conn->inStart = conn->inCursor;
- /* Is it the endmarker line? */
- if (bufsize - avail == 3 && buffer[-3] == '\\' && buffer[-2] == '.')
- return -1;
- /* No, return the data line to the caller */
- return bufsize - avail;
- }
- }
+ /*
+ * Cannot proceed unless it's a Copy Data message. Anything else means
+ * end of copy mode.
+ */
+ if (id != 'd')
+ return -1;
/*
- * We don't have a complete line. We'd prefer to leave it in libpq's
- * buffer until the rest arrives, but there is a special case: what if
- * the line is longer than the buffer the caller is offering us? In
- * that case we'd better hand over a partial line, else we'd get into
- * an infinite loop. Do this in a way that ensures we can't
- * misrecognize a terminator line later: leave last 3 characters in
- * libpq buffer.
+ * Move data from libpq's buffer to the caller's. In the case where
+ * a prior call found the caller's buffer too small, we use
+ * conn->copy_already_done to remember how much of the row was already
+ * returned to the caller.
*/
- if (avail == 0 && bufsize > 3)
+ conn->inCursor += conn->copy_already_done;
+ avail = msgLength - 4 - conn->copy_already_done;
+ if (avail <= bufsize)
{
- conn->inStart = conn->inCursor - 3;
- return bufsize - 3;
+ /* Able to consume the whole message */
+ memcpy(buffer, &conn->inBuffer[conn->inCursor], avail);
+ /* Mark message consumed */
+ conn->inStart = conn->inCursor + avail;
+ /* Reset state for next time */
+ conn->copy_already_done = 0;
+ return avail;
+ }
+ else
+ {
+ /* We must return a partial message */
+ memcpy(buffer, &conn->inBuffer[conn->inCursor], bufsize);
+ /* The message is NOT consumed from libpq's buffer */
+ conn->copy_already_done += bufsize;
+ return bufsize;
}
- return 0;
}
/*
@@ -1774,14 +1874,21 @@ PQendcopy(PGconn *conn)
if (pqFlush(conn) && pqIsnonblocking(conn))
return (1);
- /* non blocking connections may have to abort at this point. */
- if (pqIsnonblocking(conn) && PQisBusy(conn))
- return (1);
-
/* Return to active duty */
conn->asyncStatus = PGASYNC_BUSY;
resetPQExpBuffer(&conn->errorMessage);
+ /*
+ * Non blocking connections may have to abort at this point. If everyone
+ * played the game there should be no problem, but in error scenarios
+ * the expected messages may not have arrived yet. (We are assuming that
+ * the backend's packetizing will ensure that CommandComplete arrives
+ * along with the CopyDone; are there corner cases where that doesn't
+ * happen?)
+ */
+ if (pqIsnonblocking(conn) && PQisBusy(conn))
+ return (1);
+
/* Wait for the completion response */
result = PQgetResult(conn);
@@ -1793,26 +1900,16 @@ PQendcopy(PGconn *conn)
}
/*
- * Trouble. The worst case is that we've lost sync with the backend
- * entirely due to application screwup of the copy in/out protocol. To
- * recover, reset the connection (talk about using a sledgehammer...)
+ * Trouble. For backwards-compatibility reasons, we issue the error
+ * message as if it were a notice (would be nice to get rid of this
+ * silliness, but too many apps probably don't handle errors from
+ * PQendcopy reasonably). Note that the app can still obtain the
+ * error status from the PGconn object.
*/
- PQclear(result);
-
if (conn->errorMessage.len > 0)
DONOTICE(conn, conn->errorMessage.data);
- DONOTICE(conn, libpq_gettext("lost synchronization with server, resetting connection\n"));
-
- /*
- * Users doing non-blocking connections need to handle the reset
- * themselves, they'll need to check the connection status if we
- * return an error.
- */
- if (pqIsnonblocking(conn))
- PQresetStart(conn);
- else
- PQreset(conn);
+ PQclear(result);
return 1;
}
@@ -1853,6 +1950,8 @@ PQfn(PGconn *conn,
bool needInput = false;
ExecStatusType status = PGRES_FATAL_ERROR;
char id;
+ int msgLength;
+ int avail;
int i;
*actual_result_len = 0;
@@ -1927,11 +2026,55 @@ PQfn(PGconn *conn,
* Scan the message. If we run out of data, loop around to try
* again.
*/
- conn->inCursor = conn->inStart;
needInput = true;
+ conn->inCursor = conn->inStart;
if (pqGetc(&id, conn))
continue;
+ if (pqGetInt(&msgLength, 4, conn))
+ continue;
+
+ /*
+ * Try to validate message type/length here. A length less than 4
+ * is definitely broken. Large lengths should only be believed
+ * for a few message types.
+ */
+ if (msgLength < 4)
+ {
+ handleSyncLoss(conn, id, msgLength);
+ break;
+ }
+ if (msgLength > 30000 &&
+ !(id == 'T' || id == 'D' || id == 'B' || id == 'd' || id == 'V'))
+ {
+ handleSyncLoss(conn, id, msgLength);
+ break;
+ }
+
+ /*
+ * Can't process if message body isn't all here yet.
+ */
+ msgLength -= 4;
+ avail = conn->inEnd - conn->inCursor;
+ if (avail < msgLength)
+ {
+ /*
+ * Before looping, enlarge the input buffer if needed to hold
+ * the whole message. See notes in parseInput.
+ */
+ if (pqCheckInBufferSpace(conn->inCursor + msgLength, conn))
+ {
+ /*
+ * XXX add some better recovery code... plan is to skip
+ * over the message using its length, then report an error.
+ * For the moment, just treat this like loss of sync (which
+ * indeed it might be!)
+ */
+ handleSyncLoss(conn, id, msgLength);
+ break;
+ }
+ continue;
+ }
/*
* We should see V or E response to the command, but might get N
@@ -1975,7 +2118,7 @@ PQfn(PGconn *conn,
libpq_gettext("protocol error: id=0x%x\n"),
id);
saveErrorResult(conn);
- conn->inStart = conn->inCursor;
+ conn->inStart += 5 + msgLength;
return prepareAsyncResult(conn);
}
break;
@@ -1998,7 +2141,8 @@ PQfn(PGconn *conn,
break;
case 'Z': /* backend is ready for new query */
/* consume the message and exit */
- conn->inStart = conn->inCursor;
+ conn->inStart += 5 + msgLength;
+ /* XXX expect additional fields here */
/* if we saved a result object (probably an error), use it */
if (conn->result)
return prepareAsyncResult(conn);
@@ -2009,11 +2153,13 @@ PQfn(PGconn *conn,
libpq_gettext("protocol error: id=0x%x\n"),
id);
saveErrorResult(conn);
- conn->inStart = conn->inCursor;
+ /* trust the specified message length as what to skip */
+ conn->inStart += 5 + msgLength;
return prepareAsyncResult(conn);
}
/* Completed this message, keep going */
- conn->inStart = conn->inCursor;
+ /* trust the specified message length as what to skip */
+ conn->inStart += 5 + msgLength;
needInput = false;
}
diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c
index dfc46fdf598..76de4a87086 100644
--- a/src/interfaces/libpq/fe-misc.c
+++ b/src/interfaces/libpq/fe-misc.c
@@ -23,7 +23,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.89 2003/04/19 00:02:30 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.90 2003/04/22 00:08:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -277,12 +277,12 @@ pqPutInt(int value, size_t bytes, PGconn *conn)
/*
* Make sure conn's output buffer can hold bytes_needed bytes (caller must
- * include existing outCount into the value!)
+ * include already-stored data into the value!)
*
- * Returns 0 on success, EOF on error
+ * Returns 0 on success, EOF if failed to enlarge buffer
*/
static int
-checkOutBufferSpace(int bytes_needed, PGconn *conn)
+pqCheckOutBufferSpace(int bytes_needed, PGconn *conn)
{
int newsize = conn->outBufSize;
char *newbuf;
@@ -336,6 +336,66 @@ checkOutBufferSpace(int bytes_needed, PGconn *conn)
}
/*
+ * Make sure conn's input buffer can hold bytes_needed bytes (caller must
+ * include already-stored data into the value!)
+ *
+ * Returns 0 on success, EOF if failed to enlarge buffer
+ */
+int
+pqCheckInBufferSpace(int bytes_needed, PGconn *conn)
+{
+ int newsize = conn->inBufSize;
+ 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->inBuffer, newsize);
+ if (newbuf)
+ {
+ /* realloc succeeded */
+ conn->inBuffer = newbuf;
+ conn->inBufSize = newsize;
+ return 0;
+ }
+ }
+
+ newsize = conn->inBufSize;
+ do {
+ newsize += 8192;
+ } while (bytes_needed > newsize && newsize > 0);
+
+ if (bytes_needed <= newsize)
+ {
+ newbuf = realloc(conn->inBuffer, newsize);
+ if (newbuf)
+ {
+ /* realloc succeeded */
+ conn->inBuffer = newbuf;
+ conn->inBufSize = newsize;
+ return 0;
+ }
+ }
+
+ /* realloc failed. Probably out of memory */
+ printfPQExpBuffer(&conn->errorMessage,
+ "cannot allocate memory for input 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
@@ -364,7 +424,7 @@ pqPutMsgStart(char msg_type, PGconn *conn)
else
lenPos = conn->outCount;
/* make sure there is room for it */
- if (checkOutBufferSpace(lenPos + 4, conn))
+ if (pqCheckOutBufferSpace(lenPos + 4, conn))
return EOF;
/* okay, save the message type byte if any */
if (msg_type)
@@ -390,7 +450,7 @@ static int
pqPutMsgBytes(const void *buf, size_t len, PGconn *conn)
{
/* make sure there is room for it */
- if (checkOutBufferSpace(conn->outMsgEnd + len, conn))
+ if (pqCheckOutBufferSpace(conn->outMsgEnd + len, conn))
return EOF;
/* okay, save the data */
memcpy(conn->outBuffer + conn->outMsgEnd, buf, len);
@@ -486,13 +546,13 @@ pqReadData(PGconn *conn)
*/
if (conn->inBufSize - conn->inEnd < 8192)
{
- int newSize = conn->inBufSize * 2;
- char *newBuf = (char *) realloc(conn->inBuffer, newSize);
-
- if (newBuf)
+ if (pqCheckInBufferSpace(conn->inEnd + 8192, conn))
{
- conn->inBuffer = newBuf;
- conn->inBufSize = newSize;
+ /*
+ * We don't insist that the enlarge worked, but we need some room
+ */
+ if (conn->inBufSize - conn->inEnd < 100)
+ return -1; /* errorMessage already set */
}
}
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 8671922547d..35e3208eb0e 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -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.62 2003/04/19 00:02:30 tgl Exp $
+ * $Id: libpq-int.h,v 1.63 2003/04/22 00:08:07 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,101) /* XXX temporary value */
+#define PG_PROTOCOL_LIBPQ PG_PROTOCOL(3,102) /* XXX temporary value */
/*
* POSTGRES backend dependent Constants.
@@ -216,7 +216,8 @@ struct pg_conn
* is listening on; if NULL, uses a
* default constructed from pgport */
char *pgtty; /* tty on which the backend messages is
- * displayed (NOT ACTUALLY USED???) */
+ * displayed (OBSOLETE, NOT USED) */
+ char *connect_timeout; /* connection timeout (numeric string) */
char *pgoptions; /* options to start the backend with */
char *dbName; /* database name */
char *pguser; /* Postgres username and password, if any */
@@ -232,6 +233,10 @@ struct pg_conn
/* Status indicators */
ConnStatusType status;
PGAsyncStatusType asyncStatus;
+ char copy_is_binary; /* 1 = copy binary, 0 = copy text */
+ int copy_already_done; /* # bytes already returned in COPY OUT */
+ int nonblocking; /* whether this connection is using a
+ * blocking socket to the backend or not */
Dllist *notifyList; /* Notify msgs not yet handed to
* application */
@@ -246,6 +251,7 @@ struct pg_conn
int be_key; /* key of backend --- needed for cancels */
char md5Salt[4]; /* password salt received from backend */
char cryptSalt[2]; /* password salt received from backend */
+ int client_encoding; /* encoding id */
PGlobjfuncs *lobjfuncs; /* private state for large-object access
* fns */
@@ -258,9 +264,6 @@ struct pg_conn
int inEnd; /* offset to first position after avail
* data */
- int nonblocking; /* whether this connection is using a
- * blocking socket to the backend or not */
-
/* Buffer for data not yet sent to backend */
char *outBuffer; /* currently allocated buffer */
int outBufSize; /* allocated size of buffer */
@@ -291,10 +294,6 @@ struct pg_conn
/* Buffer for receiving various parts of messages */
PQExpBufferData workBuffer; /* expansible string */
-
- int client_encoding; /* encoding id */
-
- char *connect_timeout;
};
/* String descriptions of the ExecStatusTypes.
@@ -330,6 +329,7 @@ extern void pqClearAsyncResult(PGconn *conn);
* for Get, EOF merely means the buffer is exhausted, not that there is
* necessarily any error.
*/
+extern int pqCheckInBufferSpace(int bytes_needed, PGconn *conn);
extern int pqGetc(char *result, PGconn *conn);
extern int pqPutc(char c, PGconn *conn);
extern int pqGets(PQExpBuffer buf, PGconn *conn);
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 341f4ded1e0..4d1458f8916 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -995,7 +995,6 @@ copy test("........pg.dropped.1........") to stdout;
ERROR: Relation "test" has no column "........pg.dropped.1........"
copy test from stdin;
ERROR: copy: line 1, Extra data after last expected column
-lost synchronization with server, resetting connection
SET autocommit TO 'on';
select * from test;
b | c
diff --git a/src/test/regress/expected/copy2.out b/src/test/regress/expected/copy2.out
index cf28af8c198..983e6bb4a41 100644
--- a/src/test/regress/expected/copy2.out
+++ b/src/test/regress/expected/copy2.out
@@ -35,17 +35,13 @@ ERROR: Attribute "d" specified more than once
-- missing data: should fail
COPY x from stdin;
ERROR: copy: line 1, pg_atoi: zero-length string
-lost synchronization with server, resetting connection
COPY x from stdin;
ERROR: copy: line 1, Missing data for column "e"
-lost synchronization with server, resetting connection
COPY x from stdin;
ERROR: copy: line 1, Missing data for column "e"
-lost synchronization with server, resetting connection
-- extra data: should fail
COPY x from stdin;
ERROR: copy: line 1, Extra data after last expected column
-lost synchronization with server, resetting connection
SET autocommit TO 'on';
-- various COPY options: delimiters, oids, NULL string
COPY x (b, c, d, e) from stdin with oids delimiter ',' null 'x';
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index 1aaa4a85ef4..13eb14cfa2f 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -40,7 +40,6 @@ INSERT INTO basictest values ('88', 'haha', 'short', '123.1212'); -- Truncate
-- Test copy
COPY basictest (testvarchar) FROM stdin; -- fail
ERROR: copy: line 1, value too long for type character varying(5)
-lost synchronization with server, resetting connection
SET autocommit TO 'on';
COPY basictest (testvarchar) FROM stdin;
select * from basictest;
@@ -128,12 +127,10 @@ INSERT INTO nulltest values ('a', 'b', 'c', NULL, 'd'); -- Good
-- Test copy
COPY nulltest FROM stdin; --fail
ERROR: copy: line 1, Domain dcheck does not allow NULL values
-lost synchronization with server, resetting connection
SET autocommit TO 'on';
-- Last row is bad
COPY nulltest FROM stdin;
ERROR: copy: line 3, CopyFrom: rejected due to CHECK constraint "nulltest_col5" on "nulltest"
-lost synchronization with server, resetting connection
select * from nulltest;
col1 | col2 | col3 | col4 | col5
------+------+------+------+------