1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-19 13:42:17 +03:00

Add support for an application_name parameter, which is displayed in

pg_stat_activity and recorded in log entries.

Dave Page, reviewed by Andres Freund
This commit is contained in:
Tom Lane
2009-11-28 23:38:08 +00:00
parent cb98f61538
commit 8217cfbd99
16 changed files with 479 additions and 95 deletions

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.377 2009/09/27 03:43:10 tgl Exp $
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.378 2009/11/28 23:38:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -164,6 +164,12 @@ static const PQconninfoOption PQconninfoOptions[] = {
{"options", "PGOPTIONS", DefaultOption, NULL,
"Backend-Debug-Options", "D", 40},
{"application_name", "PGAPPNAME", NULL, NULL,
"Application-Name", "", 64},
{"fallback_application_name", NULL, NULL, NULL,
"Fallback-Application-Name", "", 64},
#ifdef USE_SSL
/*
@@ -256,6 +262,7 @@ static int parseServiceInfo(PQconninfoOption *options,
static char *pwdfMatchesString(char *buf, char *token);
static char *PasswordFromFile(char *hostname, char *port, char *dbname,
char *username);
static PostgresPollingStatusType pqAppnamePoll(PGconn *conn);
static void default_threadlock(int acquire);
@@ -416,6 +423,10 @@ connectOptions1(PGconn *conn, const char *conninfo)
conn->pgtty = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "options");
conn->pgoptions = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "application_name");
conn->appname = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "fallback_application_name");
conn->fbappname = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "dbname");
conn->dbName = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "user");
@@ -1064,7 +1075,7 @@ PQconnectPoll(PGconn *conn)
case CONNECTION_MADE:
break;
/* We allow pqSetenvPoll to decide whether to proceed. */
/* pqSetenvPoll/pqAppnamePoll will decide whether to proceed. */
case CONNECTION_SETENV:
break;
@@ -1888,6 +1899,12 @@ keep_going: /* We will come back to here until there is
conn->addrlist = NULL;
conn->addr_cur = NULL;
/*
* Note: To avoid changing the set of application-visible
* connection states, v2 environment setup and v3 application
* name setup both happen in the CONNECTION_SETENV state.
*/
/* Fire up post-connection housekeeping if needed */
if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
{
@@ -1896,6 +1913,13 @@ keep_going: /* We will come back to here until there is
conn->next_eo = EnvironmentOptions;
return PGRES_POLLING_WRITING;
}
else if (conn->sversion >= 80500 &&
(conn->appname || conn->fbappname))
{
conn->status = CONNECTION_SETENV;
conn->appname_state = APPNAME_STATE_CMD_SEND;
return PGRES_POLLING_WRITING;
}
/* Otherwise, we are open for business! */
conn->status = CONNECTION_OK;
@@ -1903,42 +1927,49 @@ keep_going: /* We will come back to here until there is
}
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;
PostgresPollingStatusType ret;
case PGRES_POLLING_READING: /* Still going */
conn->status = CONNECTION_SETENV;
return PGRES_POLLING_READING;
/*
* Do post-connection housekeeping (only needed in protocol
* 2.0), or send the application name in PG8.5+.
*
* We pretend that the connection is OK for the duration of
* these queries.
*/
conn->status = CONNECTION_OK;
case PGRES_POLLING_WRITING: /* Still going */
conn->status = CONNECTION_SETENV;
return PGRES_POLLING_WRITING;
if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
ret = pqSetenvPoll(conn);
else /* must be here to send app name */
ret = pqAppnamePoll(conn);
default:
goto error_return;
switch (ret)
{
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;
}
/* We are open for business! */
conn->status = CONNECTION_OK;
return PGRES_POLLING_OK;
}
/* We are open for business! */
conn->status = CONNECTION_OK;
return PGRES_POLLING_OK;
default:
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext(
"invalid connection state %c, "
"probably indicative of memory corruption\n"
),
libpq_gettext("invalid connection state %d, "
"probably indicative of memory corruption\n"),
conn->status);
goto error_return;
}
@@ -2000,6 +2031,7 @@ makeEmptyPGconn(void)
conn->options_valid = false;
conn->nonblocking = false;
conn->setenv_state = SETENV_STATE_IDLE;
conn->appname_state = APPNAME_STATE_IDLE;
conn->client_encoding = PG_SQL_ASCII;
conn->std_strings = false; /* unless server says differently */
conn->verbosity = PQERRORS_DEFAULT;
@@ -2082,6 +2114,10 @@ freePGconn(PGconn *conn)
free(conn->connect_timeout);
if (conn->pgoptions)
free(conn->pgoptions);
if (conn->appname)
free(conn->appname);
if (conn->fbappname)
free(conn->fbappname);
if (conn->dbName)
free(conn->dbName);
if (conn->pguser)
@@ -4005,6 +4041,129 @@ pqGetHomeDirectory(char *buf, int bufsize)
#endif
}
/*
* pqAppnamePoll
*
* Polls the process of passing the application name to the backend.
*
* Ideally, we'd include the appname in the startup packet, but that would
* cause old backends to reject the unknown parameter. So we send it in a
* separate query after we have determined the backend version. Once there
* is no interest in pre-8.5 backends, this should be folded into the startup
* packet logic.
*/
static PostgresPollingStatusType
pqAppnamePoll(PGconn *conn)
{
PGresult *res;
if (conn == NULL || conn->status == CONNECTION_BAD)
return PGRES_POLLING_FAILED;
/* Check whether there is any data for us */
switch (conn->appname_state)
{
/* This is a reading state. */
case APPNAME_STATE_CMD_WAIT:
{
/* Load waiting data */
int n = pqReadData(conn);
if (n < 0)
goto error_return;
if (n == 0)
return PGRES_POLLING_READING;
break;
}
/* This is a writing state, so we just proceed. */
case APPNAME_STATE_CMD_SEND:
break;
/* Should we raise an error if called when not active? */
case APPNAME_STATE_IDLE:
return PGRES_POLLING_OK;
default:
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("invalid appname state %d, "
"probably indicative of memory corruption\n"),
conn->appname_state);
goto error_return;
}
/* We will loop here until there is nothing left to do in this call. */
for (;;)
{
switch (conn->appname_state)
{
case APPNAME_STATE_CMD_SEND:
{
const char *val;
char escVal[NAMEDATALEN*2 + 1];
char setQuery[NAMEDATALEN*2 + 26 + 1];
/* Use appname if present, otherwise use fallback */
val = conn->appname ? conn->appname : conn->fbappname;
/*
* Escape the data as needed. We can truncate to NAMEDATALEN,
* so there's no need to cope with malloc.
*/
PQescapeStringConn(conn, escVal, val, NAMEDATALEN, NULL);
sprintf(setQuery, "SET application_name = '%s'", escVal);
if (!PQsendQuery(conn, setQuery))
goto error_return;
conn->appname_state = APPNAME_STATE_CMD_WAIT;
break;
}
case APPNAME_STATE_CMD_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 we're done */
conn->appname_state = APPNAME_STATE_IDLE;
return PGRES_POLLING_OK;
}
break;
}
default:
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("invalid appname state %d, "
"probably indicative of memory corruption\n"),
conn->appname_state);
goto error_return;
}
}
/* Unreachable */
error_return:
conn->appname_state = APPNAME_STATE_IDLE;
return PGRES_POLLING_FAILED;
}
/*
* To keep the API consistent, the locking stubs are always provided, even
* if they are not required.

View File

@@ -12,7 +12,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.144 2009/07/24 17:58:31 tgl Exp $
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.145 2009/11/28 23:38:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -244,6 +244,14 @@ typedef enum
SETENV_STATE_IDLE
} PGSetenvStatusType;
/* PGAppnameStatusType defines the state of the PQAppname state machine */
typedef enum
{
APPNAME_STATE_CMD_SEND, /* About to send the appname */
APPNAME_STATE_CMD_WAIT, /* Waiting for above send to complete */
APPNAME_STATE_IDLE
} PGAppnameStatusType;
/* Typedef for the EnvironmentOptions[] array */
typedef struct PQEnvironmentOption
{
@@ -295,6 +303,8 @@ struct pg_conn
* displayed (OBSOLETE, NOT USED) */
char *connect_timeout; /* connection timeout (numeric string) */
char *pgoptions; /* options to start the backend with */
char *appname; /* application name */
char *fbappname; /* fallback application name */
char *dbName; /* database name */
char *pguser; /* Postgres username and password, if any */
char *pgpass;
@@ -349,6 +359,7 @@ struct pg_conn
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 */
PGAppnameStatusType appname_state;
const PQEnvironmentOption *next_eo;
/* Miscellaneous stuff */