Commit 8217cfbd authored by Tom Lane's avatar Tom Lane

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
parent cb98f615
<!-- $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"> <chapter Id="runtime-config">
<title>Server Configuration</title> <title>Server Configuration</title>
...@@ -2882,6 +2882,26 @@ local0.* /var/log/postgresql ...@@ -2882,6 +2882,26 @@ local0.* /var/log/postgresql
<variablelist> <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> <varlistentry>
<term><varname>debug_print_parse</varname> (<type>boolean</type>)</term> <term><varname>debug_print_parse</varname> (<type>boolean</type>)</term>
<term><varname>debug_print_rewritten</varname> (<type>boolean</type>)</term> <term><varname>debug_print_rewritten</varname> (<type>boolean</type>)</term>
...@@ -3049,7 +3069,7 @@ local0.* /var/log/postgresql ...@@ -3049,7 +3069,7 @@ local0.* /var/log/postgresql
that are replaced with status information as outlined below. that are replaced with status information as outlined below.
Unrecognized escapes are ignored. Other Unrecognized escapes are ignored. Other
characters are copied straight to the log line. Some escapes are 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. background processes such as the main server process.
This parameter can only be set in the <filename>postgresql.conf</> This parameter can only be set in the <filename>postgresql.conf</>
file or on the server command line. The default is an empty string. file or on the server command line. The default is an empty string.
...@@ -3064,6 +3084,11 @@ local0.* /var/log/postgresql ...@@ -3064,6 +3084,11 @@ local0.* /var/log/postgresql
</row> </row>
</thead> </thead>
<tbody> <tbody>
<row>
<entry><literal>%a</literal></entry>
<entry>Application name</entry>
<entry>yes</entry>
</row>
<row> <row>
<entry><literal>%u</literal></entry> <entry><literal>%u</literal></entry>
<entry>User name</entry> <entry>User name</entry>
...@@ -3287,19 +3312,35 @@ FROM pg_stat_activity; ...@@ -3287,19 +3312,35 @@ FROM pg_stat_activity;
<title>Using CSV-Format Log Output</title> <title>Using CSV-Format Log Output</title>
<para> <para>
Including <literal>csvlog</> in the <varname>log_destination</> list Including <literal>csvlog</> in the <varname>log_destination</> list
provides a convenient way to import log files into a database table. provides a convenient way to import log files into a database table.
This option emits log lines in comma-separated-value format, This option emits log lines in comma-separated-value format,
with these columns: timestamp with milliseconds, user name, database with these columns:
name, process ID, host:port number, session ID, per-session or -process line timestamp with milliseconds,
number, command tag, session start time, virtual transaction ID, user name,
regular transaction id, error severity, SQL state code, error message, database name,
error message detail, hint, internal query that led to the error (if process ID,
any), character count of the error position thereof, error context, 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 user query that led to the error (if any and enabled by
<varname>log_min_error_statement</>), character count of the error <varname>log_min_error_statement</>),
position thereof, location of the error in the PostgreSQL source code character count of the error position therein,
(if <varname>log_error_verbosity</> is set to <literal>verbose</>). 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: Here is a sample table definition for storing CSV-format log output:
<programlisting> <programlisting>
...@@ -3327,6 +3368,7 @@ CREATE TABLE postgres_log ...@@ -3327,6 +3368,7 @@ CREATE TABLE postgres_log
query text, query text,
query_pos integer, query_pos integer,
location text, location text,
application_name text,
PRIMARY KEY (session_id, session_line_num) PRIMARY KEY (session_id, session_line_num)
); );
</programlisting> </programlisting>
......
<!-- $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"> <chapter id="libpq">
<title><application>libpq</application> - C Library</title> <title><application>libpq</application> - C Library</title>
...@@ -249,6 +249,32 @@ ...@@ -249,6 +249,32 @@
</listitem> </listitem>
</varlistentry> </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"> <varlistentry id="libpq-connect-tty" xreflabel="tty">
<term><literal>tty</literal></term> <term><literal>tty</literal></term>
<listitem> <listitem>
...@@ -5672,7 +5698,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) ...@@ -5672,7 +5698,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm> <indexterm>
<primary><envar>PGHOST</envar></primary> <primary><envar>PGHOST</envar></primary>
</indexterm> </indexterm>
<envar>PGHOST</envar> behaves the same as <xref <envar>PGHOST</envar> behaves the same as the <xref
linkend="libpq-connect-host"> connection parameter. linkend="libpq-connect-host"> connection parameter.
</para> </para>
</listitem> </listitem>
...@@ -5682,7 +5708,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) ...@@ -5682,7 +5708,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm> <indexterm>
<primary><envar>PGHOSTADDR</envar></primary> <primary><envar>PGHOSTADDR</envar></primary>
</indexterm> </indexterm>
<envar>PGHOSTADDR</envar> behaves the same as <xref <envar>PGHOSTADDR</envar> behaves the same as the <xref
linkend="libpq-connect-hostaddr"> connection parameter. linkend="libpq-connect-hostaddr"> connection parameter.
This can be set instead of or in addition to <envar>PGHOST</envar> This can be set instead of or in addition to <envar>PGHOST</envar>
to avoid DNS lookup overhead. to avoid DNS lookup overhead.
...@@ -5694,7 +5720,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) ...@@ -5694,7 +5720,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm> <indexterm>
<primary><envar>PGPORT</envar></primary> <primary><envar>PGPORT</envar></primary>
</indexterm> </indexterm>
<envar>PGPORT</envar> behaves the same as <xref <envar>PGPORT</envar> behaves the same as the <xref
linkend="libpq-connect-port"> connection parameter. linkend="libpq-connect-port"> connection parameter.
</para> </para>
</listitem> </listitem>
...@@ -5704,7 +5730,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) ...@@ -5704,7 +5730,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm> <indexterm>
<primary><envar>PGDATABASE</envar></primary> <primary><envar>PGDATABASE</envar></primary>
</indexterm> </indexterm>
<envar>PGDATABASE</envar> behaves the same as <xref <envar>PGDATABASE</envar> behaves the same as the <xref
linkend="libpq-connect-dbname"> connection parameter. linkend="libpq-connect-dbname"> connection parameter.
</para> </para>
</listitem> </listitem>
...@@ -5714,9 +5740,8 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) ...@@ -5714,9 +5740,8 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm> <indexterm>
<primary><envar>PGUSER</envar></primary> <primary><envar>PGUSER</envar></primary>
</indexterm> </indexterm>
<envar>PGUSER</envar> behaves the same as <xref <envar>PGUSER</envar> behaves the same as the <xref
linkend="libpq-connect-user"> connection parameter. linkend="libpq-connect-user"> connection parameter.
database.
</para> </para>
</listitem> </listitem>
...@@ -5725,12 +5750,12 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) ...@@ -5725,12 +5750,12 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm> <indexterm>
<primary><envar>PGPASSWORD</envar></primary> <primary><envar>PGPASSWORD</envar></primary>
</indexterm> </indexterm>
<envar>PGPASSWORD</envar> behaves the same as <xref <envar>PGPASSWORD</envar> behaves the same as the <xref
linkend="libpq-connect-password"> connection parameter. linkend="libpq-connect-password"> connection parameter.
Use of this environment variable 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 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">). <filename>~/.pgpass</> file (see <xref linkend="libpq-pgpass">).
</para> </para>
</listitem> </listitem>
...@@ -5751,7 +5776,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) ...@@ -5751,7 +5776,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm> <indexterm>
<primary><envar>PGSERVICE</envar></primary> <primary><envar>PGSERVICE</envar></primary>
</indexterm> </indexterm>
<envar>PGSERVICE</envar> behaves the same as <xref <envar>PGSERVICE</envar> behaves the same as the <xref
linkend="libpq-connect-service"> connection parameter. linkend="libpq-connect-service"> connection parameter.
</para> </para>
</listitem> </listitem>
...@@ -5777,17 +5802,27 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) ...@@ -5777,17 +5802,27 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm> <indexterm>
<primary><envar>PGOPTIONS</envar></primary> <primary><envar>PGOPTIONS</envar></primary>
</indexterm> </indexterm>
<envar>PGOPTIONS</envar> behaves the same as <xref <envar>PGOPTIONS</envar> behaves the same as the <xref
linkend="libpq-connect-options"> connection parameter. linkend="libpq-connect-options"> connection parameter.
</para> </para>
</listitem> </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> <listitem>
<para> <para>
<indexterm> <indexterm>
<primary><envar>PGSSLMODE</envar></primary> <primary><envar>PGSSLMODE</envar></primary>
</indexterm> </indexterm>
<envar>PGSSLMODE</envar> behaves the same as <xref <envar>PGSSLMODE</envar> behaves the same as the <xref
linkend="libpq-connect-sslmode"> connection parameter. linkend="libpq-connect-sslmode"> connection parameter.
</para> </para>
</listitem> </listitem>
...@@ -5797,7 +5832,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) ...@@ -5797,7 +5832,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm> <indexterm>
<primary><envar>PGREQUIRESSL</envar></primary> <primary><envar>PGREQUIRESSL</envar></primary>
</indexterm> </indexterm>
<envar>PGREQUIRESSL</envar> behaves the same as <xref <envar>PGREQUIRESSL</envar> behaves the same as the <xref
linkend="libpq-connect-requiressl"> connection parameter. linkend="libpq-connect-requiressl"> connection parameter.
</para> </para>
</listitem> </listitem>
...@@ -5807,7 +5842,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) ...@@ -5807,7 +5842,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm> <indexterm>
<primary><envar>PGSSLCERT</envar></primary> <primary><envar>PGSSLCERT</envar></primary>
</indexterm> </indexterm>
<envar>PGSSLCERT</envar> behaves the same as <xref <envar>PGSSLCERT</envar> behaves the same as the <xref
linkend="libpq-connect-sslcert"> connection parameter. linkend="libpq-connect-sslcert"> connection parameter.
</para> </para>
</listitem> </listitem>
...@@ -5817,7 +5852,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) ...@@ -5817,7 +5852,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm> <indexterm>
<primary><envar>PGSSLKEY</envar></primary> <primary><envar>PGSSLKEY</envar></primary>
</indexterm> </indexterm>
<envar>PGSSLKEY</envar> behaves the same as <xref <envar>PGSSLKEY</envar> behaves the same as the <xref
linkend="libpq-connect-sslkey"> connection parameter. linkend="libpq-connect-sslkey"> connection parameter.
</para> </para>
</listitem> </listitem>
...@@ -5827,7 +5862,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) ...@@ -5827,7 +5862,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm> <indexterm>
<primary><envar>PGSSLROOTCERT</envar></primary> <primary><envar>PGSSLROOTCERT</envar></primary>
</indexterm> </indexterm>
<envar>PGSSLROOTCERT</envar> behaves the same as <xref <envar>PGSSLROOTCERT</envar> behaves the same as the <xref
linkend="libpq-connect-sslrootcert"> connection parameter. linkend="libpq-connect-sslrootcert"> connection parameter.
</para> </para>
</listitem> </listitem>
...@@ -5837,7 +5872,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) ...@@ -5837,7 +5872,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm> <indexterm>
<primary><envar>PGSSLCRL</envar></primary> <primary><envar>PGSSLCRL</envar></primary>
</indexterm> </indexterm>
<envar>PGSSLCRL</envar> behaves the same as <xref <envar>PGSSLCRL</envar> behaves the same as the <xref
linkend="libpq-connect-sslcrl"> connection parameter. linkend="libpq-connect-sslcrl"> connection parameter.
</para> </para>
</listitem> </listitem>
...@@ -5847,7 +5882,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) ...@@ -5847,7 +5882,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm> <indexterm>
<primary><envar>PGKRBSRVNAME</envar></primary> <primary><envar>PGKRBSRVNAME</envar></primary>
</indexterm> </indexterm>
<envar>PGKRBSRVNAME</envar> behaves the same as <xref <envar>PGKRBSRVNAME</envar> behaves the same as the <xref
linkend="libpq-connect-krbsrvname"> connection parameter. linkend="libpq-connect-krbsrvname"> connection parameter.
</para> </para>
</listitem> </listitem>
...@@ -5857,7 +5892,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) ...@@ -5857,7 +5892,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm> <indexterm>
<primary><envar>PGGSSLIB</envar></primary> <primary><envar>PGGSSLIB</envar></primary>
</indexterm> </indexterm>
<envar>PGGSSLIB</envar> behaves the same as <xref <envar>PGGSSLIB</envar> behaves the same as the <xref
linkend="libpq-connect-gsslib"> connection parameter. linkend="libpq-connect-gsslib"> connection parameter.
</para> </para>
</listitem> </listitem>
...@@ -5867,7 +5902,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) ...@@ -5867,7 +5902,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<indexterm> <indexterm>
<primary><envar>PGCONNECT_TIMEOUT</envar></primary> <primary><envar>PGCONNECT_TIMEOUT</envar></primary>
</indexterm> </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. linkend="libpq-connect-connect-timeout"> connection parameter.
</para> </para>
</listitem> </listitem>
......
<!-- $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"> <chapter id="monitoring">
<title>Monitoring Database Activity</title> <title>Monitoring Database Activity</title>
...@@ -238,12 +238,12 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re ...@@ -238,12 +238,12 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
name, process <acronym>ID</>, user OID, user name, current query, name, process <acronym>ID</>, user OID, user name, current query,
query's waiting status, time at which the current transaction and query's waiting status, time at which the current transaction and
current query began execution, time at which the process was current query began execution, time at which the process was
started, and client's address and port number. The columns that started, client's address and port number, and application name.
report data on the current query are available unless the parameter The columns that report data on the current query are available unless
<varname>track_activities</varname> has been turned off. the parameter <varname>track_activities</varname> has been turned off.
Furthermore, these columns are only visible if the user examining Furthermore, these columns and the application name are only visible if
the view is a superuser or the same as the user owning the process the user examining the view is a superuser or the same as the user
being reported on. owning the process being reported on.
</entry> </entry>
</row> </row>
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* *
* Copyright (c) 1996-2009, PostgreSQL Global Development Group * 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 CREATE VIEW pg_roles AS
...@@ -339,7 +339,8 @@ CREATE VIEW pg_stat_activity AS ...@@ -339,7 +339,8 @@ CREATE VIEW pg_stat_activity AS
S.query_start, S.query_start,
S.backend_start, S.backend_start,
S.client_addr, 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 FROM pg_database D, pg_stat_get_activity(NULL) AS S, pg_authid U
WHERE S.datid = D.oid AND WHERE S.datid = D.oid AND
S.usesysid = U.oid; S.usesysid = U.oid;
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* *
* Copyright (c) 2001-2009, PostgreSQL Global Development Group * 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" #include "postgres.h"
...@@ -2073,6 +2073,7 @@ pgstat_fetch_global(void) ...@@ -2073,6 +2073,7 @@ pgstat_fetch_global(void)
static PgBackendStatus *BackendStatusArray = NULL; static PgBackendStatus *BackendStatusArray = NULL;
static PgBackendStatus *MyBEEntry = NULL; static PgBackendStatus *MyBEEntry = NULL;
static char *BackendAppnameBuffer = NULL;
static char *BackendActivityBuffer = NULL; static char *BackendActivityBuffer = NULL;
...@@ -2084,14 +2085,17 @@ BackendStatusShmemSize(void) ...@@ -2084,14 +2085,17 @@ BackendStatusShmemSize(void)
{ {
Size size; 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)); mul_size(pgstat_track_activity_query_size, MaxBackends));
return size; return size;
} }
/* /*
* Initialize the shared status array and activity string buffer during * Initialize the shared status array and activity/appname string buffers
* postmaster startup. * during postmaster startup.
*/ */
void void
CreateSharedBackendStatus(void) CreateSharedBackendStatus(void)
...@@ -2114,6 +2118,24 @@ CreateSharedBackendStatus(void) ...@@ -2114,6 +2118,24 @@ CreateSharedBackendStatus(void)
MemSet(BackendStatusArray, 0, size); 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 */ /* Create or attach to the shared activity buffer */
size = mul_size(pgstat_track_activity_query_size, MaxBackends); size = mul_size(pgstat_track_activity_query_size, MaxBackends);
BackendActivityBuffer = (char *) BackendActivityBuffer = (char *)
...@@ -2159,7 +2181,8 @@ pgstat_initialize(void) ...@@ -2159,7 +2181,8 @@ pgstat_initialize(void)
* pgstat_bestart() - * pgstat_bestart() -
* *
* Initialize this backend's entry in the PgBackendStatus array. * 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). * (hence, this cannot be combined with pgstat_initialize).
* ---------- * ----------
*/ */
...@@ -2214,12 +2237,18 @@ pgstat_bestart(void) ...@@ -2214,12 +2237,18 @@ pgstat_bestart(void)
beentry->st_userid = userid; beentry->st_userid = userid;
beentry->st_clientaddr = clientaddr; beentry->st_clientaddr = clientaddr;
beentry->st_waiting = false; beentry->st_waiting = false;
beentry->st_appname[0] = '\0';
beentry->st_activity[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_activity[pgstat_track_activity_query_size - 1] = '\0';
beentry->st_changecount++; beentry->st_changecount++;
Assert((beentry->st_changecount & 1) == 0); 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) ...@@ -2302,6 +2331,38 @@ pgstat_report_activity(const char *cmd_str)
Assert((beentry->st_changecount & 1) == 0); 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. * Report current transaction start timestamp as the specified value.
* Zero means there is no active transaction. * Zero means there is no active transaction.
...@@ -2364,7 +2425,8 @@ pgstat_read_current_status(void) ...@@ -2364,7 +2425,8 @@ pgstat_read_current_status(void)
volatile PgBackendStatus *beentry; volatile PgBackendStatus *beentry;
PgBackendStatus *localtable; PgBackendStatus *localtable;
PgBackendStatus *localentry; PgBackendStatus *localentry;
char *localactivity; char *localappname,
*localactivity;
int i; int i;
Assert(!pgStatRunningInCollector); Assert(!pgStatRunningInCollector);
...@@ -2376,6 +2438,9 @@ pgstat_read_current_status(void) ...@@ -2376,6 +2438,9 @@ pgstat_read_current_status(void)
localtable = (PgBackendStatus *) localtable = (PgBackendStatus *)
MemoryContextAlloc(pgStatLocalContext, MemoryContextAlloc(pgStatLocalContext,
sizeof(PgBackendStatus) * MaxBackends); sizeof(PgBackendStatus) * MaxBackends);
localappname = (char *)
MemoryContextAlloc(pgStatLocalContext,
NAMEDATALEN * MaxBackends);
localactivity = (char *) localactivity = (char *)
MemoryContextAlloc(pgStatLocalContext, MemoryContextAlloc(pgStatLocalContext,
pgstat_track_activity_query_size * MaxBackends); pgstat_track_activity_query_size * MaxBackends);
...@@ -2405,6 +2470,8 @@ pgstat_read_current_status(void) ...@@ -2405,6 +2470,8 @@ pgstat_read_current_status(void)
* strcpy is safe even if the string is modified concurrently, * strcpy is safe even if the string is modified concurrently,
* because there's always a \0 at the end of the buffer. * 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); strcpy(localactivity, (char *) beentry->st_activity);
localentry->st_activity = localactivity; localentry->st_activity = localactivity;
} }
...@@ -2422,6 +2489,7 @@ pgstat_read_current_status(void) ...@@ -2422,6 +2489,7 @@ pgstat_read_current_status(void)
if (localentry->st_procpid > 0) if (localentry->st_procpid > 0)
{ {
localentry++; localentry++;
localappname += NAMEDATALEN;
localactivity += pgstat_track_activity_query_size; localactivity += pgstat_track_activity_query_size;
localNumBackends++; localNumBackends++;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -416,7 +416,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); 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) 1, "datid", OIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "procpid", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "procpid", INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 3, "usesysid", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "usesysid", OIDOID, -1, 0);
...@@ -427,6 +427,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS) ...@@ -427,6 +427,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
TupleDescInitEntry(tupdesc, (AttrNumber) 8, "backend_start", TIMESTAMPTZOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 8, "backend_start", TIMESTAMPTZOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 9, "client_addr", INETOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 9, "client_addr", INETOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 10, "client_port", INT4OID, -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); funcctx->tuple_desc = BlessTupleDesc(tupdesc);
...@@ -478,8 +479,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS) ...@@ -478,8 +479,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
if (funcctx->call_cntr < funcctx->max_calls) if (funcctx->call_cntr < funcctx->max_calls)
{ {
/* for each row */ /* for each row */
Datum values[10]; Datum values[11];
bool nulls[10]; bool nulls[11];
HeapTuple tuple; HeapTuple tuple;
PgBackendStatus *beentry; PgBackendStatus *beentry;
SockAddr zero_clientaddr; SockAddr zero_clientaddr;
...@@ -599,6 +600,12 @@ pg_stat_get_activity(PG_FUNCTION_ARGS) ...@@ -599,6 +600,12 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
nulls[9] = true; nulls[9] = true;
} }
} }
/* application name */
if (beentry->st_appname)
values[10] = CStringGetTextDatum(beentry->st_appname);
else
nulls[10] = true;
} }
else else
{ {
...@@ -610,6 +617,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS) ...@@ -610,6 +617,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
nulls[7] = true; nulls[7] = true;
nulls[8] = true; nulls[8] = true;
nulls[9] = true; nulls[9] = true;
nulls[10] = true;
} }
tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
......
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
* *
* *
* IDENTIFICATION * 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 @@ ...@@ -68,6 +68,7 @@
#include "storage/ipc.h" #include "storage/ipc.h"
#include "storage/proc.h" #include "storage/proc.h"
#include "tcop/tcopprot.h" #include "tcop/tcopprot.h"
#include "utils/guc.h"
#include "utils/memutils.h" #include "utils/memutils.h"
#include "utils/ps_status.h" #include "utils/ps_status.h"
...@@ -1798,6 +1799,16 @@ log_line_prefix(StringInfo buf, ErrorData *edata) ...@@ -1798,6 +1799,16 @@ log_line_prefix(StringInfo buf, ErrorData *edata)
/* process the option */ /* process the option */
switch (Log_line_prefix[i]) 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': case 'u':
if (MyProcPort) if (MyProcPort)
{ {
...@@ -2103,6 +2114,11 @@ write_csvlog(ErrorData *edata) ...@@ -2103,6 +2114,11 @@ write_csvlog(ErrorData *edata)
appendCSVLiteral(&buf, msgbuf.data); appendCSVLiteral(&buf, msgbuf.data);
pfree(msgbuf.data); pfree(msgbuf.data);
} }
appendStringInfoCharMacro(&buf, ',');
/* application name */
if (application_name)
appendCSVLiteral(&buf, application_name);
appendStringInfoChar(&buf, '\n'); appendStringInfoChar(&buf, '\n');
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* Written by Peter Eisentraut <peter_e@gmx.net>. * Written by Peter Eisentraut <peter_e@gmx.net>.
* *
* IDENTIFICATION * 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); ...@@ -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_autovacuum_max_workers(int newval, bool doit, GucSource source);
static bool assign_effective_io_concurrency(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_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, static char *config_enum_get_options(struct config_enum * record,
const char *prefix, const char *suffix, const char *prefix, const char *suffix,
...@@ -378,6 +379,8 @@ char *pgstat_temp_directory; ...@@ -378,6 +379,8 @@ char *pgstat_temp_directory;
char *default_do_language; char *default_do_language;
char *application_name;
int tcp_keepalives_idle; int tcp_keepalives_idle;
int tcp_keepalives_interval; int tcp_keepalives_interval;
int tcp_keepalives_count; int tcp_keepalives_count;
...@@ -2534,6 +2537,16 @@ static struct config_string ConfigureNamesString[] = ...@@ -2534,6 +2537,16 @@ static struct config_string ConfigureNamesString[] =
"plpgsql", NULL, NULL "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 */ /* End-of-list marker */
{ {
{NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL {NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL
...@@ -7717,4 +7730,28 @@ assign_pgstat_temp_directory(const char *newval, bool doit, GucSource source) ...@@ -7717,4 +7730,28 @@ assign_pgstat_temp_directory(const char *newval, bool doit, GucSource source)
return newval; 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" #include "guc-file.c"
...@@ -334,6 +334,7 @@ ...@@ -334,6 +334,7 @@
#log_duration = off #log_duration = off
#log_hostname = off #log_hostname = off
#log_line_prefix = '' # special values: #log_line_prefix = '' # special values:
# %a = application name
# %u = user name # %u = user name
# %d = database name # %d = database name
# %r = remote host and port # %r = remote host and port
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 200911201 #define CATALOG_VERSION_NO 200911281
#endif #endif
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 * NOTES
* The script catalog/genbki.sh reads this file and generates .bki * 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 ...@@ -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"); 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_ )); 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"); 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"); 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_ )); 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"); DESCR("statistics: current backend PID");
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* *
* Copyright (c) 2001-2009, PostgreSQL Global Development Group * 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 #ifndef PGSTAT_H
...@@ -564,6 +564,9 @@ typedef struct PgBackendStatus ...@@ -564,6 +564,9 @@ typedef struct PgBackendStatus
/* Is backend currently waiting on an lmgr lock? */ /* Is backend currently waiting on an lmgr lock? */
bool st_waiting; bool st_waiting;
/* application name; MUST be null-terminated */
char *st_appname;
/* current command string; MUST be null-terminated */ /* current command string; MUST be null-terminated */
char *st_activity; char *st_activity;
} PgBackendStatus; } PgBackendStatus;
...@@ -641,7 +644,8 @@ extern void pgstat_report_analyze(Relation rel, ...@@ -641,7 +644,8 @@ extern void pgstat_report_analyze(Relation rel,
extern void pgstat_initialize(void); extern void pgstat_initialize(void);
extern void pgstat_bestart(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_xact_timestamp(TimestampTz tstamp);
extern void pgstat_report_waiting(bool waiting); extern void pgstat_report_waiting(bool waiting);
extern const char *pgstat_get_backend_current_activity(int pid, bool checkUser); extern const char *pgstat_get_backend_current_activity(int pid, bool checkUser);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Copyright (c) 2000-2009, PostgreSQL Global Development Group * Copyright (c) 2000-2009, PostgreSQL Global Development Group
* Written by Peter Eisentraut <peter_e@gmx.net>. * 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 #ifndef GUC_H
...@@ -183,6 +183,8 @@ extern char *external_pid_file; ...@@ -183,6 +183,8 @@ extern char *external_pid_file;
extern char *default_do_language; extern char *default_do_language;
extern char *application_name;
extern int tcp_keepalives_idle; extern int tcp_keepalives_idle;
extern int tcp_keepalives_interval; extern int tcp_keepalives_interval;
extern int tcp_keepalives_count; extern int tcp_keepalives_count;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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[] = { ...@@ -164,6 +164,12 @@ static const PQconninfoOption PQconninfoOptions[] = {
{"options", "PGOPTIONS", DefaultOption, NULL, {"options", "PGOPTIONS", DefaultOption, NULL,
"Backend-Debug-Options", "D", 40}, "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 #ifdef USE_SSL
/* /*
...@@ -256,6 +262,7 @@ static int parseServiceInfo(PQconninfoOption *options, ...@@ -256,6 +262,7 @@ static int parseServiceInfo(PQconninfoOption *options,
static char *pwdfMatchesString(char *buf, char *token); static char *pwdfMatchesString(char *buf, char *token);
static char *PasswordFromFile(char *hostname, char *port, char *dbname, static char *PasswordFromFile(char *hostname, char *port, char *dbname,
char *username); char *username);
static PostgresPollingStatusType pqAppnamePoll(PGconn *conn);
static void default_threadlock(int acquire); static void default_threadlock(int acquire);
...@@ -416,6 +423,10 @@ connectOptions1(PGconn *conn, const char *conninfo) ...@@ -416,6 +423,10 @@ connectOptions1(PGconn *conn, const char *conninfo)
conn->pgtty = tmp ? strdup(tmp) : NULL; conn->pgtty = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "options"); tmp = conninfo_getval(connOptions, "options");
conn->pgoptions = tmp ? strdup(tmp) : NULL; 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"); tmp = conninfo_getval(connOptions, "dbname");
conn->dbName = tmp ? strdup(tmp) : NULL; conn->dbName = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "user"); tmp = conninfo_getval(connOptions, "user");
...@@ -1064,7 +1075,7 @@ PQconnectPoll(PGconn *conn) ...@@ -1064,7 +1075,7 @@ PQconnectPoll(PGconn *conn)
case CONNECTION_MADE: case CONNECTION_MADE:
break; break;
/* We allow pqSetenvPoll to decide whether to proceed. */ /* pqSetenvPoll/pqAppnamePoll will decide whether to proceed. */
case CONNECTION_SETENV: case CONNECTION_SETENV:
break; break;
...@@ -1888,6 +1899,12 @@ keep_going: /* We will come back to here until there is ...@@ -1888,6 +1899,12 @@ keep_going: /* We will come back to here until there is
conn->addrlist = NULL; conn->addrlist = NULL;
conn->addr_cur = 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 */ /* Fire up post-connection housekeeping if needed */
if (PG_PROTOCOL_MAJOR(conn->pversion) < 3) if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
{ {
...@@ -1896,6 +1913,13 @@ keep_going: /* We will come back to here until there is ...@@ -1896,6 +1913,13 @@ keep_going: /* We will come back to here until there is
conn->next_eo = EnvironmentOptions; conn->next_eo = EnvironmentOptions;
return PGRES_POLLING_WRITING; 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! */ /* Otherwise, we are open for business! */
conn->status = CONNECTION_OK; conn->status = CONNECTION_OK;
...@@ -1903,42 +1927,49 @@ keep_going: /* We will come back to here until there is ...@@ -1903,42 +1927,49 @@ keep_going: /* We will come back to here until there is
} }
case CONNECTION_SETENV: case CONNECTION_SETENV:
{
PostgresPollingStatusType ret;
/* /*
* Do post-connection housekeeping (only needed in protocol 2.0). * 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. * We pretend that the connection is OK for the duration of
*/ * these queries.
conn->status = CONNECTION_OK; */
conn->status = CONNECTION_OK;
switch (pqSetenvPoll(conn)) if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
{ ret = pqSetenvPoll(conn);
case PGRES_POLLING_OK: /* Success */ else /* must be here to send app name */
break; ret = pqAppnamePoll(conn);
case PGRES_POLLING_READING: /* Still going */ switch (ret)
conn->status = CONNECTION_SETENV; {
return PGRES_POLLING_READING; case PGRES_POLLING_OK: /* Success */
break;
case PGRES_POLLING_WRITING: /* Still going */ case PGRES_POLLING_READING: /* Still going */
conn->status = CONNECTION_SETENV; conn->status = CONNECTION_SETENV;
return PGRES_POLLING_WRITING; return PGRES_POLLING_READING;
default: case PGRES_POLLING_WRITING: /* Still going */
goto error_return; conn->status = CONNECTION_SETENV;
} return PGRES_POLLING_WRITING;
/* We are open for business! */ default:
conn->status = CONNECTION_OK; goto error_return;
return PGRES_POLLING_OK; }
/* We are open for business! */
conn->status = CONNECTION_OK;
return PGRES_POLLING_OK;
}
default: default:
appendPQExpBuffer(&conn->errorMessage, appendPQExpBuffer(&conn->errorMessage,
libpq_gettext( libpq_gettext("invalid connection state %d, "
"invalid connection state %c, " "probably indicative of memory corruption\n"),
"probably indicative of memory corruption\n"
),
conn->status); conn->status);
goto error_return; goto error_return;
} }
...@@ -2000,6 +2031,7 @@ makeEmptyPGconn(void) ...@@ -2000,6 +2031,7 @@ makeEmptyPGconn(void)
conn->options_valid = false; conn->options_valid = false;
conn->nonblocking = false; conn->nonblocking = false;
conn->setenv_state = SETENV_STATE_IDLE; conn->setenv_state = SETENV_STATE_IDLE;
conn->appname_state = APPNAME_STATE_IDLE;
conn->client_encoding = PG_SQL_ASCII; conn->client_encoding = PG_SQL_ASCII;
conn->std_strings = false; /* unless server says differently */ conn->std_strings = false; /* unless server says differently */
conn->verbosity = PQERRORS_DEFAULT; conn->verbosity = PQERRORS_DEFAULT;
...@@ -2082,6 +2114,10 @@ freePGconn(PGconn *conn) ...@@ -2082,6 +2114,10 @@ freePGconn(PGconn *conn)
free(conn->connect_timeout); free(conn->connect_timeout);
if (conn->pgoptions) if (conn->pgoptions)
free(conn->pgoptions); free(conn->pgoptions);
if (conn->appname)
free(conn->appname);
if (conn->fbappname)
free(conn->fbappname);
if (conn->dbName) if (conn->dbName)
free(conn->dbName); free(conn->dbName);
if (conn->pguser) if (conn->pguser)
...@@ -4005,6 +4041,129 @@ pqGetHomeDirectory(char *buf, int bufsize) ...@@ -4005,6 +4041,129 @@ pqGetHomeDirectory(char *buf, int bufsize)
#endif #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 * To keep the API consistent, the locking stubs are always provided, even
* if they are not required. * if they are not required.
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 ...@@ -244,6 +244,14 @@ typedef enum
SETENV_STATE_IDLE SETENV_STATE_IDLE
} PGSetenvStatusType; } 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 for the EnvironmentOptions[] array */
typedef struct PQEnvironmentOption typedef struct PQEnvironmentOption
{ {
...@@ -295,6 +303,8 @@ struct pg_conn ...@@ -295,6 +303,8 @@ struct pg_conn
* displayed (OBSOLETE, NOT USED) */ * displayed (OBSOLETE, NOT USED) */
char *connect_timeout; /* connection timeout (numeric string) */ char *connect_timeout; /* connection timeout (numeric string) */
char *pgoptions; /* options to start the backend with */ char *pgoptions; /* options to start the backend with */
char *appname; /* application name */
char *fbappname; /* fallback application name */
char *dbName; /* database name */ char *dbName; /* database name */
char *pguser; /* Postgres username and password, if any */ char *pguser; /* Postgres username and password, if any */
char *pgpass; char *pgpass;
...@@ -349,6 +359,7 @@ struct pg_conn ...@@ -349,6 +359,7 @@ struct pg_conn
struct addrinfo *addr_cur; /* the one currently being tried */ struct addrinfo *addr_cur; /* the one currently being tried */
int addrlist_family; /* needed to know how to free addrlist */ int addrlist_family; /* needed to know how to free addrlist */
PGSetenvStatusType setenv_state; /* for 2.0 protocol only */ PGSetenvStatusType setenv_state; /* for 2.0 protocol only */
PGAppnameStatusType appname_state;
const PQEnvironmentOption *next_eo; const PQEnvironmentOption *next_eo;
/* Miscellaneous stuff */ /* Miscellaneous stuff */
......
...@@ -1289,7 +1289,7 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem ...@@ -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_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_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_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_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_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; 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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment