Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 3dfcf8c

Browse files
committed
Instead of sending application_name as a SET command after the connection
is made, include it in the startup-packet options. This makes it work more like every other libpq connection option, in particular it now has the same response to RESET ALL as the rest. This also saves one network round trip for new applications using application_name. The cost is that if the server is pre-8.5, it'll reject the startup packet altogether, forcing us to retry the entire connection cycle. But on balance we shouldn't be optimizing that case in preference to the behavior with a new server, especially when doing so creates visible behavioral oddities. Per discussion.
1 parent 925b32b commit 3dfcf8c

File tree

3 files changed

+91
-217
lines changed

3 files changed

+91
-217
lines changed

src/interfaces/libpq/fe-connect.c

Lines changed: 67 additions & 174 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.380 2009/11/29 20:14:53 petere Exp $
11+
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.381 2009/12/02 04:38:35 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -83,8 +83,18 @@ static int ldapServiceLookup(const char *purl, PQconninfoOption *options,
8383
#define PGPASSFILE "pgpass.conf"
8484
#endif
8585

86-
/* fall back options if they are not specified by arguments or defined
87-
by environment variables */
86+
/*
87+
* Pre-8.5 servers will return this SQLSTATE if asked to set
88+
* application_name in a startup packet. We hard-wire the value rather
89+
* than looking into errcodes.h since it reflects historical behavior
90+
* rather than that of the current code.
91+
*/
92+
#define ERRCODE_APPNAME_UNKNOWN "42704"
93+
94+
/*
95+
* fall back options if they are not specified by arguments or defined
96+
* by environment variables
97+
*/
8898
#define DefaultHost "localhost"
8999
#define DefaultTty ""
90100
#define DefaultOption ""
@@ -262,7 +272,6 @@ static int parseServiceInfo(PQconninfoOption *options,
262272
static char *pwdfMatchesString(char *buf, char *token);
263273
static char *PasswordFromFile(char *hostname, char *port, char *dbname,
264274
char *username);
265-
static PostgresPollingStatusType pqAppnamePoll(PGconn *conn);
266275
static void default_threadlock(int acquire);
267276

268277

@@ -901,6 +910,7 @@ connectDBStart(PGconn *conn)
901910
conn->addr_cur = addrs;
902911
conn->addrlist_family = hint.ai_family;
903912
conn->pversion = PG_PROTOCOL(3, 0);
913+
conn->send_appname = true;
904914
conn->status = CONNECTION_NEEDED;
905915

906916
/*
@@ -1075,7 +1085,7 @@ PQconnectPoll(PGconn *conn)
10751085
case CONNECTION_MADE:
10761086
break;
10771087

1078-
/* pqSetenvPoll/pqAppnamePoll will decide whether to proceed. */
1088+
/* We allow pqSetenvPoll to decide whether to proceed. */
10791089
case CONNECTION_SETENV:
10801090
break;
10811091

@@ -1880,6 +1890,35 @@ PQconnectPoll(PGconn *conn)
18801890
if (res->resultStatus != PGRES_FATAL_ERROR)
18811891
appendPQExpBuffer(&conn->errorMessage,
18821892
libpq_gettext("unexpected message from server during startup\n"));
1893+
else if (conn->send_appname &&
1894+
(conn->appname || conn->fbappname))
1895+
{
1896+
/*
1897+
* If we tried to send application_name, check to see
1898+
* if the error is about that --- pre-8.5 servers will
1899+
* reject it at this stage of the process. If so,
1900+
* close the connection and retry without sending
1901+
* application_name. We could possibly get a false
1902+
* SQLSTATE match here and retry uselessly, but there
1903+
* seems no great harm in that; we'll just get the
1904+
* same error again if it's unrelated.
1905+
*/
1906+
const char *sqlstate;
1907+
1908+
sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE);
1909+
if (sqlstate &&
1910+
strcmp(sqlstate, ERRCODE_APPNAME_UNKNOWN) == 0)
1911+
{
1912+
PQclear(res);
1913+
conn->send_appname = false;
1914+
/* Must drop the old connection */
1915+
pqsecure_close(conn);
1916+
closesocket(conn->sock);
1917+
conn->sock = -1;
1918+
conn->status = CONNECTION_NEEDED;
1919+
goto keep_going;
1920+
}
1921+
}
18831922

18841923
/*
18851924
* if the resultStatus is FATAL, then conn->errorMessage
@@ -1899,12 +1938,6 @@ PQconnectPoll(PGconn *conn)
18991938
conn->addrlist = NULL;
19001939
conn->addr_cur = NULL;
19011940

1902-
/*
1903-
* Note: To avoid changing the set of application-visible
1904-
* connection states, v2 environment setup and v3 application
1905-
* name setup both happen in the CONNECTION_SETENV state.
1906-
*/
1907-
19081941
/* Fire up post-connection housekeeping if needed */
19091942
if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
19101943
{
@@ -1913,59 +1946,43 @@ PQconnectPoll(PGconn *conn)
19131946
conn->next_eo = EnvironmentOptions;
19141947
return PGRES_POLLING_WRITING;
19151948
}
1916-
else if (conn->sversion >= 80500 &&
1917-
(conn->appname || conn->fbappname))
1918-
{
1919-
conn->status = CONNECTION_SETENV;
1920-
conn->appname_state = APPNAME_STATE_CMD_SEND;
1921-
return PGRES_POLLING_WRITING;
1922-
}
19231949

19241950
/* Otherwise, we are open for business! */
19251951
conn->status = CONNECTION_OK;
19261952
return PGRES_POLLING_OK;
19271953
}
19281954

