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

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.234 2009/11/28 16:21:31 momjian Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.235 2009/11/28 23:38:06 tgl Exp $ -->
<chapter Id="runtime-config">
<title>Server Configuration</title>
@ -2882,6 +2882,26 @@ local0.* /var/log/postgresql
<variablelist>
<varlistentry id="guc-application-name" xreflabel="application_name">
<term><varname>application_name</varname> (<type>string</type>)</term>
<indexterm>
<primary><varname>application_name</> configuration parameter</primary>
</indexterm>
<listitem>
<para>
The <varname>application_name</varname> can be any string of less than
<symbol>NAMEDATALEN</> characters (64 characters in a standard build).
It is typically set by an application upon connection to the server.
The name will be displayed in the <structname>pg_stat_activity</> view
and included in CSV log entries. It can also be included in regular
log entries via the <xref linkend="guc-log-line-prefix"> parameter.
Only printable ASCII characters may be used in the
<varname>application_name</varname> value. Other characters will be
replaced with question marks (<literal>?</literal>).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>debug_print_parse</varname> (<type>boolean</type>)</term>
<term><varname>debug_print_rewritten</varname> (<type>boolean</type>)</term>
@ -3049,7 +3069,7 @@ local0.* /var/log/postgresql
that are replaced with status information as outlined below.
Unrecognized escapes are ignored. Other
characters are copied straight to the log line. Some escapes are
only recognized by session processes, and do not apply to
only recognized by session processes, and are ignored by
background processes such as the main server process.
This parameter can only be set in the <filename>postgresql.conf</>
file or on the server command line. The default is an empty string.
@ -3064,6 +3084,11 @@ local0.* /var/log/postgresql
</row>
</thead>
<tbody>
<row>
<entry><literal>%a</literal></entry>
<entry>Application name</entry>
<entry>yes</entry>
</row>
<row>
<entry><literal>%u</literal></entry>
<entry>User name</entry>
@ -3287,19 +3312,35 @@ FROM pg_stat_activity;
<title>Using CSV-Format Log Output</title>
<para>
Including <literal>csvlog</> in the <varname>log_destination</> list
provides a convenient way to import log files into a database table.
Including <literal>csvlog</> in the <varname>log_destination</> list
provides a convenient way to import log files into a database table.
This option emits log lines in comma-separated-value format,
with these columns: timestamp with milliseconds, user name, database
name, process ID, host:port number, session ID, per-session or -process line
number, command tag, session start time, virtual transaction ID,
regular transaction id, error severity, SQL state code, error message,
error message detail, hint, internal query that led to the error (if
any), character count of the error position thereof, error context,
with these columns:
timestamp with milliseconds,
user name,
database name,
process ID,
client host:port number,
session ID,
per-session line number,
command tag,
session start time,
virtual transaction ID,
regular transaction ID,
error severity,
SQL state code,
error message,
error message detail,
hint,
internal query that led to the error (if any),
character count of the error position therein,
error context,
user query that led to the error (if any and enabled by
<varname>log_min_error_statement</>), character count of the error
position thereof, location of the error in the PostgreSQL source code
(if <varname>log_error_verbosity</> is set to <literal>verbose</>).
<varname>log_min_error_statement</>),
character count of the error position therein,
location of the error in the PostgreSQL source code
(if <varname>log_error_verbosity</> is set to <literal>verbose</>),
and application name.
Here is a sample table definition for storing CSV-format log output:
<programlisting>
@ -3327,6 +3368,7 @@ CREATE TABLE postgres_log
query text,
query_pos integer,
location text,
application_name text,
PRIMARY KEY (session_id, session_line_num)
);
</programlisting>

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.289 2009/05/28 20:02:10 momjian Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.290 2009/11/28 23:38:06 tgl Exp $ -->
<chapter id="libpq">
<title><application>libpq</application> - C Library</title>
@ -249,6 +249,32 @@
</listitem>
</varlistentry>
<varlistentry id="libpq-connect-application-name" xreflabel="application_name">
<term><literal>application_name</literal></term>
<listitem>
<para>
Specifies a value for the <xref linkend="guc-application-name">
configuration parameter.
</para>
</listitem>
</varlistentry>
<varlistentry id="libpq-connect-fallback-application-name" xreflabel="fallback_application_name">
<term><literal>fallback_application_name</literal></term>
<listitem>
<para>
Specifies a fallback value for the <xref
linkend="guc-application-name"> configuration parameter.
This value will be used if no value has been given for
<literal>application_name</> via a connection parameter or the
<envar>PGAPPNAME</envar> environment variable. Specifying
a fallback name is useful in generic utility programs that
wish to set a default application name but allow it to be
overridden by the user.
</para>
</listitem>
</varlistentry>
<varlistentry id="libpq-connect-tty" xreflabel="tty">
<term><literal>tty</literal></term>
<listitem>
@ -5672,7 +5698,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm>
<primary><envar>PGHOST</envar></primary>
</indexterm>
<envar>PGHOST</envar> behaves the same as <xref
<envar>PGHOST</envar> behaves the same as the <xref
linkend="libpq-connect-host"> connection parameter.
</para>
</listitem>
@ -5682,7 +5708,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm>
<primary><envar>PGHOSTADDR</envar></primary>
</indexterm>
<envar>PGHOSTADDR</envar> behaves the same as <xref
<envar>PGHOSTADDR</envar> behaves the same as the <xref
linkend="libpq-connect-hostaddr"> connection parameter.
This can be set instead of or in addition to <envar>PGHOST</envar>
to avoid DNS lookup overhead.
@ -5694,7 +5720,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm>
<primary><envar>PGPORT</envar></primary>
</indexterm>
<envar>PGPORT</envar> behaves the same as <xref
<envar>PGPORT</envar> behaves the same as the <xref
linkend="libpq-connect-port"> connection parameter.
</para>
</listitem>
@ -5704,7 +5730,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm>
<primary><envar>PGDATABASE</envar></primary>
</indexterm>
<envar>PGDATABASE</envar> behaves the same as <xref
<envar>PGDATABASE</envar> behaves the same as the <xref
linkend="libpq-connect-dbname"> connection parameter.
</para>
</listitem>
@ -5714,9 +5740,8 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm>
<primary><envar>PGUSER</envar></primary>
</indexterm>
<envar>PGUSER</envar> behaves the same as <xref
<envar>PGUSER</envar> behaves the same as the <xref
linkend="libpq-connect-user"> connection parameter.
database.
</para>
</listitem>
@ -5725,12 +5750,12 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm>
<primary><envar>PGPASSWORD</envar></primary>
</indexterm>
<envar>PGPASSWORD</envar> behaves the same as <xref
<envar>PGPASSWORD</envar> behaves the same as the <xref
linkend="libpq-connect-password"> connection parameter.
Use of this environment variable
is not recommended for security reasons (some operating systems
is not recommended for security reasons, as some operating systems
allow non-root users to see process environment variables via
<application>ps</>); instead consider using the
<application>ps</>; instead consider using the
<filename>~/.pgpass</> file (see <xref linkend="libpq-pgpass">).
</para>
</listitem>
@ -5751,7 +5776,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm>
<primary><envar>PGSERVICE</envar></primary>
</indexterm>
<envar>PGSERVICE</envar> behaves the same as <xref
<envar>PGSERVICE</envar> behaves the same as the <xref
linkend="libpq-connect-service"> connection parameter.
</para>
</listitem>
@ -5777,17 +5802,27 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm>
<primary><envar>PGOPTIONS</envar></primary>
</indexterm>
<envar>PGOPTIONS</envar> behaves the same as <xref
<envar>PGOPTIONS</envar> behaves the same as the <xref
linkend="libpq-connect-options"> connection parameter.
</para>
</listitem>
<listitem>
<para>
<indexterm>
<primary><envar>PGAPPNAME</envar></primary>
</indexterm>
<envar>PGAPPNAME</envar> behaves the same as the <xref
linkend="libpq-connect-application-name"> connection parameter.
</para>
</listitem>
<listitem>
<para>
<indexterm>
<primary><envar>PGSSLMODE</envar></primary>
</indexterm>
<envar>PGSSLMODE</envar> behaves the same as <xref
<envar>PGSSLMODE</envar> behaves the same as the <xref
linkend="libpq-connect-sslmode"> connection parameter.
</para>
</listitem>
@ -5797,7 +5832,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm>
<primary><envar>PGREQUIRESSL</envar></primary>
</indexterm>
<envar>PGREQUIRESSL</envar> behaves the same as <xref
<envar>PGREQUIRESSL</envar> behaves the same as the <xref
linkend="libpq-connect-requiressl"> connection parameter.
</para>
</listitem>
@ -5807,7 +5842,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm>
<primary><envar>PGSSLCERT</envar></primary>
</indexterm>
<envar>PGSSLCERT</envar> behaves the same as <xref
<envar>PGSSLCERT</envar> behaves the same as the <xref
linkend="libpq-connect-sslcert"> connection parameter.
</para>
</listitem>
@ -5817,7 +5852,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm>
<primary><envar>PGSSLKEY</envar></primary>
</indexterm>
<envar>PGSSLKEY</envar> behaves the same as <xref
<envar>PGSSLKEY</envar> behaves the same as the <xref
linkend="libpq-connect-sslkey"> connection parameter.
</para>
</listitem>
@ -5827,7 +5862,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm>
<primary><envar>PGSSLROOTCERT</envar></primary>
</indexterm>
<envar>PGSSLROOTCERT</envar> behaves the same as <xref
<envar>PGSSLROOTCERT</envar> behaves the same as the <xref
linkend="libpq-connect-sslrootcert"> connection parameter.
</para>
</listitem>
@ -5837,7 +5872,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm>
<primary><envar>PGSSLCRL</envar></primary>
</indexterm>
<envar>PGSSLCRL</envar> behaves the same as <xref
<envar>PGSSLCRL</envar> behaves the same as the <xref
linkend="libpq-connect-sslcrl"> connection parameter.
</para>
</listitem>
@ -5847,7 +5882,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm>
<primary><envar>PGKRBSRVNAME</envar></primary>
</indexterm>
<envar>PGKRBSRVNAME</envar> behaves the same as <xref
<envar>PGKRBSRVNAME</envar> behaves the same as the <xref
linkend="libpq-connect-krbsrvname"> connection parameter.
</para>
</listitem>
@ -5857,7 +5892,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm>
<primary><envar>PGGSSLIB</envar></primary>
</indexterm>
<envar>PGGSSLIB</envar> behaves the same as <xref
<envar>PGGSSLIB</envar> behaves the same as the <xref
linkend="libpq-connect-gsslib"> connection parameter.
</para>
</listitem>
@ -5867,7 +5902,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm>
<primary><envar>PGCONNECT_TIMEOUT</envar></primary>
</indexterm>
<envar>PGCONNECT_TIMEOUT</envar> behaves the same as <xref
<envar>PGCONNECT_TIMEOUT</envar> behaves the same as the <xref
linkend="libpq-connect-connect-timeout"> connection parameter.
</para>
</listitem>

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/monitoring.sgml,v 1.71 2009/06/26 22:08:17 tgl Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/monitoring.sgml,v 1.72 2009/11/28 23:38:07 tgl Exp $ -->
<chapter id="monitoring">
<title>Monitoring Database Activity</title>
@ -238,12 +238,12 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
name, process <acronym>ID</>, user OID, user name, current query,
query's waiting status, time at which the current transaction and
current query began execution, time at which the process was
started, and client's address and port number. The columns that
report data on the current query are available unless the parameter
<varname>track_activities</varname> has been turned off.
Furthermore, these columns are only visible if the user examining
the view is a superuser or the same as the user owning the process
being reported on.
started, client's address and port number, and application name.
The columns that report data on the current query are available unless
the parameter <varname>track_activities</varname> has been turned off.
Furthermore, these columns and the application name are only visible if
the user examining the view is a superuser or the same as the user
owning the process being reported on.
</entry>
</row>

View File

@ -3,7 +3,7 @@
*
* Copyright (c) 1996-2009, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/backend/catalog/system_views.sql,v 1.61 2009/10/07 22:14:18 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/catalog/system_views.sql,v 1.62 2009/11/28 23:38:07 tgl Exp $
*/
CREATE VIEW pg_roles AS
@ -339,7 +339,8 @@ CREATE VIEW pg_stat_activity AS
S.query_start,
S.backend_start,
S.client_addr,
S.client_port
S.client_port,
S.application_name
FROM pg_database D, pg_stat_get_activity(NULL) AS S, pg_authid U
WHERE S.datid = D.oid AND
S.usesysid = U.oid;

View File

@ -13,7 +13,7 @@
*
* Copyright (c) 2001-2009, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.192 2009/10/02 22:49:50 tgl Exp $
* $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.193 2009/11/28 23:38:07 tgl Exp $
* ----------
*/
#include "postgres.h"
@ -2073,6 +2073,7 @@ pgstat_fetch_global(void)
static PgBackendStatus *BackendStatusArray = NULL;
static PgBackendStatus *MyBEEntry = NULL;
static char *BackendAppnameBuffer = NULL;
static char *BackendActivityBuffer = NULL;
@ -2084,14 +2085,17 @@ BackendStatusShmemSize(void)
{
Size size;
size = add_size(mul_size(sizeof(PgBackendStatus), MaxBackends),
size = mul_size(sizeof(PgBackendStatus), MaxBackends);
size = add_size(size,
mul_size(NAMEDATALEN, MaxBackends));
size = add_size(size,
mul_size(pgstat_track_activity_query_size, MaxBackends));
return size;
}
/*
* Initialize the shared status array and activity string buffer during
* postmaster startup.
* Initialize the shared status array and activity/appname string buffers
* during postmaster startup.
*/
void
CreateSharedBackendStatus(void)
@ -2114,6 +2118,24 @@ CreateSharedBackendStatus(void)
MemSet(BackendStatusArray, 0, size);
}
/* Create or attach to the shared appname buffer */
size = mul_size(NAMEDATALEN, MaxBackends);
BackendAppnameBuffer = (char *)
ShmemInitStruct("Backend Application Name Buffer", size, &found);
if (!found)
{
MemSet(BackendAppnameBuffer, 0, size);
/* Initialize st_appname pointers. */
buffer = BackendAppnameBuffer;
for (i = 0; i < MaxBackends; i++)
{
BackendStatusArray[i].st_appname = buffer;
buffer += NAMEDATALEN;
}
}
/* Create or attach to the shared activity buffer */
size = mul_size(pgstat_track_activity_query_size, MaxBackends);
BackendActivityBuffer = (char *)
@ -2159,7 +2181,8 @@ pgstat_initialize(void)
* pgstat_bestart() -
*
* Initialize this backend's entry in the PgBackendStatus array.
* Called from InitPostgres. MyDatabaseId and session userid must be set
* Called from InitPostgres.
* MyDatabaseId, session userid, and application_name must be set
* (hence, this cannot be combined with pgstat_initialize).
* ----------
*/
@ -2214,12 +2237,18 @@ pgstat_bestart(void)
beentry->st_userid = userid;
beentry->st_clientaddr = clientaddr;
beentry->st_waiting = false;
beentry->st_appname[0] = '\0';
beentry->st_activity[0] = '\0';
/* Also make sure the last byte in the string area is always 0 */
/* Also make sure the last byte in each string area is always 0 */
beentry->st_appname[NAMEDATALEN - 1] = '\0';
beentry->st_activity[pgstat_track_activity_query_size - 1] = '\0';
beentry->st_changecount++;
Assert((beentry->st_changecount & 1) == 0);
/* Update app name to current GUC setting */
if (application_name)
pgstat_report_appname(application_name);
}
/*
@ -2302,6 +2331,38 @@ pgstat_report_activity(const char *cmd_str)
Assert((beentry->st_changecount & 1) == 0);
}
/* ----------
* pgstat_report_appname() -
*
* Called to update our application name.
* ----------
*/
void
pgstat_report_appname(const char *appname)
{
volatile PgBackendStatus *beentry = MyBEEntry;
int len;
if (!beentry)
return;
/* This should be unnecessary if GUC did its job, but be safe */
len = pg_mbcliplen(appname, strlen(appname), NAMEDATALEN - 1);
/*
* Update my status entry, following the protocol of bumping
* st_changecount before and after. We use a volatile pointer here to
* ensure the compiler doesn't try to get cute.
*/
beentry->st_changecount++;
memcpy((char *) beentry->st_appname, appname, len);
beentry->st_appname[len] = '\0';
beentry->st_changecount++;
Assert((beentry->st_changecount & 1) == 0);
}
/*
* Report current transaction start timestamp as the specified value.
* Zero means there is no active transaction.
@ -2364,7 +2425,8 @@ pgstat_read_current_status(void)
volatile PgBackendStatus *beentry;
PgBackendStatus *localtable;
PgBackendStatus *localentry;
char *localactivity;
char *localappname,
*localactivity;
int i;
Assert(!pgStatRunningInCollector);
@ -2376,6 +2438,9 @@ pgstat_read_current_status(void)
localtable = (PgBackendStatus *)
MemoryContextAlloc(pgStatLocalContext,
sizeof(PgBackendStatus) * MaxBackends);
localappname = (char *)
MemoryContextAlloc(pgStatLocalContext,
NAMEDATALEN * MaxBackends);
localactivity = (char *)
MemoryContextAlloc(pgStatLocalContext,
pgstat_track_activity_query_size * MaxBackends);
@ -2405,6 +2470,8 @@ pgstat_read_current_status(void)
* strcpy is safe even if the string is modified concurrently,
* because there's always a \0 at the end of the buffer.
*/
strcpy(localappname, (char *) beentry->st_appname);
localentry->st_appname = localappname;
strcpy(localactivity, (char *) beentry->st_activity);
localentry->st_activity = localactivity;
}
@ -2422,6 +2489,7 @@ pgstat_read_current_status(void)
if (localentry->st_procpid > 0)
{
localentry++;
localappname += NAMEDATALEN;
localactivity += pgstat_track_activity_query_size;
localNumBackends++;
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/pgstatfuncs.c,v 1.54 2009/06/11 14:49:04 momjian Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/pgstatfuncs.c,v 1.55 2009/11/28 23:38:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -416,7 +416,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
tupdesc = CreateTemplateTupleDesc(10, false);
tupdesc = CreateTemplateTupleDesc(11, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "datid", OIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "procpid", INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 3, "usesysid", OIDOID, -1, 0);
@ -427,6 +427,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
TupleDescInitEntry(tupdesc, (AttrNumber) 8, "backend_start", TIMESTAMPTZOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 9, "client_addr", INETOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 10, "client_port", INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 11, "application_name", TEXTOID, -1, 0);
funcctx->tuple_desc = BlessTupleDesc(tupdesc);
@ -478,8 +479,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
if (funcctx->call_cntr < funcctx->max_calls)
{
/* for each row */
Datum values[10];
bool nulls[10];
Datum values[11];
bool nulls[11];
HeapTuple tuple;
PgBackendStatus *beentry;
SockAddr zero_clientaddr;
@ -599,6 +600,12 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
nulls[9] = true;
}
}
/* application name */
if (beentry->st_appname)
values[10] = CStringGetTextDatum(beentry->st_appname);
else
nulls[10] = true;
}
else
{
@ -610,6 +617,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
nulls[7] = true;
nulls[8] = true;
nulls[9] = true;
nulls[10] = true;
}
tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);

View File

@ -42,7 +42,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.218 2009/10/17 00:24:50 mha Exp $
* $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.219 2009/11/28 23:38:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -68,6 +68,7 @@
#include "storage/ipc.h"
#include "storage/proc.h"
#include "tcop/tcopprot.h"
#include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/ps_status.h"
@ -1798,6 +1799,16 @@ log_line_prefix(StringInfo buf, ErrorData *edata)
/* process the option */
switch (Log_line_prefix[i])
{
case 'a':
if (MyProcPort)
{
const char *appname = application_name;
if (appname == NULL || *appname == '\0')
appname = _("[unknown]");
appendStringInfo(buf, "%s", appname);
}
break;
case 'u':
if (MyProcPort)
{
@ -2103,6 +2114,11 @@ write_csvlog(ErrorData *edata)
appendCSVLiteral(&buf, msgbuf.data);
pfree(msgbuf.data);
}
appendStringInfoCharMacro(&buf, ',');
/* application name */
if (application_name)
appendCSVLiteral(&buf, application_name);
appendStringInfoChar(&buf, '\n');

View File

@ -10,7 +10,7 @@
* Written by Peter Eisentraut <peter_e@gmx.net>.
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.523 2009/10/21 20:38:58 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.524 2009/11/28 23:38:07 tgl Exp $
*
*--------------------------------------------------------------------
*/
@ -168,6 +168,7 @@ static bool assign_maxconnections(int newval, bool doit, GucSource source);
static bool assign_autovacuum_max_workers(int newval, bool doit, GucSource source);
static bool assign_effective_io_concurrency(int newval, bool doit, GucSource source);
static const char *assign_pgstat_temp_directory(const char *newval, bool doit, GucSource source);
static const char *assign_application_name(const char *newval, bool doit, GucSource source);
static char *config_enum_get_options(struct config_enum * record,
const char *prefix, const char *suffix,
@ -378,6 +379,8 @@ char *pgstat_temp_directory;
char *default_do_language;
char *application_name;
int tcp_keepalives_idle;
int tcp_keepalives_interval;
int tcp_keepalives_count;
@ -2534,6 +2537,16 @@ static struct config_string ConfigureNamesString[] =
"plpgsql", NULL, NULL
},
{
{"application_name", PGC_USERSET, LOGGING,
gettext_noop("Sets the application name to be reported in statistics and logs."),
NULL,
GUC_IS_NAME | GUC_NOT_IN_SAMPLE
},
&application_name,
"", assign_application_name, NULL
},
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL
@ -7717,4 +7730,28 @@ assign_pgstat_temp_directory(const char *newval, bool doit, GucSource source)
return newval;
}
static const char *
assign_application_name(const char *newval, bool doit, GucSource source)
{
if (doit)
{
/* Only allow clean ASCII chars in the application name */
char *repval = guc_strdup(ERROR, newval);
char *p;
for (p = repval; *p; p++)
{
if (*p < 32 || *p > 126)
*p = '?';
}
/* Update the pg_stat_activity view */
pgstat_report_appname(repval);
return repval;
}
else
return newval;
}
#include "guc-file.c"

View File

@ -334,6 +334,7 @@
#log_duration = off
#log_hostname = off
#log_line_prefix = '' # special values:
# %a = application name
# %u = user name
# %d = database name
# %r = remote host and port

View File

@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.551 2009/11/20 20:38:11 tgl Exp $
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.552 2009/11/28 23:38:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200911201
#define CATALOG_VERSION_NO 200911281
#endif

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.552 2009/10/09 21:02:56 petere Exp $
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.553 2009/11/28 23:38:07 tgl Exp $
*
* NOTES
* The script catalog/genbki.sh reads this file and generates .bki
@ -2999,7 +2999,7 @@ DATA(insert OID = 2784 ( pg_stat_get_last_autoanalyze_time PGNSP PGUID 12 1 0 0
DESCR("statistics: last auto analyze time for a table");
DATA(insert OID = 1936 ( pg_stat_get_backend_idset PGNSP PGUID 12 1 100 0 f f f t t s 0 0 23 "" _null_ _null_ _null_ _null_ pg_stat_get_backend_idset _null_ _null_ _null_ ));
DESCR("statistics: currently active backend IDs");
DATA(insert OID = 2022 ( pg_stat_get_activity PGNSP PGUID 12 1 100 0 f f f f t s 1 0 2249 "23" "{23,26,23,26,25,16,1184,1184,1184,869,23}" "{i,o,o,o,o,o,o,o,o,o,o}" "{pid,datid,procpid,usesysid,current_query,waiting,xact_start,query_start,backend_start,client_addr,client_port}" _null_ pg_stat_get_activity _null_ _null_ _null_ ));
DATA(insert OID = 2022 ( pg_stat_get_activity PGNSP PGUID 12 1 100 0 f f f f t s 1 0 2249 "23" "{23,26,23,26,25,16,1184,1184,1184,869,23,25}" "{i,o,o,o,o,o,o,o,o,o,o,o}" "{pid,datid,procpid,usesysid,current_query,waiting,xact_start,query_start,backend_start,client_addr,client_port,application_name}" _null_ pg_stat_get_activity _null_ _null_ _null_ ));
DESCR("statistics: information about currently active backends");
DATA(insert OID = 2026 ( pg_backend_pid PGNSP PGUID 12 1 0 0 f f f t f s 0 0 23 "" _null_ _null_ _null_ _null_ pg_backend_pid _null_ _null_ _null_ ));
DESCR("statistics: current backend PID");

View File

@ -5,7 +5,7 @@
*
* Copyright (c) 2001-2009, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/include/pgstat.h,v 1.83 2009/06/11 14:49:08 momjian Exp $
* $PostgreSQL: pgsql/src/include/pgstat.h,v 1.84 2009/11/28 23:38:08 tgl Exp $
* ----------
*/
#ifndef PGSTAT_H
@ -564,6 +564,9 @@ typedef struct PgBackendStatus
/* Is backend currently waiting on an lmgr lock? */
bool st_waiting;
/* application name; MUST be null-terminated */
char *st_appname;
/* current command string; MUST be null-terminated */
char *st_activity;
} PgBackendStatus;
@ -641,7 +644,8 @@ extern void pgstat_report_analyze(Relation rel,
extern void pgstat_initialize(void);
extern void pgstat_bestart(void);
extern void pgstat_report_activity(const char *what);
extern void pgstat_report_activity(const char *cmd_str);
extern void pgstat_report_appname(const char *appname);
extern void pgstat_report_xact_timestamp(TimestampTz tstamp);
extern void pgstat_report_waiting(bool waiting);
extern const char *pgstat_get_backend_current_activity(int pid, bool checkUser);

View File

@ -7,7 +7,7 @@
* Copyright (c) 2000-2009, PostgreSQL Global Development Group
* Written by Peter Eisentraut <peter_e@gmx.net>.
*
* $PostgreSQL: pgsql/src/include/utils/guc.h,v 1.108 2009/10/13 14:18:40 alvherre Exp $
* $PostgreSQL: pgsql/src/include/utils/guc.h,v 1.109 2009/11/28 23:38:08 tgl Exp $
*--------------------------------------------------------------------
*/
#ifndef GUC_H
@ -183,6 +183,8 @@ extern char *external_pid_file;
extern char *default_do_language;
extern char *application_name;
extern int tcp_keepalives_idle;
extern int tcp_keepalives_interval;
extern int tcp_keepalives_count;

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 */

View File

@ -1289,7 +1289,7 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem
pg_rules | SELECT n.nspname AS schemaname, c.relname AS tablename, r.rulename, pg_get_ruledef(r.oid) AS definition FROM ((pg_rewrite r JOIN pg_class c ON ((c.oid = r.ev_class))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (r.rulename <> '_RETURN'::name);
pg_settings | SELECT a.name, a.setting, a.unit, a.category, a.short_desc, a.extra_desc, a.context, a.vartype, a.source, a.min_val, a.max_val, a.enumvals, a.boot_val, a.reset_val, a.sourcefile, a.sourceline FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline);
pg_shadow | SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, pg_authid.rolcreatedb AS usecreatedb, pg_authid.rolsuper AS usesuper, pg_authid.rolcatupdate AS usecatupd, pg_authid.rolpassword AS passwd, (pg_authid.rolvaliduntil)::abstime AS valuntil, s.setconfig AS useconfig FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin;
pg_stat_activity | SELECT s.datid, d.datname, s.procpid, s.usesysid, u.rolname AS usename, s.current_query, s.waiting, s.xact_start, s.query_start, s.backend_start, s.client_addr, s.client_port FROM pg_database d, pg_stat_get_activity(NULL::integer) s(datid, procpid, usesysid, current_query, waiting, xact_start, query_start, backend_start, client_addr, client_port), pg_authid u WHERE ((s.datid = d.oid) AND (s.usesysid = u.oid));
pg_stat_activity | SELECT s.datid, d.datname, s.procpid, s.usesysid, u.rolname AS usename, s.current_query, s.waiting, s.xact_start, s.query_start, s.backend_start, s.client_addr, s.client_port, s.application_name FROM pg_database d, pg_stat_get_activity(NULL::integer) s(datid, procpid, usesysid, current_query, waiting, xact_start, query_start, backend_start, client_addr, client_port, application_name), pg_authid u WHERE ((s.datid = d.oid) AND (s.usesysid = u.oid));
pg_stat_all_indexes | SELECT c.oid AS relid, i.oid AS indexrelid, n.nspname AS schemaname, c.relname, i.relname AS indexrelname, pg_stat_get_numscans(i.oid) AS idx_scan, pg_stat_get_tuples_returned(i.oid) AS idx_tup_read, pg_stat_get_tuples_fetched(i.oid) AS idx_tup_fetch FROM (((pg_class c JOIN pg_index x ON ((c.oid = x.indrelid))) JOIN pg_class i ON ((i.oid = x.indexrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char"]));
pg_stat_all_tables | SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, pg_stat_get_numscans(c.oid) AS seq_scan, pg_stat_get_tuples_returned(c.oid) AS seq_tup_read, (sum(pg_stat_get_numscans(i.indexrelid)))::bigint AS idx_scan, ((sum(pg_stat_get_tuples_fetched(i.indexrelid)))::bigint + pg_stat_get_tuples_fetched(c.oid)) AS idx_tup_fetch, pg_stat_get_tuples_inserted(c.oid) AS n_tup_ins, pg_stat_get_tuples_updated(c.oid) AS n_tup_upd, pg_stat_get_tuples_deleted(c.oid) AS n_tup_del, pg_stat_get_tuples_hot_updated(c.oid) AS n_tup_hot_upd, pg_stat_get_live_tuples(c.oid) AS n_live_tup, pg_stat_get_dead_tuples(c.oid) AS n_dead_tup, pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum, pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum, pg_stat_get_last_analyze_time(c.oid) AS last_analyze, pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze FROM ((pg_class c LEFT JOIN pg_index i ON ((c.oid = i.indrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char"])) GROUP BY c.oid, n.nspname, c.relname;
pg_stat_bgwriter | SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints_timed, pg_stat_get_bgwriter_requested_checkpoints() AS checkpoints_req, pg_stat_get_bgwriter_buf_written_checkpoints() AS buffers_checkpoint, pg_stat_get_bgwriter_buf_written_clean() AS buffers_clean, pg_stat_get_bgwriter_maxwritten_clean() AS maxwritten_clean, pg_stat_get_buf_written_backend() AS buffers_backend, pg_stat_get_buf_alloc() AS buffers_alloc;