Hi Dave, On Thursday 22 October 2009 15:07:13 Dave Page wrote: > Updated patch attached. Per discussion, this: > - Changes the envvar name to PGAPPNAME > - Removes support for setting application_name in the startup packet, > and instead sends an explicit SET command as part of the connection > setup in PQconnectPoll. In order to avoid adding to the > application-visible connection states, this is overloaded on the > CONNECTION_SETENV state which is only used in the v2 protocol at > present and seems like an ideal fit for such a similar use. I had some free time so I started to take a look at that patch:
+ PostgresPollingStatusType + pqAppnamePoll(PGconn *conn) ... + case APPNAME_STATE_OPTION_WAIT: ... + else + { + /* Query finished, so we're done */ + conn->setenv_state = APPNAME_STATE_IDLE; + return PGRES_POLLING_OK; + } + break; + } Shouldnt that set appname_state? The attached patch fixes this and also a couple occurances of trailing whitespace. What about pg_dump/psql setting fallback_application_name? Andres
From 962861a38ea28c769bb28c18f3142f0339e97f5c Mon Sep 17 00:00:00 2001 From: Andres Freund <and...@anarazel.de> Date: Fri, 13 Nov 2009 00:28:44 +0100 Subject: [PATCH] Dave Page: Application name patch - v3 --- doc/src/sgml/config.sgml | 16 +++ doc/src/sgml/libpq.sgml | 37 +++++ doc/src/sgml/monitoring.sgml | 12 +- src/backend/catalog/system_views.sql | 3 +- src/backend/postmaster/pgstat.c | 56 ++++++-- src/backend/utils/adt/pgstatfuncs.c | 16 ++- src/backend/utils/error/elog.c | 16 +++ src/backend/utils/misc/guc.c | 11 ++ src/backend/utils/misc/postgresql.conf.sample | 1 + src/include/catalog/pg_proc.h | 2 +- src/include/pgstat.h | 4 + src/include/utils/guc.h | 2 + src/interfaces/libpq/fe-connect.c | 177 ++++++++++++++++++++++++- src/interfaces/libpq/libpq-int.h | 12 ++ src/test/regress/expected/rules.out | 2 +- 15 files changed, 338 insertions(+), 29 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index eb2071f..87e6dac 100644 *** a/doc/src/sgml/config.sgml --- b/doc/src/sgml/config.sgml *************** local0.* /var/log/postgresql *** 2881,2886 **** --- 2881,2901 ---- <variablelist> <varlistentry> + <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> is typically set by an + application upon connection to the server. The value will be included + in CSV logs and may be included in the <varname>log_line_prefix</varname>. + In addition, it will be included in the <literal>pg_stat_activity</> view. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><varname>debug_print_parse</varname> (<type>boolean</type>)</term> <term><varname>debug_print_rewritten</varname> (<type>boolean</type>)</term> <term><varname>debug_print_plan</varname> (<type>boolean</type>)</term> *************** CREATE TABLE postgres_log *** 3325,3330 **** --- 3340,3346 ---- query text, query_pos integer, location text, + application_name text, PRIMARY KEY (session_id, session_line_num) ); </programlisting> diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index 86affb0..e4576e6 100644 *** a/doc/src/sgml/libpq.sgml --- b/doc/src/sgml/libpq.sgml *************** *** 249,254 **** --- 249,281 ---- </listitem> </varlistentry> + <varlistentry id="libpq-connect-application-name" xreflabel="application_name"> + <term><literal>application_name</literal></term> + <listitem> + <para> + Allows an application to specify a value for the <literal>application_name</> + configuration variable, the value of which may be included in logging + output and monitoring data from views such as <literal>pg_stat_activity</>. + </para> + </listitem> + </varlistentry> + + <varlistentry id="libpq-connect-fallback-application-name" xreflabel="fallback_application_name"> + <term><literal>fallback_application_name</literal></term> + <listitem> + <para> + Allows an application to specify a fallback value for the + <literal>application_name</> configuration variable. This value + is used if neither the <literal>application_name</> connection + string option or <envar>PGCLIENTENCODING</envar> are set, + which offers the application a way to allow the enviroment to + override a compiled in default. This is useful when scripting + generic utilities to perform specific tasks where a bespoke + application name is desirable. + </para> + </listitem> + </varlistentry> + <varlistentry id="libpq-connect-tty" xreflabel="tty"> <term><literal>tty</literal></term> <listitem> *************** myEventProc(PGEventId evtId, void *evtIn *** 5785,5790 **** --- 5812,5827 ---- <listitem> <para> <indexterm> + <primary><envar>PGAPPNAME</envar></primary> + </indexterm> + <envar>PGAPPNAME</envar> behaves the same as <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 diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index 31da86d..8690122 100644 *** a/doc/src/sgml/monitoring.sgml --- b/doc/src/sgml/monitoring.sgml *************** postgres: <replaceable>user</> <replacea *** 238,249 **** 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. </entry> </row> --- 238,249 ---- 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, 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 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> diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 98e9685..6d11a14 100644 *** a/src/backend/catalog/system_views.sql --- b/src/backend/catalog/system_views.sql *************** CREATE VIEW pg_stat_activity AS *** 339,345 **** S.query_start, S.backend_start, S.client_addr, ! S.client_port FROM pg_database D, pg_stat_get_activity(NULL) AS S, pg_authid U WHERE S.datid = D.oid AND S.usesysid = U.oid; --- 339,346 ---- S.query_start, S.backend_start, S.client_addr, ! 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; diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c index 1a818ef..0d135c4 100644 *** a/src/backend/postmaster/pgstat.c --- b/src/backend/postmaster/pgstat.c *************** pgstat_fetch_global(void) *** 2074,2079 **** --- 2074,2080 ---- static PgBackendStatus *BackendStatusArray = NULL; static PgBackendStatus *MyBEEntry = NULL; static char *BackendActivityBuffer = NULL; + static char *BackendAppnameBuffer = NULL; /* *************** BackendStatusShmemSize(void) *** 2090,2096 **** } /* ! * Initialize the shared status array and activity string buffer during * postmaster startup. */ void --- 2091,2097 ---- } /* ! * Initialize the shared status array and activity/appname string buffers during * postmaster startup. */ void *************** CreateSharedBackendStatus(void) *** 2099,2105 **** Size size; bool found; int i; ! char *buffer; /* Create or attach to the shared array */ size = mul_size(sizeof(PgBackendStatus), MaxBackends); --- 2100,2106 ---- Size size; bool found; int i; ! char *activitybuffer, *appnamebuffer; /* Create or attach to the shared array */ size = mul_size(sizeof(PgBackendStatus), MaxBackends); *************** CreateSharedBackendStatus(void) *** 2124,2134 **** MemSet(BackendActivityBuffer, 0, size); /* Initialize st_activity pointers. */ ! buffer = BackendActivityBuffer; for (i = 0; i < MaxBackends; i++) { ! BackendStatusArray[i].st_activity = buffer; ! buffer += pgstat_track_activity_query_size; } } } --- 2125,2153 ---- MemSet(BackendActivityBuffer, 0, size); /* Initialize st_activity pointers. */ ! activitybuffer = BackendActivityBuffer; for (i = 0; i < MaxBackends; i++) { ! BackendStatusArray[i].st_activity = activitybuffer; ! activitybuffer += pgstat_track_activity_query_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_activity pointers. */ ! appnamebuffer = BackendAppnameBuffer; ! for (i = 0; i < MaxBackends; i++) ! { ! BackendStatusArray[i].st_appname = appnamebuffer; ! appnamebuffer += NAMEDATALEN; } } } *************** pgstat_bestart(void) *** 2169,2175 **** TimestampTz proc_start_timestamp; Oid userid; SockAddr clientaddr; ! volatile PgBackendStatus *beentry; /* * To minimize the time spent modifying the PgBackendStatus entry, fetch --- 2188,2194 ---- TimestampTz proc_start_timestamp; Oid userid; SockAddr clientaddr; ! volatile PgBackendStatus *beentry; /* * To minimize the time spent modifying the PgBackendStatus entry, fetch *************** pgstat_bestart(void) *** 2214,2221 **** --- 2233,2242 ---- 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 */ + beentry->st_appname[NAMEDATALEN - 1] = '\0'; beentry->st_activity[pgstat_track_activity_query_size - 1] = '\0'; beentry->st_changecount++; *************** pgstat_report_activity(const char *cmd_s *** 2271,2277 **** { volatile PgBackendStatus *beentry = MyBEEntry; TimestampTz start_timestamp; ! int len; TRACE_POSTGRESQL_STATEMENT_STATUS(cmd_str); --- 2292,2298 ---- { volatile PgBackendStatus *beentry = MyBEEntry; TimestampTz start_timestamp; ! int activity_len, appname_len; TRACE_POSTGRESQL_STATEMENT_STATUS(cmd_str); *************** pgstat_report_activity(const char *cmd_s *** 2284,2291 **** */ start_timestamp = GetCurrentStatementStartTimestamp(); ! len = strlen(cmd_str); ! len = pg_mbcliplen(cmd_str, len, pgstat_track_activity_query_size - 1); /* * Update my status entry, following the protocol of bumping --- 2305,2315 ---- */ start_timestamp = GetCurrentStatementStartTimestamp(); ! activity_len = strlen(cmd_str); ! activity_len = pg_mbcliplen(cmd_str, activity_len, pgstat_track_activity_query_size - 1); ! ! appname_len = strlen(application_name); ! appname_len = pg_mbcliplen(application_name, appname_len, NAMEDATALEN - 1); /* * Update my status entry, following the protocol of bumping *************** pgstat_report_activity(const char *cmd_s *** 2295,2302 **** beentry->st_changecount++; beentry->st_activity_start_timestamp = start_timestamp; ! memcpy((char *) beentry->st_activity, cmd_str, len); ! beentry->st_activity[len] = '\0'; beentry->st_changecount++; Assert((beentry->st_changecount & 1) == 0); --- 2319,2328 ---- beentry->st_changecount++; beentry->st_activity_start_timestamp = start_timestamp; ! memcpy((char *) beentry->st_appname, application_name, appname_len); ! beentry->st_appname[appname_len] = '\0'; ! memcpy((char *) beentry->st_activity, cmd_str, activity_len); ! beentry->st_activity[activity_len] = '\0'; beentry->st_changecount++; Assert((beentry->st_changecount & 1) == 0); *************** pgstat_read_current_status(void) *** 2364,2370 **** volatile PgBackendStatus *beentry; PgBackendStatus *localtable; PgBackendStatus *localentry; ! char *localactivity; int i; Assert(!pgStatRunningInCollector); --- 2390,2396 ---- volatile PgBackendStatus *beentry; PgBackendStatus *localtable; PgBackendStatus *localentry; ! char *localactivity, *localappname; int i; Assert(!pgStatRunningInCollector); *************** pgstat_read_current_status(void) *** 2379,2384 **** --- 2405,2413 ---- localactivity = (char *) MemoryContextAlloc(pgStatLocalContext, pgstat_track_activity_query_size * MaxBackends); + localappname = (char *) + MemoryContextAlloc(pgStatLocalContext, + NAMEDATALEN * MaxBackends); localNumBackends = 0; beentry = BackendStatusArray; *************** pgstat_read_current_status(void) *** 2405,2410 **** --- 2434,2441 ---- * 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; } *************** pgstat_read_current_status(void) *** 2423,2428 **** --- 2454,2460 ---- { localentry++; localactivity += pgstat_track_activity_query_size; + localappname += NAMEDATALEN; localNumBackends++; } } diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index 39bb558..9135a47 100644 *** a/src/backend/utils/adt/pgstatfuncs.c --- b/src/backend/utils/adt/pgstatfuncs.c *************** pg_stat_get_activity(PG_FUNCTION_ARGS) *** 416,422 **** oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); ! tupdesc = CreateTemplateTupleDesc(10, 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); --- 416,422 ---- oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); ! 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); *************** pg_stat_get_activity(PG_FUNCTION_ARGS) *** 427,432 **** --- 427,433 ---- 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); *************** pg_stat_get_activity(PG_FUNCTION_ARGS) *** 478,485 **** if (funcctx->call_cntr < funcctx->max_calls) { /* for each row */ ! Datum values[10]; ! bool nulls[10]; HeapTuple tuple; PgBackendStatus *beentry; SockAddr zero_clientaddr; --- 479,486 ---- if (funcctx->call_cntr < funcctx->max_calls) { /* for each row */ ! Datum values[11]; ! bool nulls[11]; HeapTuple tuple; PgBackendStatus *beentry; SockAddr zero_clientaddr; *************** pg_stat_get_activity(PG_FUNCTION_ARGS) *** 598,604 **** --- 599,613 ---- nulls[8] = true; nulls[9] = true; } + } + + /* application name */ + if (beentry->st_appname) + values[10] = CStringGetTextDatum(beentry->st_appname); + else + nulls[10] = true; + } else { *************** pg_stat_get_activity(PG_FUNCTION_ARGS) *** 610,615 **** --- 619,625 ---- nulls[7] = true; nulls[8] = true; nulls[9] = true; + nulls[10] = true; } tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index 61751bb..ae4aa8d 100644 *** a/src/backend/utils/error/elog.c --- b/src/backend/utils/error/elog.c *************** *** 68,73 **** --- 68,74 ---- #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" *************** log_line_prefix(StringInfo buf, ErrorDat *** 1798,1803 **** --- 1799,1814 ---- /* process the option */ switch (Log_line_prefix[i]) { + case 'a': + if (application_name) + { + const char *appname = application_name; + + if (appname == NULL || *appname == '\0') + appname = _("[unknown]"); + appendStringInfo(buf, "%s", appname); + } + break; case 'u': if (MyProcPort) { *************** write_csvlog(ErrorData *edata) *** 2101,2109 **** --- 2112,2125 ---- appendStringInfo(&msgbuf, "%s:%d", edata->filename, edata->lineno); appendCSVLiteral(&buf, msgbuf.data); + appendStringInfoCharMacro(&buf, ','); pfree(msgbuf.data); } + /* application name */ + if (application_name) + appendCSVLiteral(&buf, application_name); + appendStringInfoChar(&buf, '\n'); /* If in the syslogger process, try to write messages direct to file */ diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index a0a6605..336c9d6 100644 *** a/src/backend/utils/misc/guc.c --- b/src/backend/utils/misc/guc.c *************** char *pgstat_temp_directory; *** 378,383 **** --- 378,385 ---- char *default_do_language; + char *application_name; + int tcp_keepalives_idle; int tcp_keepalives_interval; int tcp_keepalives_count; *************** static struct config_string ConfigureNam *** 2534,2539 **** --- 2536,2550 ---- "plpgsql", NULL, NULL }, + { + {"application_name", PGC_USERSET, LOGGING, + gettext_noop("Sets the application name to be reported in statistics and logs."), + NULL + }, + &application_name, + "", NULL, NULL + }, + /* End-of-list marker */ { {NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index 4c5f159..f2accd2 100644 *** a/src/backend/utils/misc/postgresql.conf.sample --- b/src/backend/utils/misc/postgresql.conf.sample *************** *** 334,339 **** --- 334,340 ---- #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 diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 90111e9..9d5da3d 100644 *** a/src/include/catalog/pg_proc.h --- b/src/include/catalog/pg_proc.h *************** DATA(insert OID = 2784 ( pg_stat_get_la *** 2999,3005 **** 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_ )); 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"); --- 2999,3005 ---- 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,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"); diff --git a/src/include/pgstat.h b/src/include/pgstat.h index 91ad364..4012888 100644 *** a/src/include/pgstat.h --- b/src/include/pgstat.h *************** typedef struct PgBackendStatus *** 564,571 **** --- 564,575 ---- /* Is backend currently waiting on an lmgr lock? */ bool st_waiting; + /* application name */ + char *st_appname; + /* current command string; MUST be null-terminated */ char *st_activity; + } PgBackendStatus; /* diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index 66617f3..eb64cb5 100644 *** a/src/include/utils/guc.h --- b/src/include/utils/guc.h *************** extern char *HbaFileName; *** 181,186 **** --- 181,188 ---- extern char *IdentFileName; extern char *external_pid_file; + extern char *application_name; + extern char *default_do_language; extern int tcp_keepalives_idle; diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 011ebab..5599ba4 100644 *** a/src/interfaces/libpq/fe-connect.c --- b/src/interfaces/libpq/fe-connect.c *************** static const PQconninfoOption PQconninfo *** 164,169 **** --- 164,175 ---- {"options", "PGOPTIONS", DefaultOption, NULL, "Backend-Debug-Options", "D", 40}, + {"application_name", "PGAPPNAME", NULL, NULL, + "Application-Name", "", 40}, + + {"fallback_application_name", NULL, NULL, NULL, + "Fallback-Application-Name", "", 40}, + #ifdef USE_SSL /* *************** connectOptions1(PGconn *conn, const char *** 416,421 **** --- 422,431 ---- 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"); *************** PQconnectPoll(PGconn *conn) *** 1064,1070 **** case CONNECTION_MADE: break; ! /* We allow pqSetenvPoll to decide whether to proceed. */ case CONNECTION_SETENV: break; --- 1074,1080 ---- case CONNECTION_MADE: break; ! /* We allow pqSetenvPoll/pqAppnamePoll to decide whether to proceed. */ case CONNECTION_SETENV: break; *************** keep_going: /* We will come back to *** 1888,1894 **** conn->addrlist = NULL; conn->addr_cur = NULL; ! /* Fire up post-connection housekeeping if needed */ if (PG_PROTOCOL_MAJOR(conn->pversion) < 3) { conn->status = CONNECTION_SETENV; --- 1898,1910 ---- conn->addrlist = NULL; conn->addr_cur = NULL; ! /* ! * Note: To avoid changing the application visible connection states ! * the v2 enviroment setup and the v3 application name setup ! * both happen in the CONNECTION_SETENV state. ! */ ! ! /* Fire up post-connection housekeeping or appname setup if needed */ if (PG_PROTOCOL_MAJOR(conn->pversion) < 3) { conn->status = CONNECTION_SETENV; *************** keep_going: /* We will come back to *** 1896,1901 **** --- 1912,1923 ---- 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_OPTION_SEND; + return PGRES_POLLING_WRITING; + } /* Otherwise, we are open for business! */ conn->status = CONNECTION_OK; *************** keep_going: /* We will come back to *** 1903,1918 **** } 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; --- 1925,1948 ---- } case CONNECTION_SETENV: ! { /* ! * Do post-connection housekeeping (only needed in protocol 2.0) ! * or setup the application name in PG8.5/v3+ * * We pretend that the connection is OK for the duration of these * queries. */ + PostgresPollingStatusType ret = PGRES_POLLING_OK; + conn->status = CONNECTION_OK; ! if (PG_PROTOCOL_MAJOR(conn->pversion) < 3) ! ret = pqSetenvPoll(conn); ! else if (conn->sversion >= 80500 && (conn->appname || conn->fbappname)) ! ret = pqAppnamePoll(conn); ! ! switch (ret) { case PGRES_POLLING_OK: /* Success */ break; *************** keep_going: /* We will come back to *** 1932,1937 **** --- 1962,1968 ---- /* We are open for business! */ conn->status = CONNECTION_OK; return PGRES_POLLING_OK; + } default: appendPQExpBuffer(&conn->errorMessage, *************** makeEmptyPGconn(void) *** 2000,2005 **** --- 2031,2037 ---- 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; *************** freePGconn(PGconn *conn) *** 2082,2087 **** --- 2114,2123 ---- 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) *************** PQregisterThreadLock(pgthreadlock_t newh *** 4057,4059 **** --- 4093,4226 ---- return prev; } + + /* + * pqAppnamePoll + * + * Polls the process of passing the values of the application name to the backend. + */ + PostgresPollingStatusType + pqAppnamePoll(PGconn *conn) + { + PGresult *res; + + if (conn == NULL || conn->status == CONNECTION_BAD) + return PGRES_POLLING_FAILED; + + /* Check whether there are any data for us */ + switch (conn->appname_state) + { + /* These is a reading state */ + case APPNAME_STATE_OPTION_WAIT: + { + /* Load waiting data */ + int n = pqReadData(conn); + + if (n < 0) + goto error_return; + if (n == 0) + return PGRES_POLLING_READING; + + break; + } + + /* These is a writing state, so we just proceed. */ + case APPNAME_STATE_OPTION_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 %c, " + "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_OPTION_SEND: + { + char *val, *safeVal, *setQuery; + int len; + + /* Use the appname if present, otherwise use the fallback */ + val = conn->appname ? conn->appname : conn->fbappname; + len = strlen(val); + + /* We need to sanitise the data */ + safeVal = malloc((len * 2) + 1); + PQescapeStringConn(conn, safeVal, val, len, NULL); + + setQuery = malloc(strlen(safeVal) + 26); + sprintf(setQuery, "SET application_name = '%s'", safeVal); + + if (!PQsendQuery(conn, setQuery)) + { + if (safeVal) + free(safeVal); + if (setQuery) + free(setQuery); + goto error_return; + } + + if (safeVal) + free(safeVal); + if (setQuery) + free(setQuery); + + conn->appname_state = APPNAME_STATE_OPTION_WAIT; + + break; + } + + case APPNAME_STATE_OPTION_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 state %c, " + "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; + } diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index 724e922..0118d96 100644 *** a/src/interfaces/libpq/libpq-int.h --- b/src/interfaces/libpq/libpq-int.h *************** typedef enum *** 244,249 **** --- 244,257 ---- SETENV_STATE_IDLE } PGSetenvStatusType; + /* PGAppnameStatusType defines the state of the PQAppname state machine */ + typedef enum + { + APPNAME_STATE_OPTION_SEND, /* About to send the appname */ + APPNAME_STATE_OPTION_WAIT, /* Waiting for above send to complete */ + APPNAME_STATE_IDLE + } PGAppnameStatusType; + /* Typedef for the EnvironmentOptions[] array */ typedef struct PQEnvironmentOption { *************** struct pg_conn *** 295,300 **** --- 303,310 ---- * 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; *************** struct pg_conn *** 349,354 **** --- 359,365 ---- 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 */ *************** extern char *const pgresStatus[]; *** 458,463 **** --- 469,475 ---- extern int pqPacketSend(PGconn *conn, char pack_type, const void *buf, size_t buf_len); extern bool pqGetHomeDirectory(char *buf, int bufsize); + extern PostgresPollingStatusType pqAppnamePoll(PGconn *conn); #ifdef ENABLE_THREAD_SAFETY extern pgthreadlock_t pg_g_threadlock; diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 9561a23..a402543 100644 *** a/src/test/regress/expected/rules.out --- b/src/test/regress/expected/rules.out *************** SELECT viewname, definition FROM pg_view *** 1289,1295 **** 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_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; --- 1289,1295 ---- 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, 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; -- 1.6.5.12.gd65df24
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers