From b497f65714d5c6a96b4a859ffbf2637cb3c9fb82 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Thu, 4 Feb 2021 12:31:34 +0200
Subject: [PATCH 1/2] Remove server and libpq support for old FE/BE protocol
 version 2.

Protocol version 3 was introduced in PostgreSQL 7.4. There shouldn't be
many clients or servers left out there without version 3 support. But as
a courtesy, I kept just enough of the old protocol support that we can
still send the "unsupported protocol version" error in v2 format, so that
old clients can display the message properly. Likewise, libpq still
understands v2 ErrorResponse messages when establishing connection.

The impetus to do this now is that I'm working on a patch to change the
way the encoding conversion performed in COPY FROM, so that we convert
the data in larger chunks, before scanning the input for line boundaries.
We cannot do that easily with the old protocol, because it requires
parsing the input one byte at a time to detect the end-of-copy marker.

Discussion: https://www.postgresql.org/message-id/9ec25819-0a8a-d51a-17dc-4150bb3cca3b%40iki.fi
---
 doc/src/sgml/libpq.sgml                  |   79 +-
 src/backend/access/common/printtup.c     |  248 +---
 src/backend/commands/async.c             |    3 +-
 src/backend/commands/copyfrom.c          |    7 -
 src/backend/commands/copyfromparse.c     |   83 +-
 src/backend/commands/copyto.c            |  127 +-
 src/backend/libpq/auth.c                 |   80 +-
 src/backend/libpq/pqcomm.c               |  215 +--
 src/backend/libpq/pqmq.c                 |   18 +-
 src/backend/postmaster/postmaster.c      |   42 +-
 src/backend/tcop/dest.c                  |   22 +-
 src/backend/tcop/fastpath.c              |  165 +--
 src/backend/tcop/postgres.c              |   71 +-
 src/backend/utils/error/elog.c           |   64 +-
 src/backend/utils/misc/guc.c             |    6 +-
 src/bin/psql/common.c                    |   10 -
 src/bin/psql/copy.c                      |    4 +-
 src/include/commands/copyfrom_internal.h |    3 +-
 src/include/libpq/libpq.h                |    7 +-
 src/include/libpq/pqcomm.h               |   33 +-
 src/include/tcop/fastpath.h              |    1 -
 src/include/utils/elog.h                 |    2 -
 src/interfaces/libpq/Makefile            |    1 -
 src/interfaces/libpq/fe-auth.c           |    8 +-
 src/interfaces/libpq/fe-connect.c        |  148 +-
 src/interfaces/libpq/fe-exec.c           |  231 +---
 src/interfaces/libpq/fe-misc.c           |   21 +-
 src/interfaces/libpq/fe-protocol2.c      | 1610 ----------------------
 src/interfaces/libpq/fe-protocol3.c      |    6 +-
 src/interfaces/libpq/libpq-fe.h          |    1 -
 src/interfaces/libpq/libpq-int.h         |   35 +-
 31 files changed, 280 insertions(+), 3071 deletions(-)
 delete mode 100644 src/interfaces/libpq/fe-protocol2.c

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index b7a82453f0d..262850bc2cf 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -2174,20 +2174,6 @@ const char *PQparameterStatus(const PGconn *conn, const char *paramName);
        cannot change after startup.
       </para>
 
-      <para>
-       Pre-3.0-protocol servers do not report parameter settings, but
-       <application>libpq</application> includes logic to obtain values for
-       <varname>server_version</varname> and <varname>client_encoding</varname> anyway.
-       Applications are encouraged to use <xref linkend="libpq-PQparameterStatus"/>
-       rather than <foreignphrase>ad hoc</foreignphrase> code to determine these values.
-       (Beware however that on a pre-3.0 connection, changing
-       <varname>client_encoding</varname> via <command>SET</command> after connection
-       startup will not be reflected by <xref linkend="libpq-PQparameterStatus"/>.)
-       For <varname>server_version</varname>, see also
-       <xref linkend="libpq-PQserverVersion"/>, which returns the information in a
-       numeric form that is much easier to compare against.
-      </para>
-
       <para>
        If no value for <varname>standard_conforming_strings</varname> is reported,
        applications can assume it is <literal>off</literal>, that is, backslashes
@@ -2214,15 +2200,12 @@ const char *PQparameterStatus(const PGconn *conn, const char *paramName);
 int PQprotocolVersion(const PGconn *conn);
 </synopsis>
        Applications might wish to use this function to determine whether certain
-       features are supported.  Currently, the possible values are 2 (2.0
-       protocol), 3 (3.0 protocol), or zero (connection bad).  The
-       protocol version will
+       features are supported.  Currently, the possible values are 3
+       (3.0 protocol), or zero (connection bad).  The protocol version will
        not change after connection startup is complete, but it could
-       theoretically change during a connection reset.  The 3.0 protocol
-       will normally be used when communicating with
-       <productname>PostgreSQL</productname> 7.4 or later servers; pre-7.4 servers
-       support only protocol 2.0.  (Protocol 1.0 is obsolete and not
-       supported by <application>libpq</application>.)
+       theoretically change during a connection reset.  The 3.0 protocol is
+       supported by <productname>PostgreSQL</productname> server versions 7.4
+       and above.
       </para>
      </listitem>
     </varlistentry>