19291955
case CONNECTION_SETENV:
1930-
{
1931-
PostgresPollingStatusType ret;
19321956

1933-
/*
1934-
* Do post-connection housekeeping (only needed in protocol
1935-
* 2.0), or send the application name in PG8.5+.
1936-
*
1937-
* We pretend that the connection is OK for the duration of
1938-
* these queries.
1939-
*/
1940-
conn->status = CONNECTION_OK;
1941-
1942-
if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
1943-
ret = pqSetenvPoll(conn);
1944-
else /* must be here to send app name */
1945-
ret = pqAppnamePoll(conn);
1946-
1947-
switch (ret)
1948-
{
1949-
case PGRES_POLLING_OK: /* Success */
1950-
break;
1957+
/*
1958+
* Do post-connection housekeeping (only needed in protocol 2.0).
1959+
*
1960+
* We pretend that the connection is OK for the duration of these
1961+
* queries.
1962+
*/
1963+
conn->status = CONNECTION_OK;
19511964

1952-
case PGRES_POLLING_READING: /* Still going */
1953-
conn->status = CONNECTION_SETENV;
1954-
return PGRES_POLLING_READING;
1965+
switch (pqSetenvPoll(conn))
1966+
{
1967+
case PGRES_POLLING_OK: /* Success */
1968+
break;
19551969

1956-
case PGRES_POLLING_WRITING: /* Still going */
1957-
conn->status = CONNECTION_SETENV;
1958-
return PGRES_POLLING_WRITING;
1970+
case PGRES_POLLING_READING: /* Still going */
1971+
conn->status = CONNECTION_SETENV;
1972+
return PGRES_POLLING_READING;
19591973

1960-
default:
1961-
goto error_return;
1962-
}
1974+
case PGRES_POLLING_WRITING: /* Still going */
1975+
conn->status = CONNECTION_SETENV;
1976+
return PGRES_POLLING_WRITING;
19631977

1964-
/* We are open for business! */
1965-
conn->status = CONNECTION_OK;
1966-
return PGRES_POLLING_OK;
1978+
default:
1979+
goto error_return;
19671980
}
19681981

1982+
/* We are open for business! */
1983+
conn->status = CONNECTION_OK;
1984+
return PGRES_POLLING_OK;
1985+
19691986
default:
19701987
appendPQExpBuffer(&conn->errorMessage,
19711988
libpq_gettext("invalid connection state %d, "
@@ -2031,7 +2048,6 @@ makeEmptyPGconn(void)
20312048
conn->options_valid = false;
20322049
conn->nonblocking = false;
20332050
conn->setenv_state = SETENV_STATE_IDLE;
2034-
conn->appname_state = APPNAME_STATE_IDLE;
20352051
conn->client_encoding = PG_SQL_ASCII;
20362052
conn->std_strings = false; /* unless server says differently */
20372053
conn->verbosity = PQERRORS_DEFAULT;
@@ -4048,129 +4064,6 @@ pqGetHomeDirectory(char *buf, int bufsize)
40484064
#endif
40494065
}
40504066

4051-
/*
4052-
* pqAppnamePoll
4053-
*
4054-
* Polls the process of passing the application name to the backend.
4055-
*
4056-
* Ideally, we'd include the appname in the startup packet, but that would
4057-
* cause old backends to reject the unknown parameter. So we send it in a
4058-
* separate query after we have determined the backend version. Once there
4059-
* is no interest in pre-8.5 backends, this should be folded into the startup
4060-
* packet logic.
4061-
*/
4062-
static PostgresPollingStatusType
4063-
pqAppnamePoll(PGconn *conn)
4064-
{
4065-
PGresult *res;
4066-
4067-
if (conn == NULL || conn->status == CONNECTION_BAD)
4068-
return PGRES_POLLING_FAILED;
4069-
4070-
/* Check whether there is any data for us */
4071-
switch (conn->appname_state)
4072-
{
4073-
/* This is a reading state. */
4074-
case APPNAME_STATE_CMD_WAIT:
4075-
{
4076-
/* Load waiting data */
4077-
int n = pqReadData(conn);
4078-
4079-
if (n < 0)
4080-
goto error_return;
4081-
if (n == 0)
4082-
return PGRES_POLLING_READING;
4083-
4084-
break;
4085-
}
4086-
4087-
/* This is a writing state, so we just proceed. */
4088-
case APPNAME_STATE_CMD_SEND:
4089-
break;
4090-
4091-
/* Should we raise an error if called when not active? */
4092-
case APPNAME_STATE_IDLE:
4093-
return PGRES_POLLING_OK;
4094-
4095-
default:
4096-
printfPQExpBuffer(&conn->errorMessage,
4097-
libpq_gettext("invalid appname state %d, "
4098-
"probably indicative of memory corruption\n"),
4099-
conn->appname_state);
4100-
goto error_return;
4101-
}
4102-
4103-
/* We will loop here until there is nothing left to do in this call. */
4104-
for (;;)
4105-
{
4106-
switch (conn->appname_state)
4107-
{
4108-
case APPNAME_STATE_CMD_SEND:
4109-
{
4110-
const char *val;
4111-
char escVal[NAMEDATALEN*2 + 1];
4112-
char setQuery[NAMEDATALEN*2 + 26 + 1];
4113-
4114-
/* Use appname if present, otherwise use fallback */
4115-
val = conn->appname ? conn->appname : conn->fbappname;
4116-
4117-
/*
4118-
* Escape the data as needed. We can truncate to NAMEDATALEN,
4119-
* so there's no need to cope with malloc.
4120-
*/
4121-
PQescapeStringConn(conn, escVal, val, NAMEDATALEN, NULL);
4122-
4123-
sprintf(setQuery, "SET application_name = '%s'", escVal);
4124-
4125-
if (!PQsendQuery(conn, setQuery))
4126-
goto error_return;
4127-
4128-
conn->appname_state = APPNAME_STATE_CMD_WAIT;
4129-
break;
4130-
}
4131-
4132-
case APPNAME_STATE_CMD_WAIT:
4133-
{
4134-
if (PQisBusy(conn))
4135-
return PGRES_POLLING_READING;
4136-
4137-
res = PQgetResult(conn);
4138-
4139-
if (res)
4140-
{
4141-
if (PQresultStatus(res) != PGRES_COMMAND_OK)
4142-
{
4143-
PQclear(res);
4144-
goto error_return;
4145-
}
4146-
PQclear(res);
4147-
/* Keep reading until PQgetResult returns NULL */
4148-
}
4149-
else
4150-
{
4151-
/* Query finished, so we're done */
4152-
conn->appname_state = APPNAME_STATE_IDLE;
4153-
return PGRES_POLLING_OK;
4154-
}
4155-
break;
4156-
}
4157-
4158-
default:
4159-
printfPQExpBuffer(&conn->errorMessage,
4160-
libpq_gettext("invalid appname state %d, "
4161-
"probably indicative of memory corruption\n"),
4162-
conn->appname_state);
4163-
goto error_return;
4164-
}
4165-
}
4166-
4167-
/* Unreachable */
4168-
4169-
error_return:
4170-
conn->appname_state = APPNAME_STATE_IDLE;
4171-
return PGRES_POLLING_FAILED;
4172-
}
4173-
41744067
/*
41754068
* To keep the API consistent, the locking stubs are always provided, even
41764069
* if they are not required.

0 commit comments

Comments
 (0)