mirror of
https://github.com/postgres/postgres.git
synced 2025-08-27 07:42:10 +03:00
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 a connection. The impetus to do this now is that I'm working on a patch to COPY FROM, to always prefetch some data. We cannot do that safely with the old protocol, because it requires parsing the input one byte at a time to detect the end-of-copy marker. Reviewed-by: Tom Lane, Alvaro Herrera, John Naylor Discussion: https://www.postgresql.org/message-id/9ec25819-0a8a-d51a-17dc-4150bb3cca3b%40iki.fi
This commit is contained in:
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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_internal("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_internal("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;
|
||||
|
Reference in New Issue
Block a user