@@ -2639,8 +2622,7 @@ PGresult *PQexecParams(PGconn *conn,
         <xref linkend="libpq-PQexecParams"/> is like <xref linkend="libpq-PQexec"/>, but offers additional
         functionality: parameter values can be specified separately from the command
         string proper, and query results can be requested in either text or binary
-        format.  <xref linkend="libpq-PQexecParams"/> is supported only in protocol 3.0 and later
-        connections; it will fail when using protocol 2.0.
+        format.
        </para>
 
        <para>
@@ -2817,8 +2799,6 @@ PGresult *PQprepare(PGconn *conn,
         execution with <xref linkend="libpq-PQexecPrepared"/>.  This feature allows
         commands to be executed repeatedly without being parsed and
         planned each time;  see <xref linkend="sql-prepare"/> for details.
-        <xref linkend="libpq-PQprepare"/> is supported only in protocol 3.0 and later
-        connections; it will fail when using protocol 2.0.
        </para>
 
        <para>
@@ -2892,9 +2872,7 @@ PGresult *PQexecPrepared(PGconn *conn,
         This feature allows commands that will be used repeatedly to be
         parsed and planned just once, rather than each time they are
         executed.  The statement must have been prepared previously in
-        the current session.  <xref linkend="libpq-PQexecPrepared"/> is supported
-        only in protocol 3.0 and later connections; it will fail when
-        using protocol 2.0.
+        the current session.
        </para>
 
        <para>
@@ -2921,8 +2899,6 @@ PGresult *PQdescribePrepared(PGconn *conn, const char *stmtName);
        <para>
         <xref linkend="libpq-PQdescribePrepared"/> allows an application to obtain
         information about a previously prepared statement.
-        <xref linkend="libpq-PQdescribePrepared"/> is supported only in protocol 3.0
-        and later connections; it will fail when using protocol 2.0.
        </para>
 
        <para>
@@ -2959,8 +2935,6 @@ PGresult *PQdescribePortal(PGconn *conn, const char *portalName);
         (<application>libpq</application> does not provide any direct access to
         portals, but you can use this function to inspect the properties
         of a cursor created with a <command>DECLARE CURSOR</command> SQL command.)
-        <xref linkend="libpq-PQdescribePortal"/> is supported only in protocol 3.0
-        and later connections; it will fail when using protocol 2.0.
        </para>
 
        <para>
@@ -3466,8 +3440,6 @@ char *PQresultErrorField(const PGresult *res, int fieldcode);
        <para>
         Errors generated internally by <application>libpq</application> will
         have severity and primary message, but typically no other fields.
-        Errors returned by a pre-3.0-protocol server will include severity and
-        primary message, and sometimes a detail message, but no other fields.
        </para>
 
        <para>
@@ -3628,8 +3600,7 @@ Oid PQftable(const PGresult *res,
 
       <para>
        <literal>InvalidOid</literal> is returned if the column number is out of range,
-       or if the specified column is not a simple reference to a table column,
-       or when using pre-3.0 protocol.
+       or if the specified column is not a simple reference to a table column.
        You can query the system table <literal>pg_class</literal> to determine
        exactly which table is referenced.
       </para>
@@ -3659,8 +3630,7 @@ int PQftablecol(const PGresult *res,
 
       <para>
        Zero is returned if the column number is out of range, or if the
-       specified column is not a simple reference to a table column, or
-       when using pre-3.0 protocol.
+       specified column is not a simple reference to a table column.
       </para>
      </listitem>
     </varlistentry>
@@ -4493,8 +4463,8 @@ int PQsendQueryParams(PGconn *conn,
        query parameters can be specified separately from the query string.
        The function's parameters are handled identically to
        <xref linkend="libpq-PQexecParams"/>.  Like
-       <xref linkend="libpq-PQexecParams"/>, it will not work on 2.0-protocol
-       connections, and it allows only one command in the query string.
+       <xref linkend="libpq-PQexecParams"/>, it allows only one command in the
+       query string.
       </para>
      </listitem>
     </varlistentry>
@@ -4519,9 +4489,7 @@ int PQsendPrepare(PGconn *conn,
        After a successful call, call <xref linkend="libpq-PQgetResult"/> to
        determine whether the server successfully created the prepared
        statement.  The function's parameters are handled identically to
-       <xref linkend="libpq-PQprepare"/>.  Like
-       <xref linkend="libpq-PQprepare"/>, it will not work on 2.0-protocol
-       connections.
+       <xref linkend="libpq-PQprepare"/>.
       </para>
      </listitem>
     </varlistentry>
@@ -4547,9 +4515,7 @@ int PQsendQueryPrepared(PGconn *conn,
        the command to be executed is specified by naming a
        previously-prepared statement, instead of giving a query string.
        The function's parameters are handled identically to
-       <xref linkend="libpq-PQexecPrepared"/>.  Like
-       <xref linkend="libpq-PQexecPrepared"/>, it will not work on
-       2.0-protocol connections.
+       <xref linkend="libpq-PQexecPrepared"/>.
       </para>
      </listitem>
     </varlistentry>
@@ -4569,9 +4535,7 @@ int PQsendDescribePrepared(PGconn *conn, const char *stmtName);
        it returns 1 if it was able to dispatch the request, and 0 if not.
        After a successful call, call <xref linkend="libpq-PQgetResult"/> to
        obtain the results.  The function's parameters are handled
-       identically to <xref linkend="libpq-PQdescribePrepared"/>.  Like
-       <xref linkend="libpq-PQdescribePrepared"/>, it will not work on
-       2.0-protocol connections.
+       identically to <xref linkend="libpq-PQdescribePrepared"/>.
       </para>
      </listitem>
     </varlistentry>
@@ -4591,9 +4555,7 @@ int PQsendDescribePortal(PGconn *conn, const char *portalName);
        it returns 1 if it was able to dispatch the request, and 0 if not.
        After a successful call, call <xref linkend="libpq-PQgetResult"/> to
        obtain the results.  The function's parameters are handled
-       identically to <xref linkend="libpq-PQdescribePortal"/>.  Like
-       <xref linkend="libpq-PQdescribePortal"/>, it will not work on
-       2.0-protocol connections.
+       identically to <xref linkend="libpq-PQdescribePortal"/>.
       </para>
      </listitem>
     </varlistentry>
@@ -5360,13 +5322,6 @@ typedef struct pgNotify
    </variablelist>
   </para>
 
-  <note>
-   <para>
-    These additional data values are only available when using protocol
-    3.0.  When using protocol 2.0, all these functions will return 0.
-   </para>
-  </note>
-
   <sect2 id="libpq-copy-send">
    <title>Functions for Sending <command>COPY</command> Data</title>
 
@@ -5431,9 +5386,7 @@ int PQputCopyEnd(PGconn *conn,
        <parameter>errormsg</parameter> used as the error message.  (One should not
        assume that this exact error message will come back from the server,
        however, as the server might have already failed the
-       <command>COPY</command> for its own reasons.  Also note that the option
-       to force failure does not work when using pre-3.0-protocol
-       connections.)
+       <command>COPY</command> for its own reasons.)
       </para>
 
       <para>
diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c
index 4468480e9b3..54b539f6fb6 100644
--- a/src/backend/access/common/printtup.c
+++ b/src/backend/access/common/printtup.c
@@ -27,16 +27,9 @@
 static void printtup_startup(DestReceiver *self, int operation,
 							 TupleDesc typeinfo);
 static bool printtup(TupleTableSlot *slot, DestReceiver *self);
-static bool printtup_20(TupleTableSlot *slot, DestReceiver *self);
-static bool printtup_internal_20(TupleTableSlot *slot, DestReceiver *self);
 static void printtup_shutdown(DestReceiver *self);
 static void printtup_destroy(DestReceiver *self);
 
-static void SendRowDescriptionCols_2(StringInfo buf, TupleDesc typeinfo,
-									 List *targetlist, int16 *formats);
-static void SendRowDescriptionCols_3(StringInfo buf, TupleDesc typeinfo,
-									 List *targetlist, int16 *formats);
-
 /* ----------------------------------------------------------------
  *		printtup / debugtup support
  * ----------------------------------------------------------------
@@ -112,19 +105,6 @@ SetRemoteDestReceiverParams(DestReceiver *self, Portal portal)
 		   myState->pub.mydest == DestRemoteExecute);
 
 	myState->portal = portal;
-
-	if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
-	{
-		/*
-		 * In protocol 2.0 the Bind message does not exist, so there is no way
-		 * for the columns to have different print formats; it's sufficient to
-		 * look at the first one.
-		 */
-		if (portal->formats && portal->formats[0] != 0)
-			myState->pub.receiveSlot = printtup_internal_20;
-		else
-			myState->pub.receiveSlot = printtup_20;
-	}
 }
 
 static void
@@ -149,21 +129,6 @@ printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 												"printtup",
 												ALLOCSET_DEFAULT_SIZES);
 
-	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.
-		 */
-		const char *portalName = portal->name;
-
-		if (portalName == NULL || portalName[0] == '\0')
-			portalName = "blank";
-
-		pq_puttextmessage('P', portalName);
-	}
-
 	/*
 	 * If we are supposed to emit row descriptions, then send the tuple
 	 * descriptor of the tuples.
@@ -202,31 +167,14 @@ SendRowDescriptionMessage(StringInfo buf, TupleDesc typeinfo,
 						  List *targetlist, int16 *formats)
 {
 	int			natts = typeinfo->natts;
-	int			proto = PG_PROTOCOL_MAJOR(FrontendProtocol);
+	int			i;
+	ListCell   *tlist_item = list_head(targetlist);
 
 	/* tuple descriptor message type */
 	pq_beginmessage_reuse(buf, 'T');
 	/* # of attrs in tuples */
 	pq_sendint16(buf, natts);
 
-	if (proto >= 3)
-		SendRowDescriptionCols_3(buf, typeinfo, targetlist, formats);
-	else
-		SendRowDescriptionCols_2(buf, typeinfo, targetlist, formats);
-
-	pq_endmessage_reuse(buf);
-}
-
-/*
- * Send description for each column when using v3+ protocol
- */
-static void
-SendRowDescriptionCols_3(StringInfo buf, TupleDesc typeinfo, List *targetlist, int16 *formats)
-{
-	int			natts = typeinfo->natts;
-	int			i;
-	ListCell   *tlist_item = list_head(targetlist);
-
 	/*
 	 * Preallocate memory for the entire message to be sent. That allows to
 	 * use the significantly faster inline pqformat.h functions and to avoid
@@ -291,33 +239,8 @@ SendRowDescriptionCols_3(StringInfo buf, TupleDesc typeinfo, List *targetlist, i
 		pq_writeint32(buf, atttypmod);
 		pq_writeint16(buf, format);
 	}
-}
-
-/*
- * Send description for each column when using v2 protocol
- */
-static void
-SendRowDescriptionCols_2(StringInfo buf, TupleDesc typeinfo, List *targetlist, int16 *formats)
-{
-	int			natts = typeinfo->natts;
-	int			i;
-
-	for (i = 0; i < natts; ++i)
-	{
-		Form_pg_attribute att = TupleDescAttr(typeinfo, i);
-		Oid			atttypid = att->atttypid;
-		int32		atttypmod = att->atttypmod;
 
-		/* If column is a domain, send the base type and typmod instead */
-		atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod);
-
-		pq_sendstring(buf, NameStr(att->attname));
-		/* column ID only info appears in protocol 3.0 and up */
-		pq_sendint32(buf, atttypid);
-		pq_sendint16(buf, att->attlen);
-		pq_sendint32(buf, atttypmod);
-		/* format info only appears in protocol 3.0 and up */
-	}
+	pq_endmessage_reuse(buf);
 }
 
 /*
@@ -371,7 +294,7 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
 }
 
 /* ----------------
- *		printtup --- print a tuple in protocol 3.0
+ *		printtup --- send a tuple to the client
  * ----------------
  */
 static bool
@@ -455,84 +378,6 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
 	return true;
 }
 
-/* ----------------
- *		printtup_20 --- print a tuple in protocol 2.0
- * ----------------
- */
-static bool
-printtup_20(TupleTableSlot *slot, DestReceiver *self)
-{
-	TupleDesc	typeinfo = slot->tts_tupleDescriptor;
-	DR_printtup *myState = (DR_printtup *) self;
-	MemoryContext oldcontext;
-	StringInfo	buf = &myState->buf;
-	int			natts = typeinfo->natts;
-	int			i,
-				j,
-				k;
-
-	/* Set or update my derived attribute info, if needed */
-	if (myState->attrinfo != typeinfo || myState->nattrs != natts)
-		printtup_prepare_info(myState, typeinfo, natts);
-
-	/* Make sure the tuple is fully deconstructed */
-	slot_getallattrs(slot);
-
-	/* Switch into per-row context so we can recover memory below */
-	oldcontext = MemoryContextSwitchTo(myState->tmpcontext);
-
-	/*
-	 * tell the frontend to expect new tuple data (in ASCII style)
-	 */
-	pq_beginmessage_reuse(buf, 'D');
-
-	/*
-	 * send a bitmap of which attributes are not null
-	 */
-	j = 0;
-	k = 1 << 7;
-	for (i = 0; i < natts; ++i)
-	{
-		if (!slot->tts_isnull[i])
-			j |= k;				/* set bit if not null */
-		k >>= 1;
-		if (k == 0)				/* end of byte? */
-		{
-			pq_sendint8(buf, j);
-			j = 0;
-			k = 1 << 7;
-		}
-	}
-	if (k != (1 << 7))			/* flush last partial byte */
-		pq_sendint8(buf, j);
-
-	/*
-	 * send the attributes of this tuple
-	 */
-	for (i = 0; i < natts; ++i)
-	{
-		PrinttupAttrInfo *thisState = myState->myinfo + i;
-		Datum		attr = slot->tts_values[i];
-		char	   *outputstr;
-
-		if (slot->tts_isnull[i])
-			continue;
-
-		Assert(thisState->format == 0);
-
-		outputstr = OutputFunctionCall(&thisState->finfo, attr);
-		pq_sendcountedtext(buf, outputstr, strlen(outputstr), true);
-	}
-
-	pq_endmessage_reuse(buf);
-
-	/* Return to caller's context, and flush row's temporary memory */
-	MemoryContextSwitchTo(oldcontext);
-	MemoryContextReset(myState->tmpcontext);
-
-	return true;
-}
-
 /* ----------------
  *		printtup_shutdown
  * ----------------
@@ -638,88 +483,3 @@ debugtup(TupleTableSlot *slot, DestReceiver *self)
 
 	return true;
 }
-
-/* ----------------
- *		printtup_internal_20 --- print a binary tuple in protocol 2.0
- *
- * We use a different message type, i.e. 'B' instead of 'D' to
- * indicate a tuple in internal (binary) form.
- *
- * This is largely same as printtup_20, except we use binary formatting.
- * ----------------
- */
-static bool
-printtup_internal_20(TupleTableSlot *slot, DestReceiver *self)
-{
-	TupleDesc	typeinfo = slot->tts_tupleDescriptor;
-	DR_printtup *myState = (DR_printtup *) self;
-	MemoryContext oldcontext;
-	StringInfo	buf = &myState->buf;
-	int			natts = typeinfo->natts;
-	int			i,
-				j,
-				k;
-
-	/* Set or update my derived attribute info, if needed */
-	if (myState->attrinfo != typeinfo || myState->nattrs != natts)
-		printtup_prepare_info(myState, typeinfo, natts);
-
-	/* Make sure the tuple is fully deconstructed */
-	slot_getallattrs(slot);
-
-	/* Switch into per-row context so we can recover memory below */
-	oldcontext = MemoryContextSwitchTo(myState->tmpcontext);
-
-	/*
-	 * tell the frontend to expect new tuple data (in binary style)
-	 */
-	pq_beginmessage_reuse(buf, 'B');
-
-	/*
-	 * send a bitmap of which attributes are not null
-	 */
-	j = 0;
-	k = 1 << 7;
-	for (i = 0; i < natts; ++i)
-	{
-		if (!slot->tts_isnull[i])
-			j |= k;				/* set bit if not null */
-		k >>= 1;
-		if (k == 0)				/* end of byte? */
-		{
-			pq_sendint8(buf, j);
-			j = 0;
-			k = 1 << 7;
-		}
-	}
-	if (k != (1 << 7))			/* flush last partial byte */
-		pq_sendint8(buf, j);
-
-	/*
-	 * send the attributes of this tuple
-	 */
-	for (i = 0; i < natts; ++i)
-	{
-		PrinttupAttrInfo *thisState = myState->myinfo + i;
-		Datum		attr = slot->tts_values[i];
-		bytea	   *outputbytes;
-
-		if (slot->tts_isnull[i])
-			continue;
-
-		Assert(thisState->format == 1);
-
-		outputbytes = SendFunctionCall(&thisState->finfo, attr);
-		pq_sendint32(buf, VARSIZE(outputbytes) - VARHDRSZ);
-		pq_sendbytes(buf, VARDATA(outputbytes),
-					 VARSIZE(outputbytes) - VARHDRSZ);
-	}
-
-	pq_endmessage_reuse(buf);
-
-	/* Return to caller's context, and flush row's temporary memory */
-	MemoryContextSwitchTo(oldcontext);
-	MemoryContextReset(myState->tmpcontext);
-
-	return true;
-}
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index 42b232d98b1..4b16fb56825 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -2311,8 +2311,7 @@ NotifyMyFrontEnd(const char *channel, const char *payload, int32 srcPid)
 		pq_beginmessage(&buf, 'A');
 		pq_sendint32(&buf, srcPid);
 		pq_sendstring(&buf, channel);
-		if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
-			pq_sendstring(&buf, payload);
+		pq_sendstring(&buf, payload);
 		pq_endmessage(&buf);
 
 		/*
diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c
index c39cc736ed2..6d43d056cca 100644
--- a/src/backend/commands/copyfrom.c
+++ b/src/backend/commands/copyfrom.c
@@ -1125,13 +1125,6 @@ CopyFrom(CopyFromState cstate)
 
 	MemoryContextSwitchTo(oldcontext);
 
-	/*
-	 * In the old protocol, tell pqcomm that we can process normal protocol
-	 * messages again.
-	 */
-	if (cstate->copy_src == COPY_OLD_FE)
-		pq_endmsgread();
-
 	/* Execute AFTER STATEMENT insertion triggers */
 	ExecASInsertTriggers(estate, target_resultRelInfo, cstate->transition_capture);
 
diff --git a/src/backend/commands/copyfromparse.c b/src/backend/commands/copyfromparse.c
index 4c74067f849..dd629668540 100644
--- a/src/backend/commands/copyfromparse.c
+++ b/src/backend/commands/copyfromparse.c
@@ -124,35 +124,19 @@ static int	CopyReadBinaryData(CopyFromState cstate, char *dest, int nbytes);
 void
 ReceiveCopyBegin(CopyFromState cstate)
 {
-	if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
-	{
-		/* new way */
-		StringInfoData buf;
-		int			natts = list_length(cstate->attnumlist);
-		int16		format = (cstate->opts.binary ? 1 : 0);
-		int			i;
-
-		pq_beginmessage(&buf, 'G');
-		pq_sendbyte(&buf, format);	/* overall format */
-		pq_sendint16(&buf, natts);
-		for (i = 0; i < natts; i++)
-			pq_sendint16(&buf, format); /* per-column formats */
-		pq_endmessage(&buf);
-		cstate->copy_src = COPY_NEW_FE;
-		cstate->fe_msgbuf = makeStringInfo();
-	}
-	else
-	{
-		/* old way */
-		if (cstate->opts.binary)
-			ereport(ERROR,
-					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-					 errmsg("COPY BINARY is not supported to stdout or from stdin")));
-		pq_putemptymessage('G');
-		/* any error in old protocol will make us lose sync */
-		pq_startmsgread();
-		cstate->copy_src = COPY_OLD_FE;
-	}
+	StringInfoData buf;
+	int			natts = list_length(cstate->attnumlist);
+	int16		format = (cstate->opts.binary ? 1 : 0);
+	int			i;
+
+	pq_beginmessage(&buf, 'G');
+	pq_sendbyte(&buf, format);	/* overall format */
+	pq_sendint16(&buf, natts);
+	for (i = 0; i < natts; i++)
+		pq_sendint16(&buf, format); /* per-column formats */
+	pq_endmessage(&buf);
+	cstate->copy_src = COPY_FRONTEND;
+	cstate->fe_msgbuf = makeStringInfo();
 	/* We *must* flush here to ensure FE knows it can send. */
 	pq_flush();
 }
@@ -228,25 +212,7 @@ CopyGetData(CopyFromState cstate, void *databuf, int minread, int maxread)
 			if (bytesread == 0)
 				cstate->reached_eof = true;
 			break;
-		case COPY_OLD_FE:
-
-			/*
-			 * We cannot read more than minread bytes (which in practice is 1)
-			 * because old protocol doesn't have any clear way of separating
-			 * the COPY stream from following data.  This is slow, but not any
-			 * slower than the code path was originally, and we don't care
-			 * much anymore about the performance of old protocol.
-			 */
-			if (pq_getbytes((char *) databuf, minread))
-			{
-				/* Only a \. terminator is legal EOF in old protocol */
-				ereport(ERROR,
-						(errcode(ERRCODE_CONNECTION_FAILURE),
-						 errmsg("unexpected EOF on client connection with an open transaction")));
-			}
-			bytesread = minread;
-			break;
-		case COPY_NEW_FE:
+		case COPY_FRONTEND:
 			while (maxread > 0 && bytesread < minread && !cstate->reached_eof)
 			{
 				int			avail;
@@ -619,21 +585,16 @@ NextCopyFrom(CopyFromState cstate, ExprContext *econtext,
 		if (fld_count == -1)
 		{
 			/*
-			 * Received EOF marker.  In a V3-protocol copy, wait for the
-			 * protocol-level EOF, and complain if it doesn't come
-			 * immediately.  This ensures that we correctly handle CopyFail,
-			 * if client chooses to send that now.
-			 *
-			 * Note that we MUST NOT try to read more data in an old-protocol
-			 * copy, since there is no protocol-level EOF marker then.  We
-			 * could go either way for copy from file, but choose to throw
-			 * error if there's data after the EOF marker, for consistency
-			 * with the new-protocol case.
+			 * Received EOF marker.  Wait for the protocol-level EOF, and
+			 * complain if it doesn't come immediately.  In COPY FROM STDIN,
+			 * this ensures that we correctly handle CopyFail, if client
+			 * chooses to send that now.  When copying from file, we could
+			 * ignore the rest of the file like in text mode, but we choose to
+			 * be consistent with the COPY FROM STDIN case.
 			 */
 			char		dummy;
 
-			if (cstate->copy_src != COPY_OLD_FE &&
-				CopyReadBinaryData(cstate, &dummy, 1) > 0)
+			if (CopyReadBinaryData(cstate, &dummy, 1) > 0)
 				ereport(ERROR,
 						(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
 						 errmsg("received copy data after EOF marker")));
@@ -712,7 +673,7 @@ CopyReadLine(CopyFromState cstate)
 		 * after \. up to the protocol end of copy data.  (XXX maybe better
 		 * not to treat \. as special?)
 		 */
-		if (cstate->copy_src == COPY_NEW_FE)
+		if (cstate->copy_src == COPY_FRONTEND)
 		{
 			do
 			{
diff --git a/src/backend/commands/copyto.c b/src/backend/commands/copyto.c
index e04ec1e331b..46155015cfd 100644
--- a/src/backend/commands/copyto.c
+++ b/src/backend/commands/copyto.c
@@ -50,8 +50,7 @@
 typedef enum CopyDest
 {
 	COPY_FILE,					/* to file (or a piped program) */
-	COPY_OLD_FE,				/* to frontend (2.0 protocol) */
-	COPY_NEW_FE,				/* to frontend (3.0 protocol) */
+	COPY_FRONTEND,				/* to frontend */
 } CopyDest;
 
 /*
@@ -116,7 +115,6 @@ static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
 /* non-export function prototypes */
 static void EndCopy(CopyToState cstate);
 static void ClosePipeToProgram(CopyToState cstate);
-static uint64 CopyTo(CopyToState cstate);
 static void CopyOneRowTo(CopyToState cstate, TupleTableSlot *slot);
 static void CopyAttributeOutText(CopyToState cstate, char *string);
 static void CopyAttributeOutCSV(CopyToState cstate, char *string,
@@ -140,53 +138,27 @@ static void CopySendInt16(CopyToState cstate, int16 val);
 static void
 SendCopyBegin(CopyToState cstate)
 {
-	if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
-	{
-		/* new way */
-		StringInfoData buf;
-		int			natts = list_length(cstate->attnumlist);
-		int16		format = (cstate->opts.binary ? 1 : 0);
-		int			i;
-
-		pq_beginmessage(&buf, 'H');
-		pq_sendbyte(&buf, format);	/* overall format */
-		pq_sendint16(&buf, natts);
-		for (i = 0; i < natts; i++)
-			pq_sendint16(&buf, format); /* per-column formats */
-		pq_endmessage(&buf);
-		cstate->copy_dest = COPY_NEW_FE;
-	}
-	else
-	{
-		/* old way */
-		if (cstate->opts.binary)
-			ereport(ERROR,
-					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-					 errmsg("COPY BINARY is not supported to stdout or from stdin")));
-		pq_putemptymessage('H');
-		/* grottiness needed for old COPY OUT protocol */
-		pq_startcopyout();
-		cstate->copy_dest = COPY_OLD_FE;
-	}
+	StringInfoData buf;
+	int			natts = list_length(cstate->attnumlist);
+	int16		format = (cstate->opts.binary ? 1 : 0);
+	int			i;
+
+	pq_beginmessage(&buf, 'H');
+	pq_sendbyte(&buf, format);	/* overall format */
+	pq_sendint16(&buf, natts);
+	for (i = 0; i < natts; i++)
+		pq_sendint16(&buf, format); /* per-column formats */
+	pq_endmessage(&buf);
+	cstate->copy_dest = COPY_FRONTEND;
 }
 
 static void
 SendCopyEnd(CopyToState cstate)
 {
-	if (cstate->copy_dest == COPY_NEW_FE)
-	{
-		/* Shouldn't have any unsent data */
-		Assert(cstate->fe_msgbuf->len == 0);
-		/* Send Copy Done message */
-		pq_putemptymessage('c');
-	}
-	else
-	{
-		CopySendData(cstate, "\\.", 2);
-		/* Need to flush out the trailer (this also appends a newline) */
-		CopySendEndOfRow(cstate);
-		pq_endcopyout(false);
-	}
+	/* Shouldn't have any unsent data */
+	Assert(cstate->fe_msgbuf->len == 0);
+	/* Send Copy Done message */
+	pq_putemptymessage('c');
 }
 
 /*----------
@@ -268,20 +240,7 @@ CopySendEndOfRow(CopyToState cstate)
 							 errmsg("could not write to COPY file: %m")));
 			}
 			break;
-		case COPY_OLD_FE:
-			/* The FE/BE protocol uses \n as newline for all platforms */
-			if (!cstate->opts.binary)
-				CopySendChar(cstate, '\n');
-
-			if (pq_putbytes(fe_msgbuf->data, fe_msgbuf->len))
-			{
-				/* no hope of recovering connection sync, so FATAL */
-				ereport(FATAL,
-						(errcode(ERRCODE_CONNECTION_FAILURE),
-						 errmsg("connection lost during COPY to stdout")));
-			}
-			break;
-		case COPY_NEW_FE:
+		case COPY_FRONTEND:
 			/* The FE/BE protocol uses \n as newline for all platforms */
 			if (!cstate->opts.binary)
 				CopySendChar(cstate, '\n');
@@ -779,42 +738,6 @@ BeginCopyTo(ParseState *pstate,
 	return cstate;
 }
 
-/*
- * This intermediate routine exists mainly to localize the effects of setjmp
- * so we don't need to plaster a lot of variables with "volatile".
- */
-uint64
-DoCopyTo(CopyToState cstate)
-{
-	bool		pipe = (cstate->filename == NULL);
-	bool		fe_copy = (pipe && whereToSendOutput == DestRemote);
-	uint64		processed;
-
-	PG_TRY();
-	{
-		if (fe_copy)
-			SendCopyBegin(cstate);
-
-		processed = CopyTo(cstate);
-
-		if (fe_copy)
-			SendCopyEnd(cstate);
-	}
-	PG_CATCH();
-	{
-		/*
-		 * Make sure we turn off old-style COPY OUT mode upon error. It is
-		 * okay to do this in all cases, since it does nothing if the mode is
-		 * not on.
-		 */
-		pq_endcopyout(true);
-		PG_RE_THROW();
-	}
-	PG_END_TRY();
-
-	return processed;
-}
-
 /*
  * Clean up storage and release resources for COPY TO.
  */
@@ -837,14 +760,19 @@ EndCopyTo(CopyToState cstate)
 /*
  * Copy from relation or query TO file.
  */
-static uint64
-CopyTo(CopyToState cstate)
+uint64
+DoCopyTo(CopyToState cstate)
 {
+	bool		pipe = (cstate->filename == NULL);
+	bool		fe_copy = (pipe && whereToSendOutput == DestRemote);
 	TupleDesc	tupDesc;
 	int			num_phys_attrs;
 	ListCell   *cur;
 	uint64		processed;
 
+	if (fe_copy)
+		SendCopyBegin(cstate);
+
 	if (cstate->rel)
 		tupDesc = RelationGetDescr(cstate->rel);
 	else
@@ -977,11 +905,14 @@ CopyTo(CopyToState cstate)
 
 	MemoryContextDelete(cstate->rowcontext);
 
+	if (fe_copy)
+		SendCopyEnd(cstate);
+
 	return processed;
 }
 
 /*
- * Emit one row during CopyTo().
+ * Emit one row during DoCopyTo().
  */
 static void
 CopyOneRowTo(CopyToState cstate, TupleTableSlot *slot)
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 545635f41a9..127f7ea3442 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -653,35 +653,26 @@ static char *
 recv_password_packet(Port *port)
 {
 	StringInfoData buf;
+	int			mtype;
 
 	pq_startmsgread();
-	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)
-				ereport(ERROR,
-						(errcode(ERRCODE_PROTOCOL_VIOLATION),
-						 errmsg("expected password response, got message type %d",
-								mtype)));
-			return NULL;		/* EOF or bad message type */
-		}
-	}
-	else
+	/* Expect 'p' message type */
+	mtype = pq_getbyte();
+	if (mtype != 'p')
 	{
-		/* For pre-3.0 clients, avoid log entry if they just disconnect */
-		if (pq_peekbyte() == EOF)
-			return NULL;		/* EOF */
+		/*
+		 * 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)
+			ereport(ERROR,
+					(errcode(ERRCODE_PROTOCOL_VIOLATION),
+					 errmsg("expected password response, got message type %d",
+							mtype)));
+		return NULL;		/* EOF or bad message type */
 	}
 
 	initStringInfo(&buf);
@@ -879,19 +870,6 @@ CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail)
 	int			result;
 	bool		initial;
 
-	/*
-	 * SASL auth is not supported for protocol versions before 3, because it
-	 * relies on the overall message length word to determine the SASL payload
-	 * size in AuthenticationSASLContinue and PasswordMessage messages.  (We
-	 * used to have a hard rule that protocol messages must be parsable
-	 * without relying on the length word, but we hardly care about older
-	 * protocol version anymore.)
-	 */
-	if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
-		ereport(FATAL,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("SASL authentication is not supported in protocol version 2")));
-
 	/*
 	 * Send the SASL authentication request to user.  It includes the list of
 	 * authentication mechanisms that are supported.
@@ -1041,19 +1019,6 @@ pg_GSS_recvauth(Port *port)
 	StringInfoData buf;
 	gss_buffer_desc gbuf;
 
-	/*
-	 * GSS auth is not supported for protocol versions before 3, because it
-	 * relies on the overall message length word to determine the GSS payload
-	 * size in AuthenticationGSSContinue and PasswordMessage messages. (This
-	 * is, in fact, a design error in our GSS support, because protocol
-	 * messages are supposed to be parsable without relying on the length
-	 * word; but it's not worth changing it now.)
-	 */
-	if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
-		ereport(FATAL,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("GSSAPI is not supported in protocol version 2")));
-
 	/*
 	 * Use the configured keytab, if there is one.  Unfortunately, Heimdal
 	 * doesn't support the cred store extensions, so use the env var.
@@ -1323,19 +1288,6 @@ pg_SSPI_recvauth(Port *port)
 
 	QUERY_SECURITY_CONTEXT_TOKEN_FN _QuerySecurityContextToken;
 
-	/*
-	 * SSPI auth is not supported for protocol versions before 3, because it
-	 * relies on the overall message length word to determine the SSPI payload
-	 * size in AuthenticationGSSContinue and PasswordMessage messages. (This
-	 * is, in fact, a design error in our SSPI support, because protocol
-	 * messages are supposed to be parsable without relying on the length
-	 * word; but it's not worth changing it now.)
-	 */
-	if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
-		ereport(FATAL,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("SSPI is not supported in protocol version 2")));
-
 	/*
 	 * Acquire a handle to the server credentials.
 	 */
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index 1e6b6db5400..176aa289f5d 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -5,23 +5,13 @@
  *
  * 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 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 ereport(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.  (New COPY-OUT is message-based and does *not*
- * set the DoingCopyOut flag.)
+ * channel, and are ignorant of the semantics of the data.
  *
- * 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 ensures
- * that the channel will not be clogged by an incomplete message if execution
- * is aborted by ereport(ERROR) partway through the message.  The only
- * non-libpq code that should call pq_putbytes directly is old-style COPY OUT.
+ * To emit an outgoing message, use the routines in pqformat.c to construct
+ * the message in a buffer and then emit it in one call to pq_putmessage.
+ * There are no functions to send raw bytes or partial messages; this
+ * ensures that the channel will not be clogged by an incomplete message if
+ * execution is aborted by ereport(ERROR) partway through the message.
  *
  * At one time, libpq was shared between frontend and backend, but now
  * the backend's "backend/libpq" is quite separate from "interfaces/libpq".
@@ -49,20 +39,16 @@
  *
  * 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
  *		pq_flush_if_writable - flush pending output if writable without blocking
  *		pq_getbyte_if_available - get a byte if available without blocking
  *
- * message-level I/O (and old-style-COPY-OUT cruft):
+ * message-level I/O
  *		pq_putmessage	- send a normal message (suppressed in COPY OUT mode)
  *		pq_putmessage_noblock - buffer a normal message (suppressed in COPY OUT)
- *		pq_startcopyout - inform libpq that a COPY OUT transfer is beginning
- *		pq_endcopyout	- end a COPY OUT transfer
  *
  *------------------------
  */
@@ -146,7 +132,6 @@ static int	PqRecvLength;		/* End of data available in PqRecvBuffer */
  */
 static bool PqCommBusy;			/* busy sending data to the client */
 static bool PqCommReadingMsg;	/* in the middle of reading a message */
-static bool DoingCopyOut;		/* in old-protocol COPY OUT processing */
 
 
 /* Internal functions */
@@ -158,8 +143,6 @@ static int	socket_flush_if_writable(void);
 static bool socket_is_send_pending(void);
 static int	socket_putmessage(char msgtype, const char *s, size_t len);
 static void socket_putmessage_noblock(char msgtype, const char *s, size_t len);
-static void socket_startcopyout(void);
-static void socket_endcopyout(bool errorAbort);
 static int	internal_putbytes(const char *s, size_t len);
 static int	internal_flush(void);
 
@@ -174,9 +157,7 @@ static const PQcommMethods PqCommSocketMethods = {
 	socket_flush_if_writable,
 	socket_is_send_pending,
 	socket_putmessage,
-	socket_putmessage_noblock,
-	socket_startcopyout,
-	socket_endcopyout
+	socket_putmessage_noblock
 };
 
 const PQcommMethods *PqCommMethods = &PqCommSocketMethods;
@@ -197,7 +178,6 @@ pq_init(void)
 	PqSendPointer = PqSendStart = PqRecvPointer = PqRecvLength = 0;
 	PqCommBusy = false;
 	PqCommReadingMsg = false;
-	DoingCopyOut = false;
 
 	/* set up process-exit hook to close the socket */
 	on_proc_exit(socket_close, 0);
@@ -238,8 +218,6 @@ socket_comm_reset(void)
 {
 	/* Do not throw away pending data, but do reset the busy flag */
 	PqCommBusy = false;
-	/* We can abort any old-style COPY OUT, too */
-	pq_endcopyout(true);
 }
 
 /* --------------------------------
@@ -1146,58 +1124,6 @@ pq_discardbytes(size_t len)
 	return 0;
 }
 
-/* --------------------------------
- *		pq_getstring	- get a null terminated string from connection
- *
- *		The return value is placed in an expansible StringInfo, which has
- *		already been initialized by the caller.
- *
- *		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			i;
-
-	Assert(PqCommReadingMsg);
-
-	resetStringInfo(s);
-
-	/* Read until we get the terminating '\0' */
-	for (;;)
-	{
-		while (PqRecvPointer >= PqRecvLength)
-		{
-			if (pq_recvbuf())	/* If nothing in buffer, then recv some */
-				return EOF;		/* Failed to recv data */
-		}
-
-		for (i = PqRecvPointer; i < PqRecvLength; i++)
-		{
-			if (PqRecvBuffer[i] == '\0')
-			{
-				/* include the '\0' in the copy */
-				appendBinaryStringInfo(s, PqRecvBuffer + PqRecvPointer,
-									   i - PqRecvPointer + 1);
-				PqRecvPointer = i + 1;	/* advance past \0 */
-				return 0;
-			}
-		}
-
-		/* If we're here we haven't got the \0 in the buffer yet. */
-		appendBinaryStringInfo(s, PqRecvBuffer + PqRecvPointer,
-							   PqRecvLength - PqRecvPointer);
-		PqRecvPointer = PqRecvLength;
-	}
-}
-
 
 /* --------------------------------
  *		pq_startmsgread - begin reading a message from the client.
@@ -1224,9 +1150,9 @@ pq_startmsgread(void)
 /* --------------------------------
  *		pq_endmsgread	- finish reading message.
  *
- *		This must be called after reading a V2 protocol message with
- *		pq_getstring() and friends, to indicate that we have read the whole
- *		message. In V3 protocol, pq_getmessage() does this implicitly.
+ *		This must be called after reading a message with pq_getbytes()
+ *		and friends, to indicate that we have read the whole message.
+ *		pq_getmessage() does this implicitly.
  * --------------------------------
  */
 void
@@ -1342,28 +1268,6 @@ pq_getmessage(StringInfo s, int maxlen)
 }
 
 
-/* --------------------------------
- *		pq_putbytes		- send bytes to connection (not flushed until pq_flush)
- *
- *		returns 0 if OK, EOF if trouble
- * --------------------------------
- */
-int
-pq_putbytes(const char *s, size_t len)
-{
-	int			res;
-
-	/* Should only be called by old-style COPY OUT */
-	Assert(DoingCopyOut);
-	/* No-op if reentrant call */
-	if (PqCommBusy)
-		return 0;
-	PqCommBusy = true;
-	res = internal_putbytes(s, len);
-	PqCommBusy = false;
-	return res;
-}
-
 static int
 internal_putbytes(const char *s, size_t len)
 {
@@ -1524,8 +1428,6 @@ socket_is_send_pending(void)
 
 /* --------------------------------
  * Message-level I/O routines begin here.
- *
- * These routines understand about the old-style COPY OUT protocol.
  * --------------------------------
  */
 
@@ -1533,20 +1435,13 @@ socket_is_send_pending(void)
 /* --------------------------------
  *		socket_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.  If msgtype is '\0', then the message has no type
- *		code (this is only valid in pre-3.0 protocols).
- *
- *		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.
+ *		msgtype is a message type code to place before the message body.
  *
- *		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.)
+ *		len is the length of the message body data at *s.  A message length
+ *		word (equal to len+4 because it counts itself too) is inserted by this
+ *		routine.
  *
- *		We also suppress messages generated while pqcomm.c is busy.  This
+ *		We suppress messages generated while pqcomm.c is busy.  This
  *		avoids any possibility of messages being inserted within other
  *		messages.  The only known trouble case arises if SIGQUIT occurs
  *		during a pqcomm.c routine --- quickdie() will try to send a warning
@@ -1558,20 +1453,20 @@ socket_is_send_pending(void)
 static int
 socket_putmessage(char msgtype, const char *s, size_t len)
 {
-	if (DoingCopyOut || PqCommBusy)
+	uint32		n32;
+
+	Assert(msgtype != 0);
+
+	if (PqCommBusy)
 		return 0;
 	PqCommBusy = true;
-	if (msgtype)
-		if (internal_putbytes(&msgtype, 1))
-			goto fail;
-	if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
-	{
-		uint32		n32;
+	if (internal_putbytes(&msgtype, 1))
+		goto fail;
+
+	n32 = pg_hton32((uint32) (len + 4));
+	if (internal_putbytes((char *) &n32, 4))
+		goto fail;
 
-		n32 = pg_hton32((uint32) (len + 4));
-		if (internal_putbytes((char *) &n32, 4))
-			goto fail;
-	}
 	if (internal_putbytes(s, len))
 		goto fail;
 	PqCommBusy = false;
@@ -1609,37 +1504,41 @@ socket_putmessage_noblock(char msgtype, const char *s, size_t len)
 								 * buffer */
 }
 
-
-/* --------------------------------
- *		socket_startcopyout - inform libpq that an old-style COPY OUT transfer
- *			is beginning
- * --------------------------------
- */
-static void
-socket_startcopyout(void)
-{
-	DoingCopyOut = true;
-}
-
 /* --------------------------------
- *		socket_endcopyout	- end an old-style COPY OUT transfer
+ *		pq_putmessage_v2 - send a message in protocol version 2
+ *
+ *		msgtype is a message type code to place before the message body.
  *
- *		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...)  Note that old-style COPY OUT does
- *		not allow binary transfers, so a textual terminator is always correct.
+ *		We no longer support protocol version 2, but we have kept this
+ *		function so that if a client tries to connect with protocol version 2,
+ *		as a courtesy we can still send the "unsupported protocol version"
+ *		error to the client in the old format.
+ *
+ *		Like in pq_putmessage(), we suppress messages generated while
+ *		pqcomm.c is busy.
+ *
+ *		returns 0 if OK, EOF if trouble
  * --------------------------------
  */
-static void
-socket_endcopyout(bool errorAbort)
+int
+pq_putmessage_v2(char msgtype, const char *s, size_t len)
 {
-	if (!DoingCopyOut)
-		return;
-	if (errorAbort)
-		pq_putbytes("\n\n\\.\n", 5);
-	/* in non-error case, copyto.c will have emitted the terminator line */
-	DoingCopyOut = false;
+	Assert(msgtype != 0);
+
+	if (PqCommBusy)
+		return 0;
+	PqCommBusy = true;
+	if (internal_putbytes(&msgtype, 1))
+		goto fail;
+
+	if (internal_putbytes(s, len))
+		goto fail;
+	PqCommBusy = false;
+	return 0;
+
+fail:
+	PqCommBusy = false;
+	return EOF;
 }
 
 /*
diff --git a/src/backend/libpq/pqmq.c b/src/backend/libpq/pqmq.c
index f468441b7a2..d1a1f47a788 100644
--- a/src/backend/libpq/pqmq.c
+++ b/src/backend/libpq/pqmq.c
@@ -33,8 +33,6 @@ static int	mq_flush_if_writable(void);
 static bool mq_is_send_pending(void);
 static int	mq_putmessage(char msgtype, const char *s, size_t len);
 static void mq_putmessage_noblock(char msgtype, const char *s, size_t len);
-static void mq_startcopyout(void);
-static void mq_endcopyout(bool errorAbort);
 
 static const PQcommMethods PqCommMqMethods = {
 	mq_comm_reset,
@@ -42,9 +40,7 @@ static const PQcommMethods PqCommMqMethods = {
 	mq_flush_if_writable,
 	mq_is_send_pending,
 	mq_putmessage,
-	mq_putmessage_noblock,
-	mq_startcopyout,
-	mq_endcopyout
+	mq_putmessage_noblock
 };
 
 /*
@@ -195,18 +191,6 @@ mq_putmessage_noblock(char msgtype, const char *s, size_t len)
 	elog(ERROR, "not currently supported");
 }
 
-static void
-mq_startcopyout(void)
-{
-	/* Nothing to do. */
-}
-
-static void
-mq_endcopyout(bool errorAbort)
-{
-	/* Nothing to do. */
-}
-
 /*
  * Parse an ErrorResponse or NoticeResponse payload and populate an ErrorData
  * structure with the results.
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 7de27ee4e01..9f4c877f6e5 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -1930,7 +1930,7 @@ static int
 ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done)
 {
 	int32		len;
-	void	   *buf;
+	char	   *buf;
 	ProtocolVersion proto;
 	MemoryContext oldcontext;
 
@@ -1980,15 +1980,12 @@ ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done)
 	}
 
 	/*
-	 * Allocate at least the size of an old-style startup packet, plus one
-	 * extra byte, and make sure all are zeroes.  This ensures we will have
-	 * null termination of all strings, in both fixed- and variable-length
-	 * packet layouts.
+	 * Allocate space to hold the startup packet, plus one extra byte that's
+	 * initialized to be zero.  This ensures we will have null termination of
+	 * all strings inside the packet.
 	 */
-	if (len <= (int32) sizeof(StartupPacket))
-		buf = palloc0(sizeof(StartupPacket) + 1);
-	else
-		buf = palloc0(len + 1);
+	buf = palloc(len + 1);
+	buf[len] = '\0';
 
 	if (pq_getbytes(buf, len) == EOF)
 	{
@@ -2111,7 +2108,7 @@ retry1:
 	 */
 	oldcontext = MemoryContextSwitchTo(TopMemoryContext);
 
-	if (PG_PROTOCOL_MAJOR(proto) >= 3)
+	/* Handle protocol version 3 startup packet */
 	{
 		int32		offset = sizeof(ProtocolVersion);
 		List	   *unrecognized_protocol_options = NIL;
@@ -2125,7 +2122,7 @@ retry1:
 
 		while (offset < len)
 		{
-			char	   *nameptr = ((char *) buf) + offset;
+			char	   *nameptr = buf + offset;
 			int32		valoffset;
 			char	   *valptr;
 
@@ -2134,7 +2131,7 @@ retry1:
 			valoffset = offset + strlen(nameptr) + 1;
 			if (valoffset >= len)
 				break;			/* missing value, will complain below */
-			valptr = ((char *) buf) + valoffset;
+			valptr = buf + valoffset;
 
 			if (strcmp(nameptr, "database") == 0)
 				port->database_name = pstrdup(valptr);
@@ -2219,27 +2216,6 @@ retry1:
 			unrecognized_protocol_options != NIL)
 			SendNegotiateProtocolVersion(unrecognized_protocol_options);
 	}
-	else
-	{
-		/*
-		 * Get the parameters from the old-style, fixed-width-fields startup
-		 * packet as C strings.  The packet destination was cleared first so a
-		 * short packet has zeros silently added.  We have to be prepared to
-		 * truncate the pstrdup result for oversize fields, though.
-		 */
-		StartupPacket *packet = (StartupPacket *) buf;
-
-		port->database_name = pstrdup(packet->database);
-		if (strlen(port->database_name) > sizeof(packet->database))
-			port->database_name[sizeof(packet->database)] = '\0';
-		port->user_name = pstrdup(packet->user);
-		if (strlen(port->user_name) > sizeof(packet->user))
-			port->user_name[sizeof(packet->user)] = '\0';
-		port->cmdline_options = pstrdup(packet->options);
-		if (strlen(port->cmdline_options) > sizeof(packet->options))
-			port->cmdline_options[sizeof(packet->options)] = '\0';
-		port->guc_options = NIL;
-	}
 
 	/* Check a user name was given. */
 	if (port->user_name == NULL || port->user_name[0] == '\0')
diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c
index 4316137a9d3..1dfadfa8e1d 100644
--- a/src/backend/tcop/dest.c
+++ b/src/backend/tcop/dest.c
@@ -226,13 +226,8 @@ EndReplicationCommand(const char *commandTag)
 /* ----------------
  *		NullCommand - tell dest that an empty query string was recognized
  *
- *		In FE/BE protocol version 1.0, this hack is necessary to support
- *		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.  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.
+ *		This ensures that there will be a recognizable end to the response
+ *		to an Execute message in the extended query protocol.
  * ----------------
  */
 void
@@ -244,14 +239,8 @@ NullCommand(CommandDest dest)
 		case DestRemoteExecute:
 		case DestRemoteSimple:
 
-			/*
-			 * tell the fe that we saw an empty query string.  In protocols
-			 * before 3.0 this has a useless empty-string message body.
-			 */
-			if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
-				pq_putemptymessage('I');
-			else
-				pq_putmessage('I', "", 1);
+			/* Tell the FE that we saw an empty query string */
+			pq_putemptymessage('I');
 			break;
 
 		case DestNone:
@@ -286,7 +275,6 @@ ReadyForQuery(CommandDest dest)
 		case DestRemote:
 		case DestRemoteExecute:
 		case DestRemoteSimple:
-			if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
 			{
 				StringInfoData buf;
 
@@ -294,8 +282,6 @@ ReadyForQuery(CommandDest dest)
 				pq_sendbyte(&buf, TransactionBlockStatusCode());
 				pq_endmessage(&buf);
 			}
-			else
-				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 1b76653caa4..77d17ebca94 100644
--- a/src/backend/tcop/fastpath.c
+++ b/src/backend/tcop/fastpath.c
@@ -58,98 +58,24 @@ struct fp_info
 
 static int16 parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
 								   FunctionCallInfo fcinfo);
-static int16 parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info *fip,
-									  FunctionCallInfo fcinfo);
-
-
-/* ----------------
- *		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.
- * ----------------
- */
-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 = pg_ntoh32(ibuf);
-	/* For each argument ... */
-	while (nargs-- > 0)
-	{
-		int			argsize;
-
-		/* argsize */
-		if (pq_getbytes((char *) &ibuf, 4))
-			return EOF;
-		appendBinaryStringInfo(buf, (char *) &ibuf, 4);
-		argsize = pg_ntoh32(ibuf);
-		if (argsize < -1)
-		{
-			/* FATAL here since no hope of regaining message sync */
-			ereport(FATAL,
-					(errcode(ERRCODE_PROTOCOL_VIOLATION),
-					 errmsg("invalid argument size %d in function call message",
-							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
- *
- * Note: although this routine doesn't check, the format had better be 1
- * (binary) when talking to a pre-3.0 client.
  * ----------------
  */
 static void
 SendFunctionResult(Datum retval, bool isnull, Oid rettype, int16 format)
 {
-	bool		newstyle = (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3);
 	StringInfoData buf;
 
 	pq_beginmessage(&buf, 'V');
 
 	if (isnull)
 	{
-		if (newstyle)
-			pq_sendint32(&buf, -1);
+		pq_sendint32(&buf, -1);
 	}
 	else
 	{
-		if (!newstyle)
-			pq_sendbyte(&buf, 'G');
-
 		if (format == 0)
 		{
 			Oid			typoutput;
@@ -180,9 +106,6 @@ SendFunctionResult(Datum retval, bool isnull, Oid rettype, int16 format)
 					 errmsg("unsupported format code: %d", format)));
 	}
 
-	if (!newstyle)
-		pq_sendbyte(&buf, '0');
-
 	pq_endmessage(&buf);
 }
 
@@ -288,9 +211,6 @@ HandleFunctionRequest(StringInfo msgBuf)
 	/*
 	 * Begin parsing the buffer contents.
 	 */
-	if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
-		(void) pq_getmsgstring(msgBuf); /* dummy string */
-
 	fid = (Oid) pq_getmsgint(msgBuf, 4);	/* function oid */
 
 	/*
@@ -334,10 +254,7 @@ HandleFunctionRequest(StringInfo msgBuf)
 	 */
 	InitFunctionCallInfoData(*fcinfo, &fip->flinfo, 0, InvalidOid, NULL, NULL);
 
-	if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
-		rformat = parse_fcall_arguments(msgBuf, fip, fcinfo);
-	else
-		rformat = parse_fcall_arguments_20(msgBuf, fip, fcinfo);
+	rformat = parse_fcall_arguments(msgBuf, fip, fcinfo);
 
 	/* Verify we reached the end of the message where expected. */
 	pq_getmsgend(msgBuf);
@@ -533,81 +450,3 @@ parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
 	/* Return result format code */
 	return (int16) pq_getmsgint(msgBuf, 2);
 }
-
-/*
- * Parse function arguments in a 2.0 protocol message
- *
- * Argument values are loaded into *fcinfo, and the desired result format
- * is returned.
- */
-static int16
-parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info *fip,
-						 FunctionCallInfo fcinfo)
-{
-	int			nargs;
-	int			i;
-	StringInfoData abuf;
-
-	nargs = pq_getmsgint(msgBuf, 4);	/* # of arguments */
-
-	if (fip->flinfo.fn_nargs != nargs || nargs > FUNC_MAX_ARGS)
-		ereport(ERROR,
-				(errcode(ERRCODE_PROTOCOL_VIOLATION),
-				 errmsg("function call message contains %d arguments but function requires %d",
-						nargs, fip->flinfo.fn_nargs)));
-
-	fcinfo->nargs = nargs;
-
-	initStringInfo(&abuf);
-
-	/*
-	 * Copy supplied arguments into arg vector.  In protocol 2.0 these are
-	 * always assumed to be supplied in binary format.
-	 *
-	 * Note: although the original protocol 2.0 code did not have any way for
-	 * the frontend to specify a NULL argument, we now choose to interpret
-	 * length == -1 as meaning a NULL.
-	 */
-	for (i = 0; i < nargs; ++i)
-	{
-		int			argsize;
-		Oid			typreceive;
-		Oid			typioparam;
-
-		getTypeBinaryInputInfo(fip->argtypes[i], &typreceive, &typioparam);
-
-		argsize = pq_getmsgint(msgBuf, 4);
-		if (argsize == -1)
-		{
-			fcinfo->args[i].isnull = true;
-			fcinfo->args[i].value = OidReceiveFunctionCall(typreceive, NULL,
-														   typioparam, -1);
-			continue;
-		}
-		fcinfo->args[i].isnull = false;
-		if (argsize < 0)
-			ereport(ERROR,
-					(errcode(ERRCODE_PROTOCOL_VIOLATION),
-					 errmsg("invalid argument size %d in function call message",
-							argsize)));
-
-		/* Reset abuf to empty, and insert raw data into it */
-		resetStringInfo(&abuf);
-		appendBinaryStringInfo(&abuf,
-							   pq_getmsgbytes(msgBuf, argsize),
-							   argsize);
-
-		fcinfo->args[i].value = OidReceiveFunctionCall(typreceive, &abuf,
-													   typioparam, -1);
-
-		/* Trouble if it didn't eat the whole buffer */
-		if (abuf.cursor != abuf.len)
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
-					 errmsg("incorrect binary data format in function argument %d",
-							i + 1)));
-	}
-
-	/* Desired result format is always binary in protocol 2.0 */
-	return 1;
-}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index cb5a96117f6..fcf6161da3f 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -370,57 +370,10 @@ SocketBackend(StringInfo inBuf)
 	{
 		case 'Q':				/* simple query */
 			doing_extended_query_message = false;
-			if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
-			{
-				/* old style without length word; convert */
-				if (pq_getstring(inBuf))
-				{
-					if (IsTransactionState())
-						ereport(COMMERROR,
-								(errcode(ERRCODE_CONNECTION_FAILURE),
-								 errmsg("unexpected EOF on client connection with an open transaction")));
-					else
-					{
-						/*
-						 * Can't send DEBUG log messages to client at this
-						 * point. Since we're disconnecting right away, we
-						 * don't need to restore whereToSendOutput.
-						 */
-						whereToSendOutput = DestNone;
-						ereport(DEBUG1,
-								(errcode(ERRCODE_CONNECTION_DOES_NOT_EXIST),
-								 errmsg("unexpected EOF on client connection")));
-					}
-					return EOF;
-				}
-			}
 			break;
 
 		case 'F':				/* fastpath function call */
 			doing_extended_query_message = false;
-			if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
-			{
-				if (GetOldFunctionMessage(inBuf))
-				{
-					if (IsTransactionState())
-						ereport(COMMERROR,
-								(errcode(ERRCODE_CONNECTION_FAILURE),
-								 errmsg("unexpected EOF on client connection with an open transaction")));
-					else
-					{
-						/*
-						 * Can't send DEBUG log messages to client at this
-						 * point. Since we're disconnecting right away, we
-						 * don't need to restore whereToSendOutput.
-						 */
-						whereToSendOutput = DestNone;
-						ereport(DEBUG1,
-								(errcode(ERRCODE_CONNECTION_DOES_NOT_EXIST),
-								 errmsg("unexpected EOF on client connection")));
-					}
-					return EOF;
-				}
-			}
 			break;
 
 		case 'X':				/* terminate */
@@ -435,11 +388,6 @@ SocketBackend(StringInfo inBuf)
 		case 'H':				/* flush */
 		case 'P':				/* parse */
 			doing_extended_query_message = true;
-			/* these are only legal in protocol 3 */
-			if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
-				ereport(FATAL,
-						(errcode(ERRCODE_PROTOCOL_VIOLATION),
-						 errmsg("invalid frontend message type %d", qtype)));
 			break;
 
 		case 'S':				/* sync */
@@ -447,22 +395,12 @@ SocketBackend(StringInfo inBuf)
 			ignore_till_sync = false;
 			/* mark not-extended, so that a new error doesn't begin skip */
 			doing_extended_query_message = false;
-			/* only legal in protocol 3 */
-			if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
-				ereport(FATAL,
-						(errcode(ERRCODE_PROTOCOL_VIOLATION),
-						 errmsg("invalid frontend message type %d", qtype)));
 			break;
 
 		case 'd':				/* copy data */
 		case 'c':				/* copy done */
 		case 'f':				/* copy fail */
 			doing_extended_query_message = false;
-			/* these are only legal in protocol 3 */
-			if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
-				ereport(FATAL,
-						(errcode(ERRCODE_PROTOCOL_VIOLATION),
-						 errmsg("invalid frontend message type %d", qtype)));
 			break;
 
 		default:
@@ -483,13 +421,8 @@ SocketBackend(StringInfo inBuf)
 	 * 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 */
-	}
-	else
-		pq_endmsgread();
+	if (pq_getmessage(inBuf, 0))
+		return EOF;			/* suitable message already logged */
 	RESUME_CANCEL_INTERRUPTS();
 
 	return qtype;
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 80c26724612..e729ebece7b 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -589,16 +589,6 @@ errfinish(const char *filename, int lineno, const char *funcname)
 		PG_RE_THROW();
 	}
 
-	/*
-	 * If we are doing FATAL or PANIC, abort any old-style COPY OUT in
-	 * progress, so that we can report the message before dying.  (Without
-	 * this, pq_putmessage will refuse to send the message at all, which is
-	 * what we want for NOTICE messages, but not for fatal exits.) This hack
-	 * is necessary because of poor design of old-style copy protocol.
-	 */
-	if (elevel >= FATAL && whereToSendOutput == DestRemote)
-		pq_endcopyout(true);
-
 	/* Emit the message to the right places */
 	EmitErrorReport();
 
@@ -1261,28 +1251,6 @@ errhidecontext(bool hide_ctx)
 	return 0;					/* return value does not matter */
 }
 
-
-/*
- * errfunction --- add reporting function name to the current error
- *
- * This is used when backwards compatibility demands that the function
- * name appear in messages sent to old-protocol clients.  Note that the
- * passed string is expected to be a non-freeable constant string.
- */
-int
-errfunction(const char *funcname)
-{
-	ErrorData  *edata = &errordata[errordata_stack_depth];
-
-	/* we don't bother incrementing recursion_depth */
-	CHECK_STACK_DEPTH();
-
-	edata->funcname = funcname;
-	edata->show_funcname = true;
-
-	return 0;					/* return value does not matter */
-}
-
 /*
  * errposition --- add cursor position to the current error
  */
@@ -3291,10 +3259,14 @@ send_message_to_frontend(ErrorData *edata)
 {
 	StringInfoData msgbuf;
 
-	/* 'N' (Notice) is for nonfatal conditions, 'E' is for errors */
-	pq_beginmessage(&msgbuf, (edata->elevel < ERROR) ? 'N' : 'E');
-
-	if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
+	/*
+	 * We no longer support pre-3.0 FE/BE protocol, except here.  If a client
+	 * tries to connect using an older protocol version, it's nice to send the
+	 * "protocol version not supported" error in a format the client
+	 * understands.  If protocol hasn't been set yet, early in backend
+	 * startup, assume modern protocol.
+	 */
+	if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3 || FrontendProtocol == 0)
 	{
 		/* New style with separate fields */
 		const char *sev;
@@ -3302,6 +3274,9 @@ send_message_to_frontend(ErrorData *edata)
 		int			ssval;
 		int			i;
 
+		/* 'N' (Notice) is for nonfatal conditions, 'E' is for errors */
+		pq_beginmessage(&msgbuf, (edata->elevel < ERROR) ? 'N' : 'E');
+
 		sev = error_severity(edata->elevel);
 		pq_sendbyte(&msgbuf, PG_DIAG_SEVERITY);
 		err_sendstring(&msgbuf, _(sev));
@@ -3417,6 +3392,8 @@ send_message_to_frontend(ErrorData *edata)
 		}
 
 		pq_sendbyte(&msgbuf, '\0'); /* terminator */
+
+		pq_endmessage(&msgbuf);
 	}
 	else
 	{
@@ -3427,30 +3404,19 @@ send_message_to_frontend(ErrorData *edata)
 
 		appendStringInfo(&buf, "%s:  ", _(error_severity(edata->elevel)));
 
-		if (edata->show_funcname && edata->funcname)
-			appendStringInfo(&buf, "%s: ", edata->funcname);
-
 		if (edata->message)
 			appendStringInfoString(&buf, edata->message);
 		else
 			appendStringInfoString(&buf, _("missing error text"));
 
-		if (edata->cursorpos > 0)
-			appendStringInfo(&buf, _(" at character %d"),
-							 edata->cursorpos);
-		else if (edata->internalpos > 0)
-			appendStringInfo(&buf, _(" at character %d"),
-							 edata->internalpos);
-
 		appendStringInfoChar(&buf, '\n');
 
-		err_sendstring(&msgbuf, buf.data);
+		/* 'N' (Notice) is for nonfatal conditions, 'E' is for errors */
+		pq_putmessage_v2((edata->elevel < ERROR) ? 'N' : 'E', buf.data, buf.len + 1);
 
 		pfree(buf.data);
 	}
 
-	pq_endmessage(&msgbuf);
-
 	/*
 	 * This flush is normally not necessary, since postgres.c will flush out
 	 * waiting data when control returns to the main loop. But it seems best
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index eafdb1118ed..34b2a2a871e 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -6295,11 +6295,9 @@ BeginReportingGUCOptions(void)
 	int			i;
 
 	/*
-	 * Don't do anything unless talking to an interactive frontend of protocol
-	 * 3.0 or later.
+	 * Don't do anything unless talking to an interactive frontend.
 	 */
-	if (whereToSendOutput != DestRemote ||
-		PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
+	if (whereToSendOutput != DestRemote)
 		return;
 
 	reporting_enabled = true;
diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index 6f507104f46..afd8a9ec151 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -2153,9 +2153,6 @@ is_select_command(const char *query)
 
 /*
  * Test if the current user is a database superuser.
- *
- * Note: this will correctly detect superuserness only with a protocol-3.0
- * or newer backend; otherwise it will always say "false".
  */
 bool
 is_superuser(void)
@@ -2176,9 +2173,6 @@ is_superuser(void)
 
 /*
  * Test if the current session uses standard string literals.
- *
- * Note: With a pre-protocol-3.0 connection this will always say "false",
- * which should be the right answer.
  */
 bool
 standard_strings(void)
@@ -2199,10 +2193,6 @@ standard_strings(void)
 
 /*
  * Return the session user of the current connection.
- *
- * Note: this will correctly detect the session user only with a
- * protocol-3.0 or newer backend; otherwise it will return the
- * connection user.
  */
 const char *
 session_username(void)
diff --git a/src/bin/psql/copy.c b/src/bin/psql/copy.c
index 78f0dc5a507..e1fee8e0992 100644
--- a/src/bin/psql/copy.c
+++ b/src/bin/psql/copy.c
@@ -662,7 +662,9 @@ handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary, PGresult **res)
 
 	/*
 	 * Terminate data transfer.  We can't send an error message if we're using
-	 * protocol version 2.
+	 * protocol version 2.  (libpq no longer supports protocol version 2, but
+	 * keep the version checks just in case you're using a pre-v14 libpq.so at
+	 * runtime)
 	 */
 	if (PQputCopyEnd(conn,
 					 (OK || PQprotocolVersion(conn) < 3) ? NULL :
diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h
index e37942df391..705f5b615be 100644
--- a/src/include/commands/copyfrom_internal.h
+++ b/src/include/commands/copyfrom_internal.h
@@ -24,8 +24,7 @@
 typedef enum CopySource
 {
 	COPY_FILE,					/* from file (or a piped program) */
-	COPY_OLD_FE,				/* from frontend (2.0 protocol) */
-	COPY_NEW_FE,				/* from frontend (3.0 protocol) */
+	COPY_FRONTEND,				/* from frontend */
 	COPY_CALLBACK				/* from callback function */
 } CopySource;
 
diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
index a55898c85af..42d97c014a2 100644
--- a/src/include/libpq/libpq.h
+++ b/src/include/libpq/libpq.h
@@ -29,8 +29,6 @@ typedef struct
 	bool		(*is_send_pending) (void);
 	int			(*putmessage) (char msgtype, const char *s, size_t len);
 	void		(*putmessage_noblock) (char msgtype, const char *s, size_t len);
-	void		(*startcopyout) (void);
-	void		(*endcopyout) (bool errorAbort);
 } PQcommMethods;
 
 extern const PGDLLIMPORT PQcommMethods *PqCommMethods;
@@ -43,8 +41,6 @@ extern const PGDLLIMPORT PQcommMethods *PqCommMethods;
 	(PqCommMethods->putmessage(msgtype, s, len))
 #define pq_putmessage_noblock(msgtype, s, len) \
 	(PqCommMethods->putmessage_noblock(msgtype, s, len))
-#define pq_startcopyout() (PqCommMethods->startcopyout())
-#define pq_endcopyout(errorAbort) (PqCommMethods->endcopyout(errorAbort))
 
 /*
  * External functions.
@@ -64,7 +60,6 @@ extern void TouchSocketFiles(void);
 extern void RemoveSocketFiles(void);
 extern void pq_init(void);
 extern int	pq_getbytes(char *s, size_t len);
-extern int	pq_getstring(StringInfo s);
 extern void pq_startmsgread(void);
 extern void pq_endmsgread(void);
 extern bool pq_is_reading_msg(void);
@@ -72,7 +67,7 @@ extern int	pq_getmessage(StringInfo s, int maxlen);
 extern int	pq_getbyte(void);
 extern int	pq_peekbyte(void);
 extern int	pq_getbyte_if_available(unsigned char *c);
-extern int	pq_putbytes(const char *s, size_t len);
+extern int	pq_putmessage_v2(char msgtype, const char *s, size_t len);
 
 /*
  * prototypes for functions in be-secure.c
diff --git a/src/include/libpq/pqcomm.h b/src/include/libpq/pqcomm.h
index a86b895b268..be9d9705744 100644
--- a/src/include/libpq/pqcomm.h
+++ b/src/include/libpq/pqcomm.h
@@ -114,9 +114,12 @@ is_unixsock_path(const char *path)
 #define PG_PROTOCOL_MINOR(v)	((v) & 0x0000ffff)
 #define PG_PROTOCOL(m,n)	(((m) << 16) | (n))
 
-/* The earliest and latest frontend/backend protocol version supported. */
+/*
+ * The earliest and latest frontend/backend protocol version supported.
+ * (Only protocol version 3 is currently supported)
+ */
 
-#define PG_PROTOCOL_EARLIEST	PG_PROTOCOL(2,0)
+#define PG_PROTOCOL_EARLIEST	PG_PROTOCOL(3,0)
 #define PG_PROTOCOL_LATEST		PG_PROTOCOL(3,0)
 
 typedef uint32 ProtocolVersion; /* FE/BE protocol version number */
@@ -132,32 +135,6 @@ typedef ProtocolVersion MsgType;
 
 typedef uint32 PacketLen;
 
-
-/*
- * Old-style startup packet layout with fixed-width fields.  This is used in
- * protocol 1.0 and 2.0, but not in later versions.  Note that the fields
- * in this layout are '\0' terminated only if there is room.
- */
-
-#define SM_DATABASE		64
-#define SM_USER			32
-/* We append database name if db_user_namespace true. */
-#define SM_DATABASE_USER (SM_DATABASE+SM_USER+1)	/* +1 for @ */
-#define SM_OPTIONS		64
-#define SM_UNUSED		64
-#define SM_TTY			64
-
-typedef struct StartupPacket
-{
-	ProtocolVersion protoVersion;	/* Protocol version */
-	char		database[SM_DATABASE];	/* Database name */
-	/* Db_user_namespace appends dbname */
-	char		user[SM_USER];	/* User name */
-	char		options[SM_OPTIONS];	/* Optional additional args */
-	char		unused[SM_UNUSED];	/* Unused */
-	char		tty[SM_TTY];	/* Tty for debug output */
-} StartupPacket;
-
 extern bool Db_user_namespace;
 
 /*
diff --git a/src/include/tcop/fastpath.h b/src/include/tcop/fastpath.h
index b67c44fe69e..c4d7a47dd72 100644
--- a/src/include/tcop/fastpath.h
+++ b/src/include/tcop/fastpath.h
@@ -15,7 +15,6 @@
 
 #include "lib/stringinfo.h"
 
-extern int	GetOldFunctionMessage(StringInfo buf);
 extern void HandleFunctionRequest(StringInfo msgBuf);
 
 #endif							/* FASTPATH_H */
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 3c0e57621fc..b59651289e4 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -207,7 +207,6 @@ extern int	errhidecontext(bool hide_ctx);
 
 extern int	errbacktrace(void);
 
-extern int	errfunction(const char *funcname);
 extern int	errposition(int cursorpos);
 
 extern int	internalerrposition(int cursorpos);
@@ -367,7 +366,6 @@ typedef struct ErrorData
 	int			elevel;			/* error level */
 	bool		output_to_server;	/* will report to server log? */
 	bool		output_to_client;	/* will report to client? */
-	bool		show_funcname;	/* true to force funcname inclusion */
 	bool		hide_stmt;		/* true to prevent STATEMENT: inclusion */
 	bool		hide_ctx;		/* true to prevent CONTEXT: inclusion */
 	const char *filename;		/* __FILE__ of ereport() call */
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index f74677eaf9b..2aca882a2be 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -37,7 +37,6 @@ OBJS = \
 	fe-lobj.o \
 	fe-misc.o \
 	fe-print.o \
-	fe-protocol2.o \
 	fe-protocol3.o \
 	fe-secure.o \
 	legacy-pqsignal.o \
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index 168b3df52bf..e8062647e60 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -579,7 +579,7 @@ pg_SASL_init(PGconn *conn, int payloadlen)
 	/*
 	 * Build a SASLInitialResponse message, and send it.
 	 */
-	if (pqPutMsgStart('p', true, conn))
+	if (pqPutMsgStart('p', conn))
 		goto error;
 	if (pqPuts(selected_mechanism, conn))
 		goto error;
@@ -798,11 +798,7 @@ pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
 		default:
 			return STATUS_ERROR;
 	}
