diff options
Diffstat (limited to 'src')
32 files changed, 287 insertions, 3051 deletions
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 @@ -456,84 +379,6 @@ printtup(TupleTableSlot *slot, DestReceiver *self) } /* ---------------- - * 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 796ca7b3f7b..f05e2d23476 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -1126,13 +1126,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 315b16fd7af..ce24a1528bd 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'); @@ -780,42 +739,6 @@ BeginCopyTo(ParseState *pstate, } /* - * 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. */ void @@ -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 baa0712c0f7..994251e7d9d 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); @@ -880,19 +871,6 @@ CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail) 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. */ @@ -1042,19 +1020,6 @@ pg_GSS_recvauth(Port *port) 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. */ @@ -1324,19 +1289,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. */ r = AcquireCredentialsHandle(NULL, diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c index 27a298f1101..4c7b1e7bfdf 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; @@ -200,7 +181,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); @@ -250,8 +230,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); } /* -------------------------------- @@ -1158,58 +1136,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. @@ -1236,9 +1162,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 @@ -1354,28 +1280,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) { @@ -1536,8 +1440,6 @@ socket_is_send_pending(void) /* -------------------------------- * Message-level I/O routines begin here. - * - * These routines understand about the old-style COPY OUT protocol. * -------------------------------- */ @@ -1545,20 +1447,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 @@ -1570,20 +1465,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; @@ -1621,37 +1516,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 3f1ce135a85..edab95a19e4 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -1934,7 +1934,7 @@ static int ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done) { int32 len; - void *buf; + char *buf; ProtocolVersion proto; MemoryContext oldcontext; @@ -1984,15 +1984,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) { @@ -2115,7 +2112,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; @@ -2129,7 +2126,7 @@ retry1: while (offset < len) { - char *nameptr = ((char *) buf) + offset; + char *nameptr = buf + offset; int32 valoffset; char *valptr; @@ -2138,7 +2135,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); @@ -2223,27 +2220,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 bb5ccb4578b..8a0332dde9d 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_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; 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 4bcf705a30d..3fd1a5fbe26 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -6306,11 +6306,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 925fe34a3fd..7a95465111a 100644 --- a/src/bin/psql/common.c +++ b/src/bin/psql/common.c @@ -2154,9 +2154,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) @@ -2177,9 +2174,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) @@ -2200,10 +2194,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 e4e5c215655..b20deeb5550 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. @@ -67,7 +63,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); @@ -75,7 +70,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 9812a14662d..c16a7bd9f22 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -2289,10 +2289,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: @@ -2956,12 +2952,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, @@ -3247,19 +3239,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)) - { - /* We'll come back when there is more data */ - return PGRES_POLLING_READING; - } - } - else + /* Read message length word */ + if (pqGetInt(&msgLength, 4, conn)) { - /* Set phony message length to disable checks below */ - msgLength = 8; + /* We'll come back when there is more data */ + return PGRES_POLLING_READING; } /* @@ -3268,7 +3252,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)) { @@ -3296,25 +3282,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; @@ -3335,21 +3307,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; @@ -3434,33 +3395,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. * @@ -3567,15 +3501,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; @@ -3596,17 +3521,9 @@ keep_going: /* We will come back to here until there is * If the server didn't report * "default_transaction_read_only" or "in_hot_standby" at * startup, we must determine its state by sending the - * query "SHOW transaction_read_only". Servers before 7.4 - * lack the transaction_read_only GUC, but by the same - * token they don't have any read-only mode, so we may - * just assume the results. + * query "SHOW transaction_read_only". This GUC exists in + * all server versions that support 3.0 protocol. */ - if (conn->sversion < 70400) - { - conn->default_transaction_read_only = PG_BOOL_NO; - conn->in_hot_standby = PG_BOOL_NO; - } - if (conn->default_transaction_read_only == PG_BOOL_UNKNOWN || conn->in_hot_standby == PG_BOOL_UNKNOWN) { @@ -3719,39 +3636,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: { /* @@ -4042,7 +3926,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->default_transaction_read_only = PG_BOOL_UNKNOWN; @@ -4259,7 +4142,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); } @@ -4652,16 +4535,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. */ @@ -6917,13 +6797,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 a5507538555..9a038043b2c 100644 --- a/src/interfaces/libpq/fe-exec.c +++ b/src/interfaces/libpq/fe-exec.c @@ -1221,7 +1221,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) { @@ -1255,7 +1255,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, @@ -1330,16 +1330,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; @@ -1365,7 +1357,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; @@ -1397,7 +1389,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, @@ -1478,7 +1470,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 @@ -1496,14 +1488,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. @@ -1512,7 +1496,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; @@ -1536,7 +1520,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; @@ -1603,21 +1587,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; @@ -1718,10 +1702,7 @@ PQconsumeInput(PGconn *conn) static void parseInput(PGconn *conn) { - if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) - pqParseInput3(conn); - else - pqParseInput2(conn); + pqParseInput3(conn); } /* @@ -1926,7 +1907,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, @@ -1949,7 +1930,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. @@ -1973,7 +1954,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, @@ -2020,41 +2001,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) { @@ -2195,23 +2155,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; @@ -2311,8 +2263,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) { @@ -2323,20 +2274,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; } @@ -2366,52 +2307,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 */ @@ -2450,10 +2370,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); } /* @@ -2492,10 +2409,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); } /* @@ -2535,10 +2449,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); } /* @@ -2573,10 +2484,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 @@ -2588,10 +2497,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); } @@ -2643,16 +2549,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); } @@ -2701,13 +2601,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-lobj.c b/src/interfaces/libpq/fe-lobj.c index b9c52eb50c2..ffd9926dc4e 100644 --- a/src/interfaces/libpq/fe-lobj.c +++ b/src/interfaces/libpq/fe-lobj.c @@ -884,38 +884,26 @@ lo_initialize(PGconn *conn) MemSet((char *) lobjfuncs, 0, sizeof(PGlobjfuncs)); /* - * Execute the query to get all the functions at once. In 7.3 and later - * we need to be schema-safe. lo_create only exists in 8.1 and up. - * lo_truncate only exists in 8.3 and up. + * Execute the query to get all the functions at once. (Not all of them + * may exist in older server versions.) */ - if (conn->sversion >= 70300) - query = "select proname, oid from pg_catalog.pg_proc " - "where proname in (" - "'lo_open', " - "'lo_close', " - "'lo_creat', " - "'lo_create', " - "'lo_unlink', " - "'lo_lseek', " - "'lo_lseek64', " - "'lo_tell', " - "'lo_tell64', " - "'lo_truncate', " - "'lo_truncate64', " - "'loread', " - "'lowrite') " - "and pronamespace = (select oid from pg_catalog.pg_namespace " - "where nspname = 'pg_catalog')"; - else - query = "select proname, oid from pg_proc " - "where proname = 'lo_open' " - "or proname = 'lo_close' " - "or proname = 'lo_creat' " - "or proname = 'lo_unlink' " - "or proname = 'lo_lseek' " - "or proname = 'lo_tell' " - "or proname = 'loread' " - "or proname = 'lowrite'"; + query = "select proname, oid from pg_catalog.pg_proc " + "where proname in (" + "'lo_open', " + "'lo_close', " + "'lo_creat', " + "'lo_create', " + "'lo_unlink', " + "'lo_lseek', " + "'lo_lseek64', " + "'lo_tell', " + "'lo_tell64', " + "'lo_truncate', " + "'lo_truncate64', " + "'loread', " + "'lowrite') " + "and pronamespace = (select oid from pg_catalog.pg_namespace " + "where nspname = 'pg_catalog')"; res = PQexec(conn, query); if (res == NULL) 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 47a098b4b93..fa9b62a8449 100644 --- a/src/interfaces/libpq/libpq-fe.h +++ b/src/interfaces/libpq/libpq-fe.h @@ -60,7 +60,7 @@ typedef enum * postmaster. */ CONNECTION_AUTH_OK, /* Received authentication; waiting for * backend startup. */ - CONNECTION_SETENV, /* Negotiating environment. */ + CONNECTION_SETENV, /* This state is no longer used. */ CONNECTION_SSL_STARTUP, /* Negotiating SSL. */ CONNECTION_NEEDED, /* Internal state: connect() needed */ CONNECTION_CHECK_WRITABLE, /* Checking if session is read-write. */ diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index 0c9e95f1a7a..8d51e6ed9ff 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -252,22 +252,6 @@ typedef enum PG_BOOL_NO /* No (false) */ } PGTernaryBool; -/* 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 { @@ -446,8 +430,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? */ /* Miscellaneous stuff */ @@ -639,22 +621,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, @@ -691,7 +657,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); diff --git a/src/interfaces/libpq/nls.mk b/src/interfaces/libpq/nls.mk index 3175ae3e1c3..f64101b2a6a 100644 --- a/src/interfaces/libpq/nls.mk +++ b/src/interfaces/libpq/nls.mk @@ -1,6 +1,6 @@ # src/interfaces/libpq/nls.mk CATALOG_NAME = libpq AVAIL_LANGUAGES = cs de es fr he it ja ko pl pt_BR ru sv tr uk zh_CN zh_TW -GETTEXT_FILES = fe-auth.c fe-auth-scram.c fe-connect.c fe-exec.c fe-gssapi-common.c fe-lobj.c fe-misc.c fe-protocol2.c fe-protocol3.c fe-secure.c fe-secure-common.c fe-secure-gssapi.c fe-secure-openssl.c win32.c +GETTEXT_FILES = fe-auth.c fe-auth-scram.c fe-connect.c fe-exec.c fe-gssapi-common.c fe-lobj.c fe-misc.c fe-protocol3.c fe-secure.c fe-secure-common.c fe-secure-gssapi.c fe-secure-openssl.c win32.c GETTEXT_TRIGGERS = libpq_gettext pqInternalNotice:2 GETTEXT_FLAGS = libpq_gettext:1:pass-c-format pqInternalNotice:2:c-format |