diff options
| author | Heikki Linnakangas | 2021-03-04 08:45:55 +0000 |
|---|---|---|
| committer | Heikki Linnakangas | 2021-03-04 08:45:55 +0000 |
| commit | 3174d69fb96a66173224e60ec7053b988d5ed4d9 (patch) | |
| tree | 2dbeb5e94ccfde05b8d40a15b88e1107220fb9b1 /src/interfaces | |
| parent | 0a687c8f103d217ff1ca8c34a644b380d89bb0ad (diff) | |
Remove server and libpq support for old FE/BE protocol version 2.
Protocol version 3 was introduced in PostgreSQL 7.4. There shouldn't be
many clients or servers left out there without version 3 support. But as
a courtesy, I kept just enough of the old protocol support that we can
still send the "unsupported protocol version" error in v2 format, so that
old clients can display the message properly. Likewise, libpq still
understands v2 ErrorResponse messages when establishing a connection.
The impetus to do this now is that I'm working on a patch to COPY
FROM, to always prefetch some data. We cannot do that safely with the
old protocol, because it requires parsing the input one byte at a time
to detect the end-of-copy marker.
Reviewed-by: Tom Lane, Alvaro Herrera, John Naylor
Discussion: https://www.postgresql.org/message-id/9ec25819-0a8a-d51a-17dc-4150bb3cca3b%40iki.fi
Diffstat (limited to 'src/interfaces')
| -rw-r--r-- | src/interfaces/libpq/Makefile | 1 | ||||
| -rw-r--r-- | src/interfaces/libpq/fe-auth.c | 8 | ||||
| -rw-r--r-- | src/interfaces/libpq/fe-connect.c | 160 | ||||
| -rw-r--r-- | src/interfaces/libpq/fe-exec.c | 231 | ||||
| -rw-r--r-- | src/interfaces/libpq/fe-lobj.c | 50 | ||||
| -rw-r--r-- | src/interfaces/libpq/fe-misc.c | 21 | ||||
| -rw-r--r-- | src/interfaces/libpq/fe-protocol2.c | 1610 | ||||
| -rw-r--r-- | src/interfaces/libpq/fe-protocol3.c | 6 | ||||
| -rw-r--r-- | src/interfaces/libpq/libpq-fe.h | 2 | ||||
| -rw-r--r-- | src/interfaces/libpq/libpq-int.h | 36 | ||||
| -rw-r--r-- | src/interfaces/libpq/nls.mk | 2 |
11 files changed, 113 insertions, 2014 deletions
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 |
