aboutsummaryrefslogtreecommitdiff
path: root/src/interfaces/libpq/fe-connect.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2009-11-28 23:38:08 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2009-11-28 23:38:08 +0000
commit8217cfbd991856d25d73b0f7afcf43d99f90b653 (patch)
tree46538428417fcd360d976a151d87cee402a878be /src/interfaces/libpq/fe-connect.c
parentcb98f615383ccf2f9f339b4e812b23e840b12d9f (diff)
downloadpostgresql-8217cfbd991856d25d73b0f7afcf43d99f90b653.tar.gz
postgresql-8217cfbd991856d25d73b0f7afcf43d99f90b653.zip
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
Diffstat (limited to 'src/interfaces/libpq/fe-connect.c')
-rw-r--r--src/interfaces/libpq/fe-connect.c217
1 files changed, 188 insertions, 29 deletions
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 1be948505e5..f218dbe2c82 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -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:
+ {
+ PostgresPollingStatusType ret;
- /*
- * 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;
+ /*
+ * 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;
- switch (pqSetenvPoll(conn))
- {
- case PGRES_POLLING_OK: /* Success */
- break;
+ if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
+ ret = pqSetenvPoll(conn);
+ else /* must be here to send app name */
+ ret = pqAppnamePoll(conn);
- case PGRES_POLLING_READING: /* Still going */
- conn->status = CONNECTION_SETENV;
- return PGRES_POLLING_READING;
+ switch (ret)
+ {
+ case PGRES_POLLING_OK: /* Success */
+ break;
- case PGRES_POLLING_WRITING: /* Still going */
- conn->status = CONNECTION_SETENV;
- return PGRES_POLLING_WRITING;
+ case PGRES_POLLING_READING: /* Still going */
+ conn->status = CONNECTION_SETENV;
+ return PGRES_POLLING_READING;
- default:
- goto error_return;
- }
+ case PGRES_POLLING_WRITING: /* Still going */
+ conn->status = CONNECTION_SETENV;
+ return PGRES_POLLING_WRITING;
- /* We are open for business! */
- conn->status = CONNECTION_OK;
- return PGRES_POLLING_OK;
+ default:
+ goto error_return;
+ }
+
+ /* 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)
@@ -4006,6 +4042,129 @@ pqGetHomeDirectory(char *buf, int bufsize)
}
/*
+ * 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.
*/