-	/* Packet has a message type as of protocol 3.0 */
-	if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
-		ret = pqPacketSend(conn, 'p', pwd_to_send, strlen(pwd_to_send) + 1);
-	else
-		ret = pqPacketSend(conn, 0, pwd_to_send, strlen(pwd_to_send) + 1);
+	ret = pqPacketSend(conn, 'p', pwd_to_send, strlen(pwd_to_send) + 1);
 	if (crypt_pwd)
 		free(crypt_pwd);
 	return ret;
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 8ca0583aa90..5cd79532f32 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -2263,10 +2263,6 @@ PQconnectPoll(PGconn *conn)
 		case CONNECTION_MADE:
 			break;
 
-			/* We allow pqSetenvPoll to decide whether to proceed. */
-		case CONNECTION_SETENV:
-			break;
-
 			/* Special cases: proceed without waiting. */
 		case CONNECTION_SSL_STARTUP:
 		case CONNECTION_NEEDED:
@@ -2918,12 +2914,8 @@ keep_going:						/* We will come back to here until there is
 				/*
 				 * Build the startup packet.
 				 */
-				if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
-					startpacket = pqBuildStartupPacket3(conn, &packetlen,
-														EnvironmentOptions);
-				else
-					startpacket = pqBuildStartupPacket2(conn, &packetlen,
-														EnvironmentOptions);
+				startpacket = pqBuildStartupPacket3(conn, &packetlen,
+													EnvironmentOptions);
 				if (!startpacket)
 				{
 					appendPQExpBufferStr(&conn->errorMessage,
@@ -3209,19 +3201,11 @@ keep_going:						/* We will come back to here until there is
 					goto error_return;
 				}
 
-				if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
+				/* Read message length word */
+				if (pqGetInt(&msgLength, 4, conn))
 				{
-					/* Read message length word */
-					if (pqGetInt(&msgLength, 4, conn))
-					{
-						/* We'll come back when there is more data */
-						return PGRES_POLLING_READING;
-					}
-				}
-				else
-				{
-					/* Set phony message length to disable checks below */
-					msgLength = 8;
+					/* We'll come back when there is more data */
+					return PGRES_POLLING_READING;
 				}
 
 				/*
@@ -3230,7 +3214,9 @@ keep_going:						/* We will come back to here until there is
 				 * auth requests may not be that small.  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.
+				 * pre-3.0-protocol server; cope.  (Before version 14, the
+				 * server also used the old protocol for errors that happened
+				 * before processing the startup packet.)
 				 */
 				if (beresp == 'R' && (msgLength < 8 || msgLength > 2000))
 				{
@@ -3258,25 +3244,11 @@ keep_going:						/* We will come back to here until there is
 					 */
 					appendPQExpBufferChar(&conn->errorMessage, '\n');
 
-					/*
-					 * If we tried to open the connection in 3.0 protocol,
-					 * fall back to 2.0 protocol.
-					 */
-					if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
-					{
-						conn->pversion = PG_PROTOCOL(2, 0);
-						need_new_connection = true;
-						goto keep_going;
-					}
-
 					goto error_return;
 				}
 
 				/*
 				 * Can't process if message body isn't all here yet.
-				 *
-				 * (In protocol 2.0 case, we are assuming messages carry at
-				 * least 4 bytes of data.)
 				 */
 				msgLength -= 4;
 				avail = conn->inEnd - conn->inCursor;
@@ -3297,21 +3269,10 @@ keep_going:						/* We will come back to here until there is
 				/* Handle errors. */
 				if (beresp == 'E')
 				{
-					if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
+					if (pqGetErrorNotice3(conn, true))
 					{
-						if (pqGetErrorNotice3(conn, true))
-						{
-							/* We'll come back when there is more data */
-							return PGRES_POLLING_READING;
-						}
-					}
-					else
-					{
-						if (pqGets_append(&conn->errorMessage, conn))
-						{
-							/* We'll come back when there is more data */
-							return PGRES_POLLING_READING;
-						}
+						/* 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;
@@ -3395,33 +3356,6 @@ keep_going:						/* We will come back to here until there is
 				}
 				msgLength -= 4;
 
-				/*
-				 * Ensure the password salt is in the input buffer, if it's an
-				 * MD5 request.  All the other authentication methods that
-				 * contain extra data in the authentication request are only
-				 * supported in protocol version 3, in which case we already
-				 * read the whole message above.
-				 */
-				if (areq == AUTH_REQ_MD5 && PG_PROTOCOL_MAJOR(conn->pversion) < 3)
-				{
-					msgLength += 4;
-
-					avail = conn->inEnd - conn->inCursor;
-					if (avail < 4)
-					{
-						/*
-						 * Before returning, try to enlarge the input buffer
-						 * if needed to hold the whole message; see notes in
-						 * pqParseInput3.
-						 */
-						if (pqCheckInBufferSpace(conn->inCursor + (size_t) 4,
-												 conn))
-							goto error_return;
-						/* We'll come back when there is more data */
-						return PGRES_POLLING_READING;
-					}
-				}
-
 				/*
 				 * Process the rest of the authentication request message, and
 				 * respond to it if necessary.
@@ -3529,15 +3463,6 @@ keep_going:						/* We will come back to here until there is
 					goto error_return;
 				}
 
-				/* Fire up post-connection housekeeping if needed */
-				if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
-				{
-					conn->status = CONNECTION_SETENV;
-					conn->setenv_state = SETENV_STATE_CLIENT_ENCODING_SEND;
-					conn->next_eo = EnvironmentOptions;
-					return PGRES_POLLING_WRITING;
-				}
-
 				/* Almost there now ... */
 				conn->status = CONNECTION_CHECK_TARGET;
 				goto keep_going;
@@ -3578,39 +3503,6 @@ keep_going:						/* We will come back to here until there is
 				return PGRES_POLLING_OK;
 			}
 
-		case CONNECTION_SETENV:
-			{
-				/*
-				 * Do post-connection housekeeping (only needed in protocol
-				 * 2.0).
-				 *
-				 * We pretend that the connection is OK for the duration of
-				 * these queries.
-				 */
-				conn->status = CONNECTION_OK;
-
-				switch (pqSetenvPoll(conn))
-				{
-					case PGRES_POLLING_OK:	/* Success */
-						break;
-
-					case PGRES_POLLING_READING: /* Still going */
-						conn->status = CONNECTION_SETENV;
-						return PGRES_POLLING_READING;
-
-					case PGRES_POLLING_WRITING: /* Still going */
-						conn->status = CONNECTION_SETENV;
-						return PGRES_POLLING_WRITING;
-
-					default:
-						goto error_return;
-				}
-
-				/* Almost there now ... */
-				conn->status = CONNECTION_CHECK_TARGET;
-				goto keep_going;
-			}
-
 		case CONNECTION_CONSUME:
 			{
 				conn->status = CONNECTION_OK;
@@ -3852,7 +3744,6 @@ makeEmptyPGconn(void)
 	conn->xactStatus = PQTRANS_IDLE;
 	conn->options_valid = false;
 	conn->nonblocking = false;
-	conn->setenv_state = SETENV_STATE_IDLE;
 	conn->client_encoding = PG_SQL_ASCII;
 	conn->std_strings = false;	/* unless server says differently */
 	conn->verbosity = PQERRORS_DEFAULT;
@@ -4065,7 +3956,7 @@ sendTerminateConn(PGconn *conn)
 		 * Try to send "close connection" message to backend. Ignore any
 		 * error.
 		 */
-		pqPutMsgStart('X', false, conn);
+		pqPutMsgStart('X', conn);
 		pqPutMsgEnd(conn);
 		(void) pqFlush(conn);
 	}
@@ -4458,16 +4349,13 @@ PQrequestCancel(PGconn *conn)
  *
  * RETURNS: STATUS_ERROR if the write fails, STATUS_OK otherwise.
  * SIDE_EFFECTS: may block.
- *
- * Note: all messages sent with this routine have a length word, whether
- * it's protocol 2.0 or 3.0.
  */
 int
 pqPacketSend(PGconn *conn, char pack_type,
 			 const void *buf, size_t buf_len)
 {
 	/* Start the message. */
-	if (pqPutMsgStart(pack_type, true, conn))
+	if (pqPutMsgStart(pack_type, conn))
 		return STATUS_ERROR;
 
 	/* Send the message body. */
@@ -6723,13 +6611,9 @@ PQsetClientEncoding(PGconn *conn, const char *encoding)
 	else
 	{
 		/*
-		 * In protocol 2 we have to assume the setting will stick, and adjust
-		 * our state immediately.  In protocol 3 and up we can rely on the
-		 * backend to report the parameter value, and we'll change state at
-		 * that time.
+		 * We rely on the backend to report the parameter value, and we'll
+		 * change state at that time.
 		 */
-		if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
-			pqSaveParameterStatus(conn, "client_encoding", encoding);
 		status = 0;				/* everything is ok */
 	}
 	PQclear(res);
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index e7307533876..7c7d2017f97 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -1210,7 +1210,7 @@ PQsendQueryInternal(PGconn *conn, const char *query, bool newQuery)
 	}
 
 	/* construct the outgoing Query message */
-	if (pqPutMsgStart('Q', false, conn) < 0 ||
+	if (pqPutMsgStart('Q', conn) < 0 ||
 		pqPuts(query, conn) < 0 ||
 		pqPutMsgEnd(conn) < 0)
 	{
@@ -1244,7 +1244,7 @@ PQsendQueryInternal(PGconn *conn, const char *query, bool newQuery)
 
 /*
  * PQsendQueryParams
- *		Like PQsendQuery, but use protocol 3.0 so we can pass parameters
+ *		Like PQsendQuery, but use extended query protocol so we can pass parameters
  */
 int
 PQsendQueryParams(PGconn *conn,
@@ -1319,16 +1319,8 @@ PQsendPrepare(PGconn *conn,
 		return 0;
 	}
 
-	/* This isn't gonna work on a 2.0 server */
-	if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
-	{
-		appendPQExpBufferStr(&conn->errorMessage,
-							 libpq_gettext("function requires at least protocol version 3.0\n"));
-		return 0;
-	}
-
 	/* construct the Parse message */
-	if (pqPutMsgStart('P', false, conn) < 0 ||
+	if (pqPutMsgStart('P', conn) < 0 ||
 		pqPuts(stmtName, conn) < 0 ||
 		pqPuts(query, conn) < 0)
 		goto sendFailed;
@@ -1354,7 +1346,7 @@ PQsendPrepare(PGconn *conn,
 		goto sendFailed;
 
 	/* construct the Sync message */
-	if (pqPutMsgStart('S', false, conn) < 0 ||
+	if (pqPutMsgStart('S', conn) < 0 ||
 		pqPutMsgEnd(conn) < 0)
 		goto sendFailed;
 
@@ -1386,7 +1378,7 @@ sendFailed:
 /*
  * PQsendQueryPrepared
  *		Like PQsendQuery, but execute a previously prepared statement,
- *		using protocol 3.0 so we can pass parameters
+ *		using extended query protocol so we can pass parameters
  */
 int
 PQsendQueryPrepared(PGconn *conn,
@@ -1467,7 +1459,7 @@ PQsendQueryStart(PGconn *conn, bool newQuery)
 
 /*
  * PQsendQueryGuts
- *		Common code for protocol-3.0 query sending
+ *		Common code for sending a query with extended query protocol
  *		PQsendQueryStart should be done already
  *
  * command may be NULL to indicate we use an already-prepared statement
@@ -1485,14 +1477,6 @@ PQsendQueryGuts(PGconn *conn,
 {
 	int			i;
 
-	/* This isn't gonna work on a 2.0 server */
-	if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
-	{
-		appendPQExpBufferStr(&conn->errorMessage,
-							 libpq_gettext("function requires at least protocol version 3.0\n"));
-		return 0;
-	}
-
 	/*
 	 * We will send Parse (if needed), Bind, Describe Portal, Execute, Sync,
 	 * using specified statement name and the unnamed portal.
@@ -1501,7 +1485,7 @@ PQsendQueryGuts(PGconn *conn,
 	if (command)
 	{
 		/* construct the Parse message */
-		if (pqPutMsgStart('P', false, conn) < 0 ||
+		if (pqPutMsgStart('P', conn) < 0 ||
 			pqPuts(stmtName, conn) < 0 ||
 			pqPuts(command, conn) < 0)
 			goto sendFailed;
@@ -1525,7 +1509,7 @@ PQsendQueryGuts(PGconn *conn,
 	}
 
 	/* Construct the Bind message */
-	if (pqPutMsgStart('B', false, conn) < 0 ||
+	if (pqPutMsgStart('B', conn) < 0 ||
 		pqPuts("", conn) < 0 ||
 		pqPuts(stmtName, conn) < 0)
 		goto sendFailed;
@@ -1592,21 +1576,21 @@ PQsendQueryGuts(PGconn *conn,
 		goto sendFailed;
 
 	/* construct the Describe Portal message */
-	if (pqPutMsgStart('D', false, conn) < 0 ||
+	if (pqPutMsgStart('D', conn) < 0 ||
 		pqPutc('P', conn) < 0 ||
 		pqPuts("", conn) < 0 ||
 		pqPutMsgEnd(conn) < 0)
 		goto sendFailed;
 
 	/* construct the Execute message */
-	if (pqPutMsgStart('E', false, conn) < 0 ||
+	if (pqPutMsgStart('E', conn) < 0 ||
 		pqPuts("", conn) < 0 ||
 		pqPutInt(0, 4, conn) < 0 ||
 		pqPutMsgEnd(conn) < 0)
 		goto sendFailed;
 
 	/* construct the Sync message */
-	if (pqPutMsgStart('S', false, conn) < 0 ||
+	if (pqPutMsgStart('S', conn) < 0 ||
 		pqPutMsgEnd(conn) < 0)
 		goto sendFailed;
 
@@ -1707,10 +1691,7 @@ PQconsumeInput(PGconn *conn)
 static void
 parseInput(PGconn *conn)
 {
-	if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
-		pqParseInput3(conn);
-	else
-		pqParseInput2(conn);
+	pqParseInput3(conn);
 }
 
 /*
@@ -1915,7 +1896,7 @@ PQexec(PGconn *conn, const char *query)
 
 /*
  * PQexecParams
- *		Like PQexec, but use protocol 3.0 so we can pass parameters
+ *		Like PQexec, but use extended query protocol so we can pass parameters
  */
 PGresult *
 PQexecParams(PGconn *conn,
@@ -1938,7 +1919,7 @@ PQexecParams(PGconn *conn,
 
 /*
  * PQprepare
- *	  Creates a prepared statement by issuing a v3.0 parse message.
+ *	  Creates a prepared statement by issuing a Parse message.
  *
  * If the query was not even sent, return NULL; conn->errorMessage is set to
  * a relevant message.
@@ -1962,7 +1943,7 @@ PQprepare(PGconn *conn,
 /*
  * PQexecPrepared
  *		Like PQexec, but execute a previously prepared statement,
- *		using protocol 3.0 so we can pass parameters
+ *		using extended query protocol so we can pass parameters
  */
 PGresult *
 PQexecPrepared(PGconn *conn,
@@ -2009,41 +1990,20 @@ PQexecStart(PGconn *conn)
 		PQclear(result);		/* only need its status */
 		if (resultStatus == PGRES_COPY_IN)
 		{
-			if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
-			{
-				/* In protocol 3, we can get out of a COPY IN state */
-				if (PQputCopyEnd(conn,
-								 libpq_gettext("COPY terminated by new PQexec")) < 0)
-					return false;
-				/* keep waiting to swallow the copy's failure message */
-			}
-			else
-			{
-				/* In older protocols we have to punt */
-				appendPQExpBufferStr(&conn->errorMessage,
-									 libpq_gettext("COPY IN state must be terminated first\n"));
+			/* get out of a COPY IN state */
+			if (PQputCopyEnd(conn,
+							 libpq_gettext("COPY terminated by new PQexec")) < 0)
 				return false;
-			}
+			/* keep waiting to swallow the copy's failure message */
 		}
 		else if (resultStatus == PGRES_COPY_OUT)
 		{
-			if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
-			{
-				/*
-				 * In protocol 3, we can get out of a COPY OUT state: we just
-				 * switch back to BUSY and allow the remaining COPY data to be
-				 * dropped on the floor.
-				 */
-				conn->asyncStatus = PGASYNC_BUSY;
-				/* keep waiting to swallow the copy's completion message */
-			}
-			else
-			{
-				/* In older protocols we have to punt */
-				appendPQExpBufferStr(&conn->errorMessage,
-									 libpq_gettext("COPY OUT state must be terminated first\n"));
-				return false;
-			}
+			/*
+			 * Get out of a COPY OUT state: we just switch back to BUSY and
+			 * allow the remaining COPY data to be dropped on the floor.
+			 */
+			conn->asyncStatus = PGASYNC_BUSY;
+			/* keep waiting to swallow the copy's completion message */
 		}
 		else if (resultStatus == PGRES_COPY_BOTH)
 		{
@@ -2184,23 +2144,15 @@ PQsendDescribe(PGconn *conn, char desc_type, const char *desc_target)
 	if (!PQsendQueryStart(conn, true))
 		return 0;
 
-	/* This isn't gonna work on a 2.0 server */
-	if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
-	{
-		appendPQExpBufferStr(&conn->errorMessage,
-							 libpq_gettext("function requires at least protocol version 3.0\n"));
-		return 0;
-	}
-
 	/* construct the Describe message */
-	if (pqPutMsgStart('D', false, conn) < 0 ||
+	if (pqPutMsgStart('D', conn) < 0 ||
 		pqPutc(desc_type, conn) < 0 ||
 		pqPuts(desc_target, conn) < 0 ||
 		pqPutMsgEnd(conn) < 0)
 		goto sendFailed;
 
 	/* construct the Sync message */
-	if (pqPutMsgStart('S', false, conn) < 0 ||
+	if (pqPutMsgStart('S', conn) < 0 ||
 		pqPutMsgEnd(conn) < 0)
 		goto sendFailed;
 
@@ -2300,8 +2252,7 @@ PQputCopyData(PGconn *conn, const char *buffer, int nbytes)
 		 * Try to flush any previously sent data in preference to growing the
 		 * output buffer.  If we can't enlarge the buffer enough to hold the
 		 * data, return 0 in the nonblock case, else hard error. (For
-		 * simplicity, always assume 5 bytes of overhead even in protocol 2.0
-		 * case.)
+		 * simplicity, always assume 5 bytes of overhead.)
 		 */
 		if ((conn->outBufSize - conn->outCount - 5) < nbytes)
 		{
@@ -2312,20 +2263,10 @@ PQputCopyData(PGconn *conn, const char *buffer, int nbytes)
 				return pqIsnonblocking(conn) ? 0 : -1;
 		}
 		/* Send the data (too simple to delegate to fe-protocol files) */
-		if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
-		{
-			if (pqPutMsgStart('d', false, conn) < 0 ||
-				pqPutnchar(buffer, nbytes, conn) < 0 ||
-				pqPutMsgEnd(conn) < 0)
-				return -1;
-		}
-		else
-		{
-			if (pqPutMsgStart(0, false, conn) < 0 ||
-				pqPutnchar(buffer, nbytes, conn) < 0 ||
-				pqPutMsgEnd(conn) < 0)
-				return -1;
-		}
+		if (pqPutMsgStart('d', conn) < 0 ||
+			pqPutnchar(buffer, nbytes, conn) < 0 ||
+			pqPutMsgEnd(conn) < 0)
+			return -1;
 	}
 	return 1;
 }
@@ -2355,52 +2296,31 @@ PQputCopyEnd(PGconn *conn, const char *errormsg)
 	 * Send the COPY END indicator.  This is simple enough that we don't
 	 * bother delegating it to the fe-protocol files.
 	 */
-	if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
+	if (errormsg)
 	{
-		if (errormsg)
-		{
-			/* Send COPY FAIL */
-			if (pqPutMsgStart('f', false, conn) < 0 ||
-				pqPuts(errormsg, conn) < 0 ||
-				pqPutMsgEnd(conn) < 0)
-				return -1;
-		}
-		else
-		{
-			/* Send COPY DONE */
-			if (pqPutMsgStart('c', false, conn) < 0 ||
-				pqPutMsgEnd(conn) < 0)
-				return -1;
-		}
-
-		/*
-		 * If we sent the COPY command in extended-query mode, we must issue a
-		 * Sync as well.
-		 */
-		if (conn->queryclass != PGQUERY_SIMPLE)
-		{
-			if (pqPutMsgStart('S', false, conn) < 0 ||
-				pqPutMsgEnd(conn) < 0)
-				return -1;
-		}
+		/* Send COPY FAIL */
+		if (pqPutMsgStart('f', conn) < 0 ||
+			pqPuts(errormsg, conn) < 0 ||
+			pqPutMsgEnd(conn) < 0)
+			return -1;
 	}
 	else
 	{
-		if (errormsg)
-		{
-			/* Oops, no way to do this in 2.0 */
-			appendPQExpBufferStr(&conn->errorMessage,
-								 libpq_gettext("function requires at least protocol version 3.0\n"));
+		/* Send COPY DONE */
+		if (pqPutMsgStart('c', conn) < 0 ||
+			pqPutMsgEnd(conn) < 0)
+			return -1;
+	}
+
+	/*
+	 * If we sent the COPY command in extended-query mode, we must issue a
+	 * Sync as well.
+	 */
+	if (conn->queryclass != PGQUERY_SIMPLE)
+	{
+		if (pqPutMsgStart('S', conn) < 0 ||
+			pqPutMsgEnd(conn) < 0)
 			return -1;
-		}
-		else
-		{
-			/* Send old-style end-of-data marker */
-			if (pqPutMsgStart(0, false, conn) < 0 ||
-				pqPutnchar("\\.\n", 3, conn) < 0 ||
-				pqPutMsgEnd(conn) < 0)
-				return -1;
-		}
 	}
 
 	/* Return to active duty */
@@ -2439,10 +2359,7 @@ PQgetCopyData(PGconn *conn, char **buffer, int async)
 							 libpq_gettext("no COPY in progress\n"));
 		return -2;
 	}
-	if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
-		return pqGetCopyData3(conn, buffer, async);
-	else
-		return pqGetCopyData2(conn, buffer, async);
+	return pqGetCopyData3(conn, buffer, async);
 }
 
 /*
@@ -2481,10 +2398,7 @@ PQgetline(PGconn *conn, char *s, int maxlen)
 	if (!conn)
 		return EOF;
 
-	if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
-		return pqGetline3(conn, s, maxlen);
-	else
-		return pqGetline2(conn, s, maxlen);
+	return pqGetline3(conn, s, maxlen);
 }
 
 /*
@@ -2524,10 +2438,7 @@ PQgetlineAsync(PGconn *conn, char *buffer, int bufsize)
 	if (!conn)
 		return -1;
 
-	if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
-		return pqGetlineAsync3(conn, buffer, bufsize);
-	else
-		return pqGetlineAsync2(conn, buffer, bufsize);
+	return pqGetlineAsync3(conn, buffer, bufsize);
 }
 
 /*
@@ -2562,10 +2473,8 @@ PQputnbytes(PGconn *conn, const char *buffer, int nbytes)
  *		After completing the data transfer portion of a copy in/out,
  *		the application must call this routine to finish the command protocol.
  *
- * When using protocol 3.0 this is deprecated; it's cleaner to use PQgetResult
- * to get the transfer status.  Note however that when using 2.0 protocol,
- * recovering from a copy failure often requires a PQreset.  PQendcopy will
- * take care of that, PQgetResult won't.
+ * This is deprecated; it's cleaner to use PQgetResult to get the transfer
+ * status.
  *
  * RETURNS:
  *		0 on success
@@ -2577,10 +2486,7 @@ PQendcopy(PGconn *conn)
 	if (!conn)
 		return 0;
 
-	if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
-		return pqEndcopy3(conn);
-	else
-		return pqEndcopy2(conn);
+	return pqEndcopy3(conn);
 }
 
 
@@ -2632,16 +2538,10 @@ PQfn(PGconn *conn,
 		return NULL;
 	}
 
-	if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
-		return pqFunctionCall3(conn, fnid,
-							   result_buf, result_len,
-							   result_is_int,
-							   args, nargs);
-	else
-		return pqFunctionCall2(conn, fnid,
-							   result_buf, result_len,
-							   result_is_int,
-							   args, nargs);
+	return pqFunctionCall3(conn, fnid,
+						   result_buf, result_len,
+						   result_is_int,
+						   args, nargs);
 }
 
 
@@ -2690,13 +2590,6 @@ PQresultVerboseErrorMessage(const PGresult *res,
 
 	initPQExpBuffer(&workBuf);
 
-	/*
-	 * Currently, we pass this off to fe-protocol3.c in all cases; it will
-	 * behave reasonably sanely with an error reported by fe-protocol2.c as
-	 * well.  If necessary, we could record the protocol version in PGresults
-	 * so as to be able to invoke a version-specific message formatter, but
-	 * for now there's no need.
-	 */
 	pqBuildErrorMessage3(&workBuf, res, verbosity, show_context);
 
 	/* If insufficient memory to format the message, fail cleanly */
diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c
index 2bfb6acd895..ce2d24b91fc 100644
--- a/src/interfaces/libpq/fe-misc.c
+++ b/src/interfaces/libpq/fe-misc.c
@@ -484,9 +484,6 @@ pqCheckInBufferSpace(size_t bytes_needed, PGconn *conn)
  * msg_type is the message type byte, or 0 for a message without type byte
  * (only startup messages have no type byte)
  *
- * force_len forces the message to have a length word; otherwise, we add
- * a length word if protocol 3.
- *
  * Returns 0 on success, EOF on error
  *
  * The idea here is that we construct the message in conn->outBuffer,
@@ -497,12 +494,11 @@ pqCheckInBufferSpace(size_t bytes_needed, PGconn *conn)
  *
  * 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.  If we are sending a message without length word
- * (pre protocol 3.0 only), then outMsgStart is -1.  The state variable
- * conn->outMsgEnd is the end of the data collected so far.
+ * there is a type byte.  The state variable conn->outMsgEnd is the end of
+ * the data collected so far.
  */
 int
-pqPutMsgStart(char msg_type, bool force_len, PGconn *conn)
+pqPutMsgStart(char msg_type, PGconn *conn)
 {
 	int			lenPos;
 	int			endPos;
@@ -514,14 +510,9 @@ pqPutMsgStart(char msg_type, bool force_len, PGconn *conn)
 		endPos = conn->outCount;
 
 	/* do we want a length word? */
-	if (force_len || PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
-	{
-		lenPos = endPos;
-		/* allow room for message length */
-		endPos += 4;
-	}
-	else
-		lenPos = -1;
+	lenPos = endPos;
+	/* allow room for message length */
+	endPos += 4;
 
 	/* make sure there is room for message header */
 	if (pqCheckOutBufferSpace(endPos, conn))
diff --git a/src/interfaces/libpq/fe-protocol2.c b/src/interfaces/libpq/fe-protocol2.c
deleted file mode 100644
index 6efa53d8b71..00000000000
--- a/src/interfaces/libpq/fe-protocol2.c
+++ /dev/null
@@ -1,1610 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * fe-protocol2.c
- *	  functions that are specific to frontend/backend protocol version 2
- *
- * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- *	  src/interfaces/libpq/fe-protocol2.c
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres_fe.h"
-
-#include <ctype.h>
-#include <fcntl.h>
-
-#ifdef WIN32
-#include "win32.h"
-#else
-#include <unistd.h>
-#ifdef HAVE_NETINET_TCP_H
-#include <netinet/tcp.h>
-#endif
-#endif
-
-#include "libpq-fe.h"
-#include "libpq-int.h"
-#include "port/pg_bswap.h"
-
-static int	getRowDescriptions(PGconn *conn);
-static int	getAnotherTuple(PGconn *conn, bool binary);
-static int	pqGetErrorNotice2(PGconn *conn, bool isError);
-static void checkXactStatus(PGconn *conn, const char *cmdTag);
-static int	getNotify(PGconn *conn);
-
-
-/*
- *		pqSetenvPoll
- *
- * Polls the process of passing the values of a standard set of environment
- * variables to the backend.
- */
-PostgresPollingStatusType
-pqSetenvPoll(PGconn *conn)
-{
-	PGresult   *res;
-
-	if (conn == NULL || conn->status == CONNECTION_BAD)
-		return PGRES_POLLING_FAILED;
-
-	/* Check whether there are any data for us */
-	switch (conn->setenv_state)
-	{
-			/* These are reading states */
-		case SETENV_STATE_CLIENT_ENCODING_WAIT:
-		case SETENV_STATE_OPTION_WAIT:
-		case SETENV_STATE_QUERY1_WAIT:
-		case SETENV_STATE_QUERY2_WAIT:
-			{
-				/* Load waiting data */
-				int			n = pqReadData(conn);
-
-				if (n < 0)
-					goto error_return;
-				if (n == 0)
-					return PGRES_POLLING_READING;
-
-				break;
-			}
-
-			/* These are writing states, so we just proceed. */
-		case SETENV_STATE_CLIENT_ENCODING_SEND:
-		case SETENV_STATE_OPTION_SEND:
-		case SETENV_STATE_QUERY1_SEND:
-		case SETENV_STATE_QUERY2_SEND:
-			break;
-
-			/* Should we raise an error if called when not active? */
-		case SETENV_STATE_IDLE:
-			return PGRES_POLLING_OK;
-
-		default:
-			appendPQExpBuffer(&conn->errorMessage,
-							  libpq_gettext("invalid setenv state %c, probably indicative of memory corruption\n"),
-							  conn->setenv_state);
-			goto error_return;
-	}
-
-	/* We will loop here until there is nothing left to do in this call. */
-	for (;;)
-	{
-		switch (conn->setenv_state)
-		{
-				/*
-				 * The _CLIENT_ENCODING_SEND code is slightly different from
-				 * _OPTION_SEND below (e.g., no getenv() call), which is why a
-				 * different state is used.
-				 */
-			case SETENV_STATE_CLIENT_ENCODING_SEND:
-				{
-					char		setQuery[100];	/* note length limit in
-												 * sprintf below */
-					const char *val = conn->client_encoding_initial;
-
-					if (val)
-					{
-						if (pg_strcasecmp(val, "default") == 0)
-							sprintf(setQuery, "SET client_encoding = DEFAULT");
-						else
-							sprintf(setQuery, "SET client_encoding = '%.60s'",
-									val);
-#ifdef CONNECTDEBUG
-						fprintf(stderr,
-								"Sending client_encoding with %s\n",
-								setQuery);
-#endif
-						if (!PQsendQuery(conn, setQuery))
-							goto error_return;
-
-						conn->setenv_state = SETENV_STATE_CLIENT_ENCODING_WAIT;
-					}
-					else
-						conn->setenv_state = SETENV_STATE_OPTION_SEND;
-					break;
-				}
-
-			case SETENV_STATE_OPTION_SEND:
-				{
-					/*
-					 * Send SET commands for stuff directed by Environment
-					 * Options.  Note: we assume that SET commands won't start
-					 * transaction blocks, even in a 7.3 server with
-					 * autocommit off.
-					 */
-					char		setQuery[100];	/* note length limit in
-												 * sprintf below */
-
-					if (conn->next_eo->envName)
-					{
-						const char *val;
-
-						if ((val = getenv(conn->next_eo->envName)))
-						{
-							if (pg_strcasecmp(val, "default") == 0)
-								sprintf(setQuery, "SET %s = DEFAULT",
-										conn->next_eo->pgName);
-							else
-								sprintf(setQuery, "SET %s = '%.60s'",
-										conn->next_eo->pgName, val);
-#ifdef CONNECTDEBUG
-							fprintf(stderr,
-									"Use environment variable %s to send %s\n",
-									conn->next_eo->envName, setQuery);
-#endif
-							if (!PQsendQuery(conn, setQuery))
-								goto error_return;
-
-							conn->setenv_state = SETENV_STATE_OPTION_WAIT;
-						}
-						else
-							conn->next_eo++;
-					}
-					else
-					{
-						/* No more options to send, so move on to querying */
-						conn->setenv_state = SETENV_STATE_QUERY1_SEND;
-					}
-					break;
-				}
-
-			case SETENV_STATE_CLIENT_ENCODING_WAIT:
-				{
-					if (PQisBusy(conn))
-						return PGRES_POLLING_READING;
-
-					res = PQgetResult(conn);
-
-					if (res)
-					{
-						if (PQresultStatus(res) != PGRES_COMMAND_OK)
-						{
-							PQclear(res);
-							goto error_return;
-						}
-						PQclear(res);
-						/* Keep reading until PQgetResult returns NULL */
-					}
-					else
-					{
-						/* Query finished, so send the next option */
-						conn->setenv_state = SETENV_STATE_OPTION_SEND;
-					}
-					break;
-				}
-
-			case SETENV_STATE_OPTION_WAIT:
-				{
-					if (PQisBusy(conn))
-						return PGRES_POLLING_READING;
-
-					res = PQgetResult(conn);
-
-					if (res)
-					{
-						if (PQresultStatus(res) != PGRES_COMMAND_OK)
-						{
-							PQclear(res);
-							goto error_return;
-						}
-						PQclear(res);
-						/* Keep reading until PQgetResult returns NULL */
-					}
-					else
-					{
-						/* Query finished, so send the next option */
-						conn->next_eo++;
-						conn->setenv_state = SETENV_STATE_OPTION_SEND;
-					}
-					break;
-				}
-
-			case SETENV_STATE_QUERY1_SEND:
-				{
-					/*
-					 * Issue query to get information we need.  Here we must
-					 * use begin/commit in case autocommit is off by default
-					 * in a 7.3 server.
-					 *
-					 * Note: version() exists in all protocol-2.0-supporting
-					 * backends.  In 7.3 it would be safer to write
-					 * pg_catalog.version(), but we can't do that without
-					 * causing problems on older versions.
-					 */
-					if (!PQsendQuery(conn, "begin; select version(); end"))
-						goto error_return;
-
-					conn->setenv_state = SETENV_STATE_QUERY1_WAIT;
-					return PGRES_POLLING_READING;
-				}
-
-			case SETENV_STATE_QUERY1_WAIT:
-				{
-					if (PQisBusy(conn))
-						return PGRES_POLLING_READING;
-
-					res = PQgetResult(conn);
-
-					if (res)
-					{
-						char	   *val;
-
-						if (PQresultStatus(res) == PGRES_COMMAND_OK)
-						{
-							/* ignore begin/commit command results */
-							PQclear(res);
-							continue;
-						}
-
-						if (PQresultStatus(res) != PGRES_TUPLES_OK ||
-							PQntuples(res) != 1)
-						{
-							PQclear(res);
-							goto error_return;
-						}
-
-						/*
-						 * Extract server version and save as if
-						 * ParameterStatus
-						 */
-						val = PQgetvalue(res, 0, 0);
-						if (val && strncmp(val, "PostgreSQL ", 11) == 0)
-						{
-							char	   *ptr;
-
-							/* strip off PostgreSQL part */
-							val += 11;
-
-							/*
-							 * strip off platform part (scribbles on result,
-							 * naughty naughty)
-							 */
-							ptr = strchr(val, ' ');
-							if (ptr)
-								*ptr = '\0';
-
-							pqSaveParameterStatus(conn, "server_version",
-												  val);
-						}
-
-						PQclear(res);
-						/* Keep reading until PQgetResult returns NULL */
-					}
-					else
-					{
-						/* Query finished, move to next */
-						conn->setenv_state = SETENV_STATE_QUERY2_SEND;
-					}
-					break;
-				}
-
-			case SETENV_STATE_QUERY2_SEND:
-				{
-					const char *query;
-
-					/*
-					 * pg_client_encoding does not exist in pre-7.2 servers.
-					 * So we need to be prepared for an error here.  Do *not*
-					 * start a transaction block, except in 7.3 servers where
-					 * we need to prevent autocommit-off from starting a
-					 * transaction anyway.
-					 */
-					if (conn->sversion >= 70300 &&
-						conn->sversion < 70400)
-						query = "begin; select pg_catalog.pg_client_encoding(); end";
-					else
-						query = "select pg_client_encoding()";
-					if (!PQsendQuery(conn, query))
-						goto error_return;
-
-					conn->setenv_state = SETENV_STATE_QUERY2_WAIT;
-					return PGRES_POLLING_READING;
-				}
-
-			case SETENV_STATE_QUERY2_WAIT:
-				{
-					if (PQisBusy(conn))
-						return PGRES_POLLING_READING;
-
-					res = PQgetResult(conn);
-
-					if (res)
-					{
-						const char *val;
-
-						if (PQresultStatus(res) == PGRES_COMMAND_OK)
-						{
-							/* ignore begin/commit command results */
-							PQclear(res);
-							continue;
-						}
-
-						if (PQresultStatus(res) == PGRES_TUPLES_OK &&
-							PQntuples(res) == 1)
-						{
-							/* Extract client encoding and save it */
-							val = PQgetvalue(res, 0, 0);
-							if (val && *val)	/* null should not happen, but */
-								pqSaveParameterStatus(conn, "client_encoding",
-													  val);
-						}
-						else
-						{
-							/*
-							 * Error: presumably function not available, so
-							 * use PGCLIENTENCODING or SQL_ASCII as the
-							 * fallback.
-							 */
-							val = getenv("PGCLIENTENCODING");
-							if (val && *val)
-								pqSaveParameterStatus(conn, "client_encoding",
-													  val);
-							else
-								pqSaveParameterStatus(conn, "client_encoding",
-													  "SQL_ASCII");
-						}
-
-						PQclear(res);
-						/* Keep reading until PQgetResult returns NULL */
-					}
-					else
-					{
-						/* Query finished, so we're done */
-						conn->setenv_state = SETENV_STATE_IDLE;
-						return PGRES_POLLING_OK;
-					}
-					break;
-				}
-
-			default:
-				appendPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("invalid state %c, "
-												"probably indicative of memory corruption\n"),
-								  conn->setenv_state);
-				goto error_return;
-		}
-	}
-
-	/* Unreachable */
-
-error_return:
-	conn->setenv_state = SETENV_STATE_IDLE;
-	return PGRES_POLLING_FAILED;
-}
-
-
-/*
- * parseInput: if appropriate, parse input data from backend
- * until input is exhausted or a stopping state is reached.
- * Note that this function will NOT attempt to read more data from the backend.
- */
-void
-pqParseInput2(PGconn *conn)
-{
-	char		id;
-
-	/*
-	 * Loop to parse successive complete messages available in the buffer.
-	 */
-	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.)
-		 */
-		if (conn->asyncStatus == PGASYNC_COPY_OUT)
-			return;
-
-		/*
-		 * OK to try to read a message type code.
-		 */
-		conn->inCursor = conn->inStart;
-		if (pqGetc(&id, conn))
-			return;
-
-		/*
-		 * NOTIFY and NOTICE messages can happen in any state besides COPY
-		 * OUT; 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 until
-		 * the application collects the current PGresult.)
-		 *
-		 * However, if the state is IDLE then we got trouble; we need to deal
-		 * with the unexpected message somehow.
-		 */
-		if (id == 'A')
-		{
-			if (getNotify(conn))
-				return;
-		}
-		else if (id == 'N')
-		{
-			if (pqGetErrorNotice2(conn, false))
-				return;
-		}
-		else if (conn->asyncStatus != PGASYNC_BUSY)
-		{
-			/* If not IDLE state, just wait ... */
-			if (conn->asyncStatus != PGASYNC_IDLE)
-				return;
-
-			/*
-			 * Unexpected message in IDLE state; need to recover somehow.
-			 * ERROR messages are displayed using the notice processor;
-			 * anything else is just dropped on the floor after displaying a
-			 * suitable warning notice.  (An ERROR is very possibly the
-			 * backend telling us why it is about to close the connection, so
-			 * we don't want to just discard it...)
-			 */
-			if (id == 'E')
-			{
-				if (pqGetErrorNotice2(conn, false /* treat as notice */ ))
-					return;
-			}
-			else
-			{
-				pqInternalNotice(&conn->noticeHooks,
-								 "message type 0x%02x arrived from server while idle",
-								 id);
-				/* Discard the unexpected message; good idea?? */
-				conn->inStart = conn->inEnd;
-				break;
-			}
-		}
-		else
-		{
-			/*
-			 * In BUSY state, we can process everything.
-			 */
-			switch (id)
-			{
-				case 'C':		/* command complete */
-					if (pqGets(&conn->workBuffer, conn))
-						return;
-					if (conn->result == NULL)
-					{
-						conn->result = PQmakeEmptyPGresult(conn,
-														   PGRES_COMMAND_OK);
-						if (!conn->result)
-						{
-							appendPQExpBufferStr(&conn->errorMessage,
-												 libpq_gettext("out of memory"));
-							pqSaveErrorResult(conn);
-						}
-					}
-					if (conn->result)
-					{
-						strlcpy(conn->result->cmdStatus, conn->workBuffer.data,
-								CMDSTATUS_LEN);
-					}
-					checkXactStatus(conn, conn->workBuffer.data);
-					conn->asyncStatus = PGASYNC_READY;
-					break;
-				case 'E':		/* error return */
-					if (pqGetErrorNotice2(conn, true))
-						return;
-					conn->asyncStatus = PGASYNC_READY;
-					break;
-				case 'Z':		/* backend is ready for new query */
-					conn->asyncStatus = PGASYNC_IDLE;
-					break;
-				case 'I':		/* empty query */
-					/* read and throw away the closing '\0' */
-					if (pqGetc(&id, conn))
-						return;
-					if (id != '\0')
-						pqInternalNotice(&conn->noticeHooks,
-										 "unexpected character %c following empty query response (\"I\" message)",
-										 id);
-					if (conn->result == NULL)
-					{
-						conn->result = PQmakeEmptyPGresult(conn,
-														   PGRES_EMPTY_QUERY);
-						if (!conn->result)
-						{
-							appendPQExpBufferStr(&conn->errorMessage,
-												 libpq_gettext("out of memory"));
-							pqSaveErrorResult(conn);
-						}
-					}
-					conn->asyncStatus = PGASYNC_READY;
-					break;
-				case 'K':		/* secret key data from the backend */
-
-					/*
-					 * This is expected only during backend startup, but it's
-					 * just as easy to handle it as part of the main loop.
-					 * Save the data and continue processing.
-					 */
-					if (pqGetInt(&(conn->be_pid), 4, conn))
-						return;
-					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)
-					{
-						/* First 'T' in a query sequence */
-						if (getRowDescriptions(conn))
-							return;
-						/* getRowDescriptions() moves inStart itself */
-						continue;
-					}
-					else
-					{
-						/*
-						 * A new 'T' message is treated as the start of
-						 * another PGresult.  (It is not clear that this is
-						 * really possible with the current backend.) We stop
-						 * parsing until the application accepts the current
-						 * result.
-						 */
-						conn->asyncStatus = PGASYNC_READY;
-						return;
-					}
-					break;
-				case 'D':		/* ASCII data tuple */
-					if (conn->result != NULL)
-					{
-						/* Read another tuple of a normal query response */
-						if (getAnotherTuple(conn, false))
-							return;
-						/* getAnotherTuple() moves inStart itself */
-						continue;
-					}
-					else
-					{
-						pqInternalNotice(&conn->noticeHooks,
-										 "server sent data (\"D\" message) without prior row description (\"T\" message)");
-						/* Discard the unexpected message; good idea?? */
-						conn->inStart = conn->inEnd;
-						return;
-					}
-					break;
-				case 'B':		/* Binary data tuple */
-					if (conn->result != NULL)
-					{
-						/* Read another tuple of a normal query response */
-						if (getAnotherTuple(conn, true))
-							return;
-						/* getAnotherTuple() moves inStart itself */
-						continue;
-					}
-					else
-					{
-						pqInternalNotice(&conn->noticeHooks,
-										 "server sent binary data (\"B\" message) without prior row description (\"T\" message)");
-						/* Discard the unexpected message; good idea?? */
-						conn->inStart = conn->inEnd;
-						return;
-					}
-					break;
-				case 'G':		/* Start Copy In */
-					conn->asyncStatus = PGASYNC_COPY_IN;
-					break;
-				case 'H':		/* Start Copy Out */
-					conn->asyncStatus = PGASYNC_COPY_OUT;
-					break;
-
-					/*
-					 * Don't need to process CopyBothResponse here because it
-					 * never arrives from the server during protocol 2.0.
-					 */
-				default:
-					appendPQExpBuffer(&conn->errorMessage,
-									  libpq_gettext("unexpected response from server; first received character was \"%c\"\n"),
-									  id);
-					/* build an error result holding the error message */
-					pqSaveErrorResult(conn);
-					/* Discard the unexpected message; good idea?? */
-					conn->inStart = conn->inEnd;
-					conn->asyncStatus = PGASYNC_READY;
-					return;
-			}					/* switch on protocol character */
-		}
-		/* Successfully consumed this message */
-		conn->inStart = conn->inCursor;
-	}
-}
-
-/*
- * parseInput subroutine to read a 'T' (row descriptions) message.
- * We build a PGresult structure containing the attribute data.
- * Returns: 0 if completed message, EOF if error or not enough data
- * received yet.
- *
- * Note that if we run out of data, we have to suspend and reprocess
- * the message after more data is received.  Otherwise, conn->inStart
- * must get advanced past the processed data.
- */
-static int
-getRowDescriptions(PGconn *conn)
-{
-	PGresult   *result;
-	int			nfields;
-	const char *errmsg;
-	int			i;
-
-	result = PQmakeEmptyPGresult(conn, PGRES_TUPLES_OK);
-	if (!result)
-	{
-		errmsg = NULL;			/* means "out of memory", see below */
-		goto advance_and_error;
-	}
-
-	/* parseInput already read the 'T' label. */
-	/* the next two bytes are the number of fields	*/
-	if (pqGetInt(&(result->numAttributes), 2, conn))
-		goto EOFexit;
-	nfields = result->numAttributes;
-
-	/* allocate space for the attribute descriptors */
-	if (nfields > 0)
-	{
-		result->attDescs = (PGresAttDesc *)
-			pqResultAlloc(result, nfields * sizeof(PGresAttDesc), true);
-		if (!result->attDescs)
-		{
-			errmsg = NULL;		/* means "out of memory", see below */
-			goto advance_and_error;
-		}
-		MemSet(result->attDescs, 0, nfields * sizeof(PGresAttDesc));
-	}
-
-	/* get type info */
-	for (i = 0; i < nfields; i++)
-	{
-		int			typid;
-		int			typlen;
-		int			atttypmod;
-
-		if (pqGets(&conn->workBuffer, conn) ||
-			pqGetInt(&typid, 4, conn) ||
-			pqGetInt(&typlen, 2, conn) ||
-			pqGetInt(&atttypmod, 4, conn))
-			goto EOFexit;
-
-		/*
-		 * Since pqGetInt treats 2-byte integers as unsigned, we need to
-		 * coerce the result to signed form.
-		 */
-		typlen = (int) ((int16) typlen);
-
-		result->attDescs[i].name = pqResultStrdup(result,
-												  conn->workBuffer.data);
-		if (!result->attDescs[i].name)
-		{
-			errmsg = NULL;		/* means "out of memory", see below */
-			goto advance_and_error;
-		}
-		result->attDescs[i].tableid = 0;
-		result->attDescs[i].columnid = 0;
-		result->attDescs[i].format = 0;
-		result->attDescs[i].typid = typid;
-		result->attDescs[i].typlen = typlen;
-		result->attDescs[i].atttypmod = atttypmod;
-	}
-
-	/* Success! */
-	conn->result = result;
-
-	/* Advance inStart to show that the "T" message has been processed. */
-	conn->inStart = conn->inCursor;
-
-	/*
-	 * We could perform additional setup for the new result set here, but for
-	 * now there's nothing else to do.
-	 */
-
-	/* And we're done. */
-	return 0;
-
-advance_and_error:
-
-	/*
-	 * Discard the failed message.  Unfortunately we don't know for sure where
-	 * the end is, so just throw away everything in the input buffer. This is
-	 * not very desirable but it's the best we can do in protocol v2.
-	 */
-	conn->inStart = conn->inEnd;
-
-	/*
-	 * Replace partially constructed result with an error result. First
-	 * discard the old result to try to win back some memory.
-	 */
-	pqClearAsyncResult(conn);
-
-	/*
-	 * If preceding code didn't provide an error message, assume "out of
-	 * memory" was meant.  The advantage of having this special case is that
-	 * freeing the old result first greatly improves the odds that gettext()
-	 * will succeed in providing a translation.
-	 */
-	if (!errmsg)
-		errmsg = libpq_gettext("out of memory for query result");
-
-	appendPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
-
-	/*
-	 * XXX: if PQmakeEmptyPGresult() fails, there's probably not much we can
-	 * do to recover...
-	 */
-	conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
-	conn->asyncStatus = PGASYNC_READY;
-
-EOFexit:
-	if (result && result != conn->result)
-		PQclear(result);
-	return EOF;
-}
-
-/*
- * parseInput subroutine to read a 'B' or 'D' (row data) message.
- * We fill rowbuf with column pointers and then call the row processor.
- * Returns: 0 if completed message, EOF if error or not enough data
- * received yet.
- *
- * Note that if we run out of data, we have to suspend and reprocess
- * the message after more data is received.  Otherwise, conn->inStart
- * must get advanced past the processed data.
- */
-static int
-getAnotherTuple(PGconn *conn, bool binary)
-{
-	PGresult   *result = conn->result;
-	int			nfields = result->numAttributes;
-	const char *errmsg;
-	PGdataValue *rowbuf;
-
-	/* the backend sends us a bitmap of which attributes are null */
-	char		std_bitmap[64]; /* used unless it doesn't fit */
-	char	   *bitmap = std_bitmap;
-	int			i;
-	size_t		nbytes;			/* the number of bytes in bitmap  */
-	char		bmap;			/* One byte of the bitmap */
-	int			bitmap_index;	/* Its index */
-	int			bitcnt;			/* number of bits examined in current byte */
-	int			vlen;			/* length of the current field value */
-
-	/* Resize row buffer if needed */
-	rowbuf = conn->rowBuf;
-	if (nfields > conn->rowBufLen)
-	{
-		rowbuf = (PGdataValue *) realloc(rowbuf,
-										 nfields * sizeof(PGdataValue));
-		if (!rowbuf)
-		{
-			errmsg = NULL;		/* means "out of memory", see below */
-			goto advance_and_error;
-		}
-		conn->rowBuf = rowbuf;
-		conn->rowBufLen = nfields;
-	}
-
-	/* Save format specifier */
-	result->binary = binary;
-
-	/*
-	 * If it's binary, fix the column format indicators.  We assume the
-	 * backend will consistently send either B or D, not a mix.
-	 */
-	if (binary)
-	{
-		for (i = 0; i < nfields; i++)
-			result->attDescs[i].format = 1;
-	}
-
-	/* Get the null-value bitmap */
-	nbytes = (nfields + BITS_PER_BYTE - 1) / BITS_PER_BYTE;
-	/* malloc() only for unusually large field counts... */
-	if (nbytes > sizeof(std_bitmap))
-	{
-		bitmap = (char *) malloc(nbytes);
-		if (!bitmap)
-		{
-			errmsg = NULL;		/* means "out of memory", see below */
-			goto advance_and_error;
-		}
-	}
-
-	if (pqGetnchar(bitmap, nbytes, conn))
-		goto EOFexit;
-
-	/* Scan the fields */
-	bitmap_index = 0;
-	bmap = bitmap[bitmap_index];
-	bitcnt = 0;
-
-	for (i = 0; i < nfields; i++)
-	{
-		/* get the value length */
-		if (!(bmap & 0200))
-			vlen = NULL_LEN;
-		else if (pqGetInt(&vlen, 4, conn))
-			goto EOFexit;
-		else
-		{
-			if (!binary)
-				vlen = vlen - 4;
-			if (vlen < 0)
-				vlen = 0;
-		}
-		rowbuf[i].len = vlen;
-
-		/*
-		 * rowbuf[i].value always points to the next address in the data
-		 * buffer even if the value is NULL.  This allows row processors to
-		 * estimate data sizes more easily.
-		 */
-		rowbuf[i].value = conn->inBuffer + conn->inCursor;
-
-		/* Skip over the data value */
-		if (vlen > 0)
-		{
-			if (pqSkipnchar(vlen, conn))
-				goto EOFexit;
-		}
-
-		/* advance the bitmap stuff */
-		bitcnt++;
-		if (bitcnt == BITS_PER_BYTE)
-		{
-			bitmap_index++;
-			bmap = bitmap[bitmap_index];
-			bitcnt = 0;
-		}
-		else
-			bmap <<= 1;
-	}
-
-	/* Release bitmap now if we allocated it */
-	if (bitmap != std_bitmap)
-		free(bitmap);
-	bitmap = NULL;
-
-	/* Advance inStart to show that the "D" message has been processed. */
-	conn->inStart = conn->inCursor;
-
-	/* Process the collected row */
-	errmsg = NULL;
-	if (pqRowProcessor(conn, &errmsg))
-		return 0;				/* normal, successful exit */
-
-	goto set_error_result;		/* pqRowProcessor failed, report it */
-
-advance_and_error:
-
-	/*
-	 * Discard the failed message.  Unfortunately we don't know for sure where
-	 * the end is, so just throw away everything in the input buffer. This is
-	 * not very desirable but it's the best we can do in protocol v2.
-	 */
-	conn->inStart = conn->inEnd;
-
-set_error_result:
-
-	/*
-	 * Replace partially constructed result with an error result. First
-	 * discard the old result to try to win back some memory.
-	 */
-	pqClearAsyncResult(conn);
-
-	/*
-	 * If preceding code didn't provide an error message, assume "out of
-	 * memory" was meant.  The advantage of having this special case is that
-	 * freeing the old result first greatly improves the odds that gettext()
-	 * will succeed in providing a translation.
-	 */
-	if (!errmsg)
-		errmsg = libpq_gettext("out of memory for query result");
-
-	appendPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
-
-	/*
-	 * XXX: if PQmakeEmptyPGresult() fails, there's probably not much we can
-	 * do to recover...
-	 */
-	conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
-	conn->asyncStatus = PGASYNC_READY;
-
-EOFexit:
-	if (bitmap != NULL && bitmap != std_bitmap)
-		free(bitmap);
-	return EOF;
-}
-
-
-/*
- * Attempt to read an Error or Notice response message.
- * This is possible in several places, so we break it out as a subroutine.
- * Entry: 'E' or 'N' message type has already been consumed.
- * Exit: returns 0 if successfully consumed message.
- *		 returns EOF if not enough data.
- */
-static int
-pqGetErrorNotice2(PGconn *conn, bool isError)
-{
-	PGresult   *res = NULL;
-	PQExpBufferData workBuf;
-	char	   *startp;
-	char	   *splitp;
-
-	/*
-	 * If this is an error message, pre-emptively clear any incomplete query
-	 * result we may have.  We'd just throw it away below anyway, and
-	 * releasing it before collecting the error might avoid out-of-memory.
-	 */
-	if (isError)
-		pqClearAsyncResult(conn);
-
-	/*
-	 * Since the message might be pretty long, we create a temporary
-	 * PQExpBuffer rather than using conn->workBuffer.  workBuffer is intended
-	 * for stuff that is expected to be short.
-	 */
-	initPQExpBuffer(&workBuf);
-	if (pqGets(&workBuf, conn))
-		goto failure;
-
-	/*
-	 * Make a PGresult to hold the message.  We temporarily lie about the
-	 * result status, so that PQmakeEmptyPGresult doesn't uselessly copy
-	 * conn->errorMessage.
-	 *
-	 * NB: This allocation can fail, if you run out of memory. The rest of the
-	 * function handles that gracefully, and we still try to set the error
-	 * message as the connection's error message.
-	 */
-	res = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY);
-	if (res)
-	{
-		res->resultStatus = isError ? PGRES_FATAL_ERROR : PGRES_NONFATAL_ERROR;
-		res->errMsg = pqResultStrdup(res, workBuf.data);
-	}
-
-	/*
-	 * Break the message into fields.  We can't do very much here, but we can
-	 * split the severity code off, and remove trailing newlines. Also, we use
-	 * the heuristic that the primary message extends only to the first
-	 * newline --- anything after that is detail message.  (In some cases it'd
-	 * be better classed as hint, but we can hardly be expected to guess that
-	 * here.)
-	 */
-	while (workBuf.len > 0 && workBuf.data[workBuf.len - 1] == '\n')
-		workBuf.data[--workBuf.len] = '\0';
-	splitp = strstr(workBuf.data, ":  ");
-	if (splitp)
-	{
-		/* what comes before the colon is severity */
-		*splitp = '\0';
-		pqSaveMessageField(res, PG_DIAG_SEVERITY, workBuf.data);
-		startp = splitp + 3;
-	}
-	else
-	{
-		/* can't find a colon?  oh well... */
-		startp = workBuf.data;
-	}
-	splitp = strchr(startp, '\n');
-	if (splitp)
-	{
-		/* what comes before the newline is primary message */
-		*splitp++ = '\0';
-		pqSaveMessageField(res, PG_DIAG_MESSAGE_PRIMARY, startp);
-		/* the rest is detail; strip any leading whitespace */
-		while (*splitp && isspace((unsigned char) *splitp))
-			splitp++;
-		pqSaveMessageField(res, PG_DIAG_MESSAGE_DETAIL, splitp);
-	}
-	else
-	{
-		/* single-line message, so all primary */
-		pqSaveMessageField(res, PG_DIAG_MESSAGE_PRIMARY, startp);
-	}
-
-	/*
-	 * Either save error as current async result, or just emit the notice.
-	 * Also, if it's an error and we were in a transaction block, assume the
-	 * server has now gone to error-in-transaction state.
-	 */
-	if (isError)
-	{
-		pqClearAsyncResult(conn);	/* redundant, but be safe */
-		conn->result = res;
-		if (res && !PQExpBufferDataBroken(workBuf) && res->errMsg)
-			appendPQExpBufferStr(&conn->errorMessage, res->errMsg);
-		else
-			appendPQExpBufferStr(&conn->errorMessage,
-								 libpq_gettext("out of memory"));
-		if (conn->xactStatus == PQTRANS_INTRANS)
-			conn->xactStatus = PQTRANS_INERROR;
-	}
-	else
-	{
-		if (res)
-		{
-			if (res->noticeHooks.noticeRec != NULL)
-				res->noticeHooks.noticeRec(res->noticeHooks.noticeRecArg, res);
-			PQclear(res);
-		}
-	}
-
-	termPQExpBuffer(&workBuf);
-	return 0;
-
-failure:
-	if (res)
-		PQclear(res);
-	termPQExpBuffer(&workBuf);
-	return EOF;
-}
-
-/*
- * checkXactStatus - attempt to track transaction-block status of server
- *
- * This is called each time we receive a command-complete message.  By
- * watching for messages from BEGIN/COMMIT/ROLLBACK commands, we can do
- * a passable job of tracking the server's xact status.  BUT: this does
- * not work at all on 7.3 servers with AUTOCOMMIT OFF.  (Man, was that
- * feature ever a mistake.)  Caveat user.
- *
- * The tags known here are all those used as far back as 7.0; is it worth
- * adding those from even-older servers?
- */
-static void
-checkXactStatus(PGconn *conn, const char *cmdTag)
-{
-	if (strcmp(cmdTag, "BEGIN") == 0)
-		conn->xactStatus = PQTRANS_INTRANS;
-	else if (strcmp(cmdTag, "COMMIT") == 0)
-		conn->xactStatus = PQTRANS_IDLE;
-	else if (strcmp(cmdTag, "ROLLBACK") == 0)
-		conn->xactStatus = PQTRANS_IDLE;
-	else if (strcmp(cmdTag, "START TRANSACTION") == 0)	/* 7.3 only */
-		conn->xactStatus = PQTRANS_INTRANS;
-
-	/*
-	 * Normally we get into INERROR state by detecting an Error message.
-	 * However, if we see one of these tags then we know for sure the server
-	 * is in abort state ...
-	 */
-	else if (strcmp(cmdTag, "*ABORT STATE*") == 0)	/* pre-7.3 only */
-		conn->xactStatus = PQTRANS_INERROR;
-}
-
-/*
- * Attempt to read a Notify response message.
- * This is possible in several places, so we break it out as a subroutine.
- * Entry: 'A' message type and length have already been consumed.
- * Exit: returns 0 if successfully consumed Notify message.
- *		 returns EOF if not enough data.
- */
-static int
-getNotify(PGconn *conn)
-{
-	int			be_pid;
-	int			nmlen;
-	PGnotify   *newNotify;
-
-	if (pqGetInt(&be_pid, 4, conn))
-		return EOF;
-	if (pqGets(&conn->workBuffer, conn))
-		return EOF;
-
-	/*
-	 * Store the relation name right after the PQnotify structure so it can
-	 * all be freed at once.  We don't use NAMEDATALEN because we don't want
-	 * to tie this interface to a specific server name length.
-	 */
-	nmlen = strlen(conn->workBuffer.data);
-	newNotify = (PGnotify *) malloc(sizeof(PGnotify) + nmlen + 1);
-	if (newNotify)
-	{
-		newNotify->relname = (char *) newNotify + sizeof(PGnotify);
-		strcpy(newNotify->relname, conn->workBuffer.data);
-		/* fake up an empty-string extra field */
-		newNotify->extra = newNotify->relname + nmlen;
-		newNotify->be_pid = be_pid;
-		newNotify->next = NULL;
-		if (conn->notifyTail)
-			conn->notifyTail->next = newNotify;
-		else
-			conn->notifyHead = newNotify;
-		conn->notifyTail = newNotify;
-	}
-
-	return 0;
-}
-
-
-/*
- * PQgetCopyData - read a row of data from the backend during COPY OUT
- *
- * If successful, sets *buffer to point to a malloc'd row of data, and
- * returns row length (always > 0) as result.
- * Returns 0 if no row available yet (only possible if async is true),
- * -1 if end of copy (consult PQgetResult), or -2 if error (consult
- * PQerrorMessage).
- */
-int
-pqGetCopyData2(PGconn *conn, char **buffer, int async)
-{
-	bool		found;
-	int			msgLength;
-
-	for (;;)
-	{
-		/*
-		 * Do we have a complete line of data?
-		 */
-		conn->inCursor = conn->inStart;
-		found = false;
-		while (conn->inCursor < conn->inEnd)
-		{
-			char		c = conn->inBuffer[conn->inCursor++];
-
-			if (c == '\n')
-			{
-				found = true;
-				break;
-			}
-		}
-		if (!found)
-			goto nodata;
-		msgLength = conn->inCursor - conn->inStart;
-
-		/*
-		 * If it's the end-of-data marker, consume it, exit COPY_OUT mode, and
-		 * let caller read status with PQgetResult().
-		 */
-		if (msgLength == 3 &&
-			strncmp(&conn->inBuffer[conn->inStart], "\\.\n", 3) == 0)
-		{
-			conn->inStart = conn->inCursor;
-			conn->asyncStatus = PGASYNC_BUSY;
-			return -1;
-		}
-
-		/*
-		 * Pass the line back to the caller.
-		 */
-		*buffer = (char *) malloc(msgLength + 1);
-		if (*buffer == NULL)
-		{
-			appendPQExpBufferStr(&conn->errorMessage,
-								 libpq_gettext("out of memory\n"));
-			return -2;
-		}
-		memcpy(*buffer, &conn->inBuffer[conn->inStart], msgLength);
-		(*buffer)[msgLength] = '\0';	/* Add terminating null */
-
-		/* Mark message consumed */
-		conn->inStart = conn->inCursor;
-
-		return msgLength;
-
-nodata:
-		/* Don't block if async read requested */
-		if (async)
-			return 0;
-		/* Need to load more data */
-		if (pqWait(true, false, conn) ||
-			pqReadData(conn) < 0)
-			return -2;
-	}
-}
-
-
-/*
- * PQgetline - gets a newline-terminated string from the backend.
- *
- * See fe-exec.c for documentation.
- */
-int
-pqGetline2(PGconn *conn, char *s, int maxlen)
-{
-	int			result = 1;		/* return value if buffer overflows */
-
-	if (conn->sock == PGINVALID_SOCKET ||
-		conn->asyncStatus != PGASYNC_COPY_OUT)
-	{
-		*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)
-	{
-		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)
-			{
-				result = EOF;
-				break;
-			}
-		}
-	}
-	*s = '\0';
-
-	return result;
-}
-
-/*
- * PQgetlineAsync - gets a COPY data row without blocking.
- *
- * See fe-exec.c for documentation.
- */
-int
-pqGetlineAsync2(PGconn *conn, char *buffer, int bufsize)
-{
-	int			avail;
-
-	if (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.)
-	 */
-
-	conn->inCursor = conn->inStart;
-
-	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;
-		}
-	}
-
-	/*
-	 * 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.
-	 */
-	if (avail == 0 && bufsize > 3)
-	{
-		conn->inStart = conn->inCursor - 3;
-		return bufsize - 3;
-	}
-	return 0;
-}
-
-/*
- * PQendcopy
- *
- * See fe-exec.c for documentation.
- */
-int
-pqEndcopy2(PGconn *conn)
-{
-	PGresult   *result;
-
-	if (conn->asyncStatus != PGASYNC_COPY_IN &&
-		conn->asyncStatus != PGASYNC_COPY_OUT)
-	{
-		appendPQExpBufferStr(&conn->errorMessage,
-							 libpq_gettext("no COPY in progress\n"));
-		return 1;
-	}
-
-	/*
-	 * make sure no data is waiting to be sent, abort if we are non-blocking
-	 * and the flush fails
-	 */
-	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;
-
-	/* Wait for the completion response */
-	result = PQgetResult(conn);
-
-	/* Expecting a successful result */
-	if (result && result->resultStatus == PGRES_COMMAND_OK)
-	{
-		PQclear(result);
-		return 0;
-	}
-
-	/*
-	 * 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.
-	 */
-	if (conn->errorMessage.len > 0)
-	{
-		/* We have to strip the trailing newline ... pain in neck... */
-		char		svLast = conn->errorMessage.data[conn->errorMessage.len - 1];
-
-		if (svLast == '\n')
-			conn->errorMessage.data[conn->errorMessage.len - 1] = '\0';
-		pqInternalNotice(&conn->noticeHooks, "%s", conn->errorMessage.data);
-		conn->errorMessage.data[conn->errorMessage.len - 1] = svLast;
-	}
-
-	PQclear(result);
-
-	/*
-	 * 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...)
-	 */
-	pqInternalNotice(&conn->noticeHooks,
-					 "lost synchronization with server, resetting connection");
-
-	/*
-	 * 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);
-
-	return 1;
-}
-
-
-/*
- * PQfn - Send a function call to the POSTGRES backend.
- *
- * See fe-exec.c for documentation.
- */
-PGresult *
-pqFunctionCall2(PGconn *conn, Oid fnid,
-				int *result_buf, int *actual_result_len,
-				int result_is_int,
-				const PQArgBlock *args, int nargs)
-{
-	bool		needInput = false;
-	ExecStatusType status = PGRES_FATAL_ERROR;
-	char		id;
-	int			i;
-
-	/* PQfn already validated connection state */
-
-	if (pqPutMsgStart('F', false, conn) < 0 ||	/* function call msg */
-		pqPuts(" ", conn) < 0 ||	/* dummy string */
-		pqPutInt(fnid, 4, conn) != 0 || /* function id */
-		pqPutInt(nargs, 4, conn) != 0)	/* # of args */
-	{
-		/* error message should be set up already */
-		return NULL;
-	}
-
-	for (i = 0; i < nargs; ++i)
-	{							/* len.int4 + contents	   */
-		if (pqPutInt(args[i].len, 4, conn))
-			return NULL;
-
-		if (args[i].isint)
-		{
-			if (pqPutInt(args[i].u.integer, 4, conn))
-				return NULL;
-		}
-		else
-		{
-			if (pqPutnchar((char *) args[i].u.ptr, args[i].len, conn))
-				return NULL;
-		}
-	}
-
-	if (pqPutMsgEnd(conn) < 0 ||
-		pqFlush(conn))
-		return NULL;
-
-	for (;;)
-	{
-		if (needInput)
-		{
-			/* Wait for some data to arrive (or for the channel to close) */
-			if (pqWait(true, false, conn) ||
-				pqReadData(conn) < 0)
-				break;
-		}
-
-		/*
-		 * Scan the message. If we run out of data, loop around to try again.
-		 */
-		conn->inCursor = conn->inStart;
-		needInput = true;
-
-		if (pqGetc(&id, conn))
-			continue;
-
-		/*
-		 * We should see V or E response to the command, but might get N
-		 * and/or A notices first. We also need to swallow the final Z before
-		 * returning.
-		 */
-		switch (id)
-		{
-			case 'V':			/* function result */
-				if (pqGetc(&id, conn))
-					continue;
-				if (id == 'G')
-				{
-					/* function returned nonempty value */
-					if (pqGetInt(actual_result_len, 4, conn))
-						continue;
-					if (result_is_int)
-					{
-						if (pqGetInt(result_buf, 4, conn))
-							continue;
-					}
-					else
-					{
-						if (pqGetnchar((char *) result_buf,
-									   *actual_result_len,
-									   conn))
-							continue;
-					}
-					if (pqGetc(&id, conn))	/* get the last '0' */
-						continue;
-				}
-				if (id == '0')
-				{
-					/* correctly finished function result message */
-					status = PGRES_COMMAND_OK;
-				}
-				else
-				{
-					/* The backend violates the protocol. */
-					appendPQExpBuffer(&conn->errorMessage,
-									  libpq_gettext("protocol error: id=0x%x\n"),
-									  id);
-					pqSaveErrorResult(conn);
-					conn->inStart = conn->inCursor;
-					return pqPrepareAsyncResult(conn);
-				}
-				break;
-			case 'E':			/* error return */
-				if (pqGetErrorNotice2(conn, true))
-					continue;
-				status = PGRES_FATAL_ERROR;
-				break;
-			case 'A':			/* notify message */
-				/* handle notify and go back to processing return values */
-				if (getNotify(conn))
-					continue;
-				break;
-			case 'N':			/* notice */
-				/* handle notice and go back to processing return values */
-				if (pqGetErrorNotice2(conn, false))
-					continue;
-				break;
-			case 'Z':			/* backend is ready for new query */
-				/* consume the message and exit */
-				conn->inStart = conn->inCursor;
-				/* if we saved a result object (probably an error), use it */
-				if (conn->result)
-					return pqPrepareAsyncResult(conn);
-				return PQmakeEmptyPGresult(conn, status);
-			default:
-				/* The backend violates the protocol. */
-				appendPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("protocol error: id=0x%x\n"),
-								  id);
-				pqSaveErrorResult(conn);
-				conn->inStart = conn->inCursor;
-				return pqPrepareAsyncResult(conn);
-		}
-		/* Completed this message, keep going */
-		conn->inStart = conn->inCursor;
-		needInput = false;
-	}
-
-	/*
-	 * We fall out of the loop only upon failing to read data.
-	 * conn->errorMessage has been set by pqWait or pqReadData. We want to
-	 * append it to any already-received error message.
-	 */
-	pqSaveErrorResult(conn);
-	return pqPrepareAsyncResult(conn);
-}
-
-
-/*
- * Construct startup packet
- *
- * Returns a malloc'd packet buffer, or NULL if out of memory
- */
-char *
-pqBuildStartupPacket2(PGconn *conn, int *packetlen,
-					  const PQEnvironmentOption *options)
-{
-	StartupPacket *startpacket;
-
-	*packetlen = sizeof(StartupPacket);
-	startpacket = (StartupPacket *) malloc(sizeof(StartupPacket));
-	if (!startpacket)
-		return NULL;
-
-	MemSet(startpacket, 0, sizeof(StartupPacket));
-
-	startpacket->protoVersion = pg_hton32(conn->pversion);
-
-	/* strncpy is safe here: postmaster will handle full fields correctly */
-	strncpy(startpacket->user, conn->pguser, SM_USER);
-	strncpy(startpacket->database, conn->dbName, SM_DATABASE);
-	strncpy(startpacket->tty, conn->pgtty, SM_TTY);
-
-	if (conn->pgoptions)
-		strncpy(startpacket->options, conn->pgoptions, SM_OPTIONS);
-
-	return (char *) startpacket;
-}
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index e4ee9d69d25..2ca8c057b9d 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -1843,7 +1843,7 @@ pqEndcopy3(PGconn *conn)
 	if (conn->asyncStatus == PGASYNC_COPY_IN ||
 		conn->asyncStatus == PGASYNC_COPY_BOTH)
 	{
-		if (pqPutMsgStart('c', false, conn) < 0 ||
+		if (pqPutMsgStart('c', conn) < 0 ||
 			pqPutMsgEnd(conn) < 0)
 			return 1;
 
@@ -1853,7 +1853,7 @@ pqEndcopy3(PGconn *conn)
 		 */
 		if (conn->queryclass != PGQUERY_SIMPLE)
 		{
-			if (pqPutMsgStart('S', false, conn) < 0 ||
+			if (pqPutMsgStart('S', conn) < 0 ||
 				pqPutMsgEnd(conn) < 0)
 				return 1;
 		}
@@ -1933,7 +1933,7 @@ pqFunctionCall3(PGconn *conn, Oid fnid,
 
 	/* PQfn already validated connection state */
 
-	if (pqPutMsgStart('F', false, conn) < 0 ||	/* function call msg */
+	if (pqPutMsgStart('F', conn) < 0 || /* function call msg */
 		pqPutInt(fnid, 4, conn) < 0 ||	/* function id */
 		pqPutInt(1, 2, conn) < 0 || /* # of format codes */
 		pqPutInt(1, 2, conn) < 0 || /* format code: BINARY */
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index c266ad5b138..f9ca8c5cc62 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -60,7 +60,6 @@ typedef enum
 									 * postmaster.        */
 	CONNECTION_AUTH_OK,			/* Received authentication; waiting for
 								 * backend startup. */
-	CONNECTION_SETENV,			/* Negotiating environment. */
 	CONNECTION_SSL_STARTUP,		/* Negotiating SSL. */
 	CONNECTION_NEEDED,			/* Internal state: connect() needed */
 	CONNECTION_CHECK_WRITABLE,	/* Check if we could make a writable
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 4db498369c7..6e831a838f2 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -232,22 +232,6 @@ typedef enum
 	PGQUERY_DESCRIBE			/* Describe Statement or Portal */
 } PGQueryClass;
 
-/* PGSetenvStatusType defines the state of the pqSetenv state machine */
-
-/* (this is used only for 2.0-protocol connections) */
-typedef enum
-{
-	SETENV_STATE_CLIENT_ENCODING_SEND,	/* About to send an Environment Option */
-	SETENV_STATE_CLIENT_ENCODING_WAIT,	/* Waiting for above send to complete */
-	SETENV_STATE_OPTION_SEND,	/* About to send an Environment Option */
-	SETENV_STATE_OPTION_WAIT,	/* Waiting for above send to complete */
-	SETENV_STATE_QUERY1_SEND,	/* About to send a status query */
-	SETENV_STATE_QUERY1_WAIT,	/* Waiting for query to complete */
-	SETENV_STATE_QUERY2_SEND,	/* About to send a status query */
-	SETENV_STATE_QUERY2_WAIT,	/* Waiting for query to complete */
-	SETENV_STATE_IDLE
-} PGSetenvStatusType;
-
 /* Typedef for the EnvironmentOptions[] array */
 typedef struct PQEnvironmentOption
 {
@@ -426,7 +410,6 @@ struct pg_conn
 	struct addrinfo *addrlist;	/* list of addresses for current connhost */
 	struct addrinfo *addr_cur;	/* the one currently being tried */
 	int			addrlist_family;	/* needed to know how to free addrlist */
-	PGSetenvStatusType setenv_state;	/* for 2.0 protocol only */
 	const PQEnvironmentOption *next_eo;
 	bool		send_appname;	/* okay to send application_name? */
 
@@ -617,22 +600,6 @@ extern void pqSaveParameterStatus(PGconn *conn, const char *name,
 extern int	pqRowProcessor(PGconn *conn, const char **errmsgp);
 extern int	PQsendQueryContinue(PGconn *conn, const char *query);
 
-/* === in fe-protocol2.c === */
-
-extern PostgresPollingStatusType pqSetenvPoll(PGconn *conn);
-
-extern char *pqBuildStartupPacket2(PGconn *conn, int *packetlen,
-								   const PQEnvironmentOption *options);
-extern void pqParseInput2(PGconn *conn);
-extern int	pqGetCopyData2(PGconn *conn, char **buffer, int async);
-extern int	pqGetline2(PGconn *conn, char *s, int maxlen);
-extern int	pqGetlineAsync2(PGconn *conn, char *buffer, int bufsize);
-extern int	pqEndcopy2(PGconn *conn);
-extern PGresult *pqFunctionCall2(PGconn *conn, Oid fnid,
-								 int *result_buf, int *actual_result_len,
-								 int result_is_int,
-								 const PQArgBlock *args, int nargs);
-
 /* === in fe-protocol3.c === */
 
 extern char *pqBuildStartupPacket3(PGconn *conn, int *packetlen,
@@ -669,7 +636,7 @@ extern int	pqSkipnchar(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, bool force_len, 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);
-- 
2.30.0

