Application name patch - v4
Updated application name patch, including a GUC assign hook to clean
the application name of any unsafe characters, per discussion.
Regards, Dave
--
Dave Page
EnterpriseDB UK: http://www.enterprisedb.com
Attachments:
appname-v4.diffapplication/octet-stream; name=appname-v4.diffDownload
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index e42f301..40c46cc 100644
*** a/doc/src/sgml/config.sgml
--- b/doc/src/sgml/config.sgml
*************** local0.* /var/log/postgresql
*** 2881,2886 ****
--- 2881,2904 ----
<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.
+ Only ASCII characters with decimal values between 32 and 126 may be used
+ in the <varname>application_name</varname> configuration variable. Other
+ values will be replaced with the <literal>?</literal> character.
+ </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 ****
--- 3343,3349 ----
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..1337363 100644
*** a/doc/src/sgml/libpq.sgml
--- b/doc/src/sgml/libpq.sgml
***************
*** 248,253 ****
--- 248,280 ----
</para>
</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>
*************** myEventProc(PGEventId evtId, void *evtIn
*** 5781,5786 ****
--- 5808,5823 ----
linkend="libpq-connect-options"> connection parameter.
</para>
</listitem>
+
+ <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>
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 31da86d..580d2fa 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..519cef4 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..03243a4 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..8b1f255 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,2108 ****
--- 2112,2124 ----
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');
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index a0a6605..0dd005c 100644
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
*************** static bool assign_maxconnections(int ne
*** 168,173 ****
--- 168,174 ----
static bool assign_autovacuum_max_workers(int newval, bool doit, GucSource source);
static bool assign_effective_io_concurrency(int newval, bool doit, GucSource source);
static const char *assign_pgstat_temp_directory(const char *newval, bool doit, GucSource source);
+ static const char *assign_application_name(const char *newval, bool doit, GucSource source);
static char *config_enum_get_options(struct config_enum * record,
const char *prefix, const char *suffix,
*************** char *pgstat_temp_directory;
*** 378,383 ****
--- 379,386 ----
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 ****
--- 2537,2551 ----
"plpgsql", NULL, NULL
},
+ {
+ {"application_name", PGC_USERSET, LOGGING,
+ gettext_noop("Sets the application name to be reported in statistics and logs."),
+ NULL
+ },
+ &application_name,
+ "", assign_application_name, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL
*************** InitializeGUCOptions(void)
*** 3283,3289 ****
env = getenv("PGCLIENTENCODING");
if (env != NULL)
SetConfigOption("client_encoding", env, PGC_POSTMASTER, PGC_S_ENV_VAR);
!
/*
* rlimit isn't exactly an "environment variable", but it behaves about
* the same. If we can identify the platform stack depth rlimit, increase
--- 3295,3301 ----
env = getenv("PGCLIENTENCODING");
if (env != NULL)
SetConfigOption("client_encoding", env, PGC_POSTMASTER, PGC_S_ENV_VAR);
!
/*
* rlimit isn't exactly an "environment variable", but it behaves about
* the same. If we can identify the platform stack depth rlimit, increase
*************** assign_pgstat_temp_directory(const char
*** 7717,7720 ****
--- 7729,7753 ----
return newval;
}
+ static const char *
+ assign_application_name(const char *newval, bool doit, GucSource source)
+ {
+ /* Only allow clean ASCII chars in the application name */
+ int x;
+
+ char *repval = guc_malloc(ERROR, strlen(newval) + 1);
+ repval[0] = 0;
+
+ for (x=0; x<strlen(newval); x++)
+ {
+ if (newval[x] < 32 || newval[x] > 126)
+ repval[x] = '?';
+ else
+ repval[x] = newval[x];
+ }
+
+ repval[x+1] = 0;
+ return repval;
+ }
+
#include "guc-file.c"
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 4c5f159..76f2c4e 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..455517b 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..3f12dfe 100644
*** a/src/interfaces/libpq/fe-connect.c
--- b/src/interfaces/libpq/fe-connect.c
*************** static const PQconninfoOption PQconninfo
*** 163,168 ****
--- 163,174 ----
{"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
*** 1927,1938 ****
default:
goto error_return;
! }
!
/* We are open for business! */
conn->status = CONNECTION_OK;
return PGRES_POLLING_OK;
!
default:
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext(
--- 1957,1969 ----
default:
goto error_return;
! }
!
/* We are open for business! */
conn->status = CONNECTION_OK;
return PGRES_POLLING_OK;
! }
!
default:
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext(
*************** 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..2aa93f9 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;
Dave Page <dpage@pgadmin.org> writes:
Updated application name patch, including a GUC assign hook to clean
the application name of any unsafe characters, per discussion.
Applied with assorted editorialization. There were a couple of
definitional issues that I don't recall if we had consensus on:
1. The patch prevents non-superusers from seeing other users'
application names in pg_stat_activity. This seems at best pretty
debatable to me. Yes, it supports usages in which you want to put
security-sensitive information into the appname, but at the cost of
disabling (perfectly reasonable) usages where you don't. If we made
the app name universally visible, people simply wouldn't put security
sensitive info in it, the same as they don't put it on the command line.
Should we change this?
(While I'm looking at it, I wonder why client_addr and client_port
are similarly hidden.)
2. I am wondering if we should mark application_name as
GUC_NO_RESET_ALL. As-is, the value sent at libpq initialization
will be lost during RESET ALL, which would probably surprise people.
On the other hand, not resetting it might surprise other people.
If we were able to send it in the startup packet then this wouldn't
be a problem, but we are far from being able to do that.
Comments?
regards, tom lane
On Sat, Nov 28, 2009 at 06:47:49PM -0500, Tom Lane wrote:
Dave Page <dpage@pgadmin.org> writes:
Updated application name patch, including a GUC assign hook to clean
the application name of any unsafe characters, per discussion.Applied with assorted editorialization. There were a couple of
definitional issues that I don't recall if we had consensus on:1. The patch prevents non-superusers from seeing other users'
application names in pg_stat_activity. This seems at best pretty
debatable to me. Yes, it supports usages in which you want to put
security-sensitive information into the appname, but at the cost of
disabling (perfectly reasonable) usages where you don't. If we made
the app name universally visible, people simply wouldn't put security
sensitive info in it, the same as they don't put it on the command line.
Should we change this?(While I'm looking at it, I wonder why client_addr and client_port
are similarly hidden.)
I vote for showing it to everyone, superuser or otherwise, though I can't
really say why I feel that way.
2. I am wondering if we should mark application_name as
GUC_NO_RESET_ALL. As-is, the value sent at libpq initialization
will be lost during RESET ALL, which would probably surprise people.
On the other hand, not resetting it might surprise other people.
If we were able to send it in the startup packet then this wouldn't
be a problem, but we are far from being able to do that.
Nothing I've written uses RESET ALL, but if it did, I expect it would be
because whatever the connection was being used for in the past differs
substantially from whatever I plan to use it for in the future, which seems a
suitable time also to change application_name. I vote against
GUC_NO_RESET_ALL.
--
Joshua Tolley / eggyknap
End Point Corporation
http://www.endpoint.com
On Sunday 29 November 2009 00:47:49 Tom Lane wrote:
Dave Page <dpage@pgadmin.org> writes:
Updated application name patch, including a GUC assign hook to clean
the application name of any unsafe characters, per discussion.Applied with assorted editorialization. There were a couple of
definitional issues that I don't recall if we had consensus on:1. The patch prevents non-superusers from seeing other users'
application names in pg_stat_activity. This seems at best pretty
debatable to me. Yes, it supports usages in which you want to put
security-sensitive information into the appname, but at the cost of
disabling (perfectly reasonable) usages where you don't. If we made
the app name universally visible, people simply wouldn't put security
sensitive info in it, the same as they don't put it on the command line.
Should we change this?
I personally would prefer if it were not protected and explicitly documented
as such - I cant really see a use case where one would want to store something
really private in there.
(While I'm looking at it, I wonder why client_addr and client_port
are similarly hidden.)
In a shared hosting environment this is somewhat sensible - afair some data
protection laws even require that nobody except the designated receiver of
information is able to get that information.
Whether shared hosting is sensible is another matter.
2. I am wondering if we should mark application_name as
GUC_NO_RESET_ALL. As-is, the value sent at libpq initialization
will be lost during RESET ALL, which would probably surprise people.
On the other hand, not resetting it might surprise other people.
If we were able to send it in the startup packet then this wouldn't
be a problem, but we are far from being able to do that.
One possibility would be to make it possible to issue SETs that behave as if
set in a startup packet - imho its an implementation detail that SET currently
is used.
Andres
On Sat, Nov 28, 2009 at 7:27 PM, Joshua Tolley <eggyknap@gmail.com> wrote:
On Sat, Nov 28, 2009 at 06:47:49PM -0500, Tom Lane wrote:
Dave Page <dpage@pgadmin.org> writes:
Updated application name patch, including a GUC assign hook to clean
the application name of any unsafe characters, per discussion.Applied with assorted editorialization. There were a couple of
definitional issues that I don't recall if we had consensus on:1. The patch prevents non-superusers from seeing other users'
application names in pg_stat_activity. This seems at best pretty
debatable to me. Yes, it supports usages in which you want to put
security-sensitive information into the appname, but at the cost of
disabling (perfectly reasonable) usages where you don't. If we made
the app name universally visible, people simply wouldn't put security
sensitive info in it, the same as they don't put it on the command line.
Should we change this?(While I'm looking at it, I wonder why client_addr and client_port
are similarly hidden.)I vote for showing it to everyone, superuser or otherwise, though I can't
really say why I feel that way.
+1.
2. I am wondering if we should mark application_name as
GUC_NO_RESET_ALL. As-is, the value sent at libpq initialization
will be lost during RESET ALL, which would probably surprise people.
On the other hand, not resetting it might surprise other people.
If we were able to send it in the startup packet then this wouldn't
be a problem, but we are far from being able to do that.Nothing I've written uses RESET ALL, but if it did, I expect it would be
because whatever the connection was being used for in the past differs
substantially from whatever I plan to use it for in the future, which seems a
suitable time also to change application_name. I vote against
GUC_NO_RESET_ALL.
+1 to this, too.
...Robert
On Sat, Nov 28, 2009 at 11:47 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Dave Page <dpage@pgadmin.org> writes:
Updated application name patch, including a GUC assign hook to clean
the application name of any unsafe characters, per discussion.Applied with assorted editorialization. There were a couple of
definitional issues that I don't recall if we had consensus on:1. The patch prevents non-superusers from seeing other users'
application names in pg_stat_activity. This seems at best pretty
debatable to me. Yes, it supports usages in which you want to put
security-sensitive information into the appname, but at the cost of
disabling (perfectly reasonable) usages where you don't. If we made
the app name universally visible, people simply wouldn't put security
sensitive info in it, the same as they don't put it on the command line.
Should we change this?
Uh, yeah, I guess. That wasn't a concious decision, more a copy n
paste inherited 'feature'.
(While I'm looking at it, I wonder why client_addr and client_port
are similarly hidden.)2. I am wondering if we should mark application_name as
GUC_NO_RESET_ALL. As-is, the value sent at libpq initialization
will be lost during RESET ALL, which would probably surprise people.
On the other hand, not resetting it might surprise other people.
If we were able to send it in the startup packet then this wouldn't
be a problem, but we are far from being able to do that.
In the use cases I envisage for this, the appname is more a property
of the connection than the session, thus I wouldn't expect it to
change following a RESET ALL. That said, one could then argue that it
should RESET to the connection-time value...
I think we should use GUC_NO_RESET_ALL.
--
Dave Page
EnterpriseDB UK: http://www.enterprisedb.com
Dave Page <dpage@pgadmin.org> writes:
On Sat, Nov 28, 2009 at 11:47 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
1. The patch prevents non-superusers from seeing other users'
application names in pg_stat_activity. �This seems at best pretty
debatable to me. �Yes, it supports usages in which you want to put
security-sensitive information into the appname, but at the cost of
disabling (perfectly reasonable) usages where you don't. �If we made
the app name universally visible, people simply wouldn't put security
sensitive info in it, the same as they don't put it on the command line.
Should we change this?
Uh, yeah, I guess. That wasn't a concious decision, more a copy n
paste inherited 'feature'.
OK. Everybody seems to agree it should not be hidden, so I'll go change
that.
2. I am wondering if we should mark application_name as
GUC_NO_RESET_ALL.
I think we should use GUC_NO_RESET_ALL.
I agree with you, but it seems we have at least as many votes to not do
that. Any other votes out there?
regards, tom lane
Hi,
Le 29 nov. 2009 à 18:22, Tom Lane a écrit :
I think we should use GUC_NO_RESET_ALL.
I agree with you, but it seems we have at least as many votes to not do
that. Any other votes out there?
Driven by the pooler use case (pgbouncer, even), I'd say RESET ALL should reset also the application name. And the connection value is not tied any more to something sensible as soon as you have pooling in there...
Regards,
--
dim
Dimitri Fontaine <dfontaine@hi-media.com> writes:
Le 29 nov. 2009 � 18:22, Tom Lane a �crit :
I think we should use GUC_NO_RESET_ALL.
I agree with you, but it seems we have at least as many votes to not do
that. Any other votes out there?
Driven by the pooler use case (pgbouncer, even), I'd say RESET ALL should reset also the application name. And the connection value is not tied any more to something sensible as soon as you have pooling in there...
The thing is that the libpq API treats application_name as a *property
of the connection*. You shouldn't expect it to go away on RESET ALL,
any more than you'd expect RESET ALL to cause you to be reconnected to
some other database.
If a pooler wants application_name to be cleared when it issues RESET
ALL, I think it ought to be setting the name via SET, not via the libpq
connection option.
But it's certainly true that using GUC_NO_RESET_ALL would be a quick
kluge rather than a proper solution. Andres Freund suggested upthread
that we should fix this by extending SET:
: One possibility would be to make it possible to issue SETs that behave
: as if set in a startup packet - imho its an implementation detail that
: SET currently is used.
I think there's a good deal of merit in this, and it would't be hard at
all to implement, seeing that we already have SET LOCAL and SET SESSION.
We could add a third keyword, say SET DEFAULT, that would have the
behavior of setting the value in a fashion that would persist across
resets. I'm not sure that DEFAULT is exactly le mot juste here, but
agreeing on a keyword would probably be the hardest part of making it
happen.
regards, tom lane
Tom Lane wrote:
: One possibility would be to make it possible to issue SETs that
behave : as if set in a startup packet - imho its an implementation
detail that : SET currently is used.I think there's a good deal of merit in this, and it would't be hard
at all to implement, seeing that we already have SET LOCAL and SET
SESSION. We could add a third keyword, say SET DEFAULT, that would
have the behavior of setting the value in a fashion that would
persist across resets. I'm not sure that DEFAULT is exactly le mot
juste here, but agreeing on a keyword would probably be the hardest
part of making it happen.
Hm, but without a way to prevent the users of a connection pool from
issuing "SET DEFAULT", that leaves a connection pool with no way to
revert a connection to a known state.
How about "SET CONNECTION", with an additional GUC called
connection_setup which can only be set to true, never back to false.
Once connection_setup is set to true, further SET CONNECTION attempts
would fail.
In a way, this mimics startup-packet SETs without actually doing things
in the startup packet.
best regards,
Florian Pflug
On Sun, Nov 29, 2009 at 8:47 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Dave Page <dpage@pgadmin.org> writes:
Updated application name patch, including a GUC assign hook to clean
the application name of any unsafe characters, per discussion.Applied with assorted editorialization. There were a couple of
definitional issues that I don't recall if we had consensus on:
Why doesn't application_name appear in postgresql.conf.sample?
That is expected to be set from only libpq?
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
Fujii Masao <masao.fujii@gmail.com> writes:
Why doesn't application_name appear in postgresql.conf.sample?
That is expected to be set from only libpq?
It would seem pretty silly to set it in the conf file. You *can*,
if you want, but I see no reason to list it there.
regards, tom lane
On Mon, Nov 30, 2009 at 10:20 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Fujii Masao <masao.fujii@gmail.com> writes:
Why doesn't application_name appear in postgresql.conf.sample?
That is expected to be set from only libpq?It would seem pretty silly to set it in the conf file. You *can*,
if you want, but I see no reason to list it there.
Yeah, I see your point. But, is it a policy not to put such parameter
(other than that for debug) on postgresql.conf.sample?
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
On Mon, Nov 30, 2009 at 11:21 AM, Fujii Masao <masao.fujii@gmail.com> wrote:
On Mon, Nov 30, 2009 at 10:20 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Fujii Masao <masao.fujii@gmail.com> writes:
Why doesn't application_name appear in postgresql.conf.sample?
That is expected to be set from only libpq?It would seem pretty silly to set it in the conf file. You *can*,
if you want, but I see no reason to list it there.Yeah, I see your point. But, is it a policy not to put such parameter
(other than that for debug) on postgresql.conf.sample?
Ooops! I missed GUC_NOT_IN_SAMPLE parameters. Sorry for noise.
Regards,
--
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
Hi,
On Monday 30 November 2009 01:16:43 Florian G. Pflug wrote:
Tom Lane wrote:
: One possibility would be to make it possible to issue SETs that
behave : as if set in a startup packet - imho its an implementation
detail that : SET currently is used.I think there's a good deal of merit in this, and it would't be hard
at all to implement, seeing that we already have SET LOCAL and SET
SESSION. We could add a third keyword, say SET DEFAULT, that would
have the behavior of setting the value in a fashion that would
persist across resets. I'm not sure that DEFAULT is exactly le mot
juste here, but agreeing on a keyword would probably be the hardest
part of making it happen.Hm, but without a way to prevent the users of a connection pool from
issuing "SET DEFAULT", that leaves a connection pool with no way to
revert a connection to a known state.
Perhaps we should only allow a few parameters to be SET as a connection
default - then the pooler would have to issue those just as it has to do for
actual connection defaults.
How about "SET CONNECTION", with an additional GUC called
connection_setup which can only be set to true, never back to false.
Once connection_setup is set to true, further SET CONNECTION attempts
would fail.
How would that help the pooler case? The next connection to it might be from a
different application.
Andres
Le 30 nov. 2009 à 00:25, Tom Lane a écrit :
The thing is that the libpq API treats application_name as a *property
of the connection*.
Oh. Yeah.
We could add a third keyword, say SET DEFAULT, that would have the
behavior of setting the value in a fashion that would persist across
resets. I'm not sure that DEFAULT is exactly le mot juste here, but
agreeing on a keyword would probably be the hardest part of making it
happen.
I vaguely remember you explaining how hard it would be to be able to predict the value we RESET to as soon as we add this or that possibility. That's very vague, sorry, but only leaves a bad impression on the keyword choice (bikeshedding, I should open a club).
So what about SET CONNECTION application_name TO 'whatever'?
Regards,
--
dim
On Mon, Nov 30, 2009 at 4:11 PM, Dimitri Fontaine
<dfontaine@hi-media.com> wrote:
Le 30 nov. 2009 à 00:25, Tom Lane a écrit :
The thing is that the libpq API treats application_name as a *property
of the connection*.Oh. Yeah.
We could add a third keyword, say SET DEFAULT, that would have the
behavior of setting the value in a fashion that would persist across
resets. I'm not sure that DEFAULT is exactly le mot juste here, but
agreeing on a keyword would probably be the hardest part of making it
happen.I vaguely remember you explaining how hard it would be to be able to predict the value we RESET to as soon as we add this or that possibility. That's very vague, sorry, but only leaves a bad impression on the keyword choice (bikeshedding, I should open a club).
So what about SET CONNECTION application_name TO 'whatever'?
I still don't really understand why we wouldn't want RESET ALL to
reset the application name. In what circumstances would you want the
application name to stay the same across a RESET ALL?
...Robert
Le 30 nov. 2009 à 22:38, Robert Haas a écrit :
I still don't really understand why we wouldn't want RESET ALL to
reset the application name. In what circumstances would you want the
application name to stay the same across a RESET ALL?
I can't see any use case, but SET/RESET is tied to SESSION whereas application_name is a CONNECTION property. So it's a hard sell that reseting the session will change connection properties.
Regards,
--
dim
On Mon, Nov 30, 2009 at 4:54 PM, Dimitri Fontaine
<dfontaine@hi-media.com> wrote:
Le 30 nov. 2009 à 22:38, Robert Haas a écrit :
I still don't really understand why we wouldn't want RESET ALL to
reset the application name. In what circumstances would you want the
application name to stay the same across a RESET ALL?I can't see any use case, but SET/RESET is tied to SESSION whereas application_name is a CONNECTION property. So it's a hard sell that reseting the session will change connection properties.
Is there any technical difference between a connection property and a
session property? If so, what is it?
ISTM that the only time you're likely going to use RESET ALL is in a
connection pooling environment, and that if you're in a connection
pooling environment you probably want to reset the application name
along with everything else. I might be wrong, but that's how it seems
to me at first blush.
...Robert
Robert Haas wrote:
On Mon, Nov 30, 2009 at 4:54 PM, Dimitri Fontaine
<dfontaine@hi-media.com> wrote:Le 30 nov. 2009 ? 22:38, Robert Haas a ?crit :
I still don't really understand why we wouldn't want RESET ALL to
reset the application name. ?In what circumstances would you want the
application name to stay the same across a RESET ALL?I can't see any use case, but SET/RESET is tied to SESSION whereas application_name is a CONNECTION property. So it's a hard sell that reseting the session will change connection properties.
Is there any technical difference between a connection property and a
session property? If so, what is it?ISTM that the only time you're likely going to use RESET ALL is in a
connection pooling environment, and that if you're in a connection
pooling environment you probably want to reset the application name
along with everything else. I might be wrong, but that's how it seems
to me at first blush.
Uh, what does it mean to reset the application name? Are you resetting
it to what it was before the session started, or to a blank string?
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Robert Haas <robertmhaas@gmail.com> writes:
On Mon, Nov 30, 2009 at 4:54 PM, Dimitri Fontaine
<dfontaine@hi-media.com> wrote:Le 30 nov. 2009 � 22:38, Robert Haas a �crit :
I still don't really understand why we wouldn't want RESET ALL to
reset the application name. �In what circumstances would you want the
application name to stay the same across a RESET ALL?I can't see any use case, but SET/RESET is tied to SESSION whereas application_name is a CONNECTION property. So it's a hard sell that reseting the session will change connection properties.
Is there any technical difference between a connection property and a
session property? If so, what is it?
The point is that every other thing you can set in a libpq connection
string is persistent throughout the connection. For the ones that you
can change at all, such as client_encoding, *RESET ALL actually resets
it to what was specified in the connection string*. It does not satisfy
the POLA for application_name to behave differently.
I think the argument about poolers expecting something different is
hogwash. A pooler would want RESET ALL to revert the connection state
to what it was at establishment. That would include whatever
application name the pooler would have specified when it started the
connection, I should think.
The only reason we're even having this discussion is that libpq
isn't able to make application_name work exactly like its other
connection parameters because of the backwards-compatibility issue.
Maybe we should think a bit harder about that. Or else give up
having libpq manage it like a connection parameter.
regards, tom lane
On Tuesday 01 December 2009 01:11:13 Tom Lane wrote:
Robert Haas <robertmhaas@gmail.com> writes:
On Mon, Nov 30, 2009 at 4:54 PM, Dimitri Fontaine
<dfontaine@hi-media.com> wrote:
Le 30 nov. 2009 à 22:38, Robert Haas a écrit :
I still don't really understand why we wouldn't want RESET ALL to
reset the application name. In what circumstances would you want the
application name to stay the same across a RESET ALL?I can't see any use case, but SET/RESET is tied to SESSION whereas
application_name is a CONNECTION property. So it's a hard sell that
reseting the session will change connection properties.Is there any technical difference between a connection property and a
session property? If so, what is it?I think the argument about poolers expecting something different is
hogwash. A pooler would want RESET ALL to revert the connection state
to what it was at establishment. That would include whatever
application name the pooler would have specified when it started the
connection, I should think.
Actually I think the poolers make a good case for a SET variant which emulates
connection set variables...
RESET ALL in a connection pooler does different things than RESET ALL outside
of one.
Andres
On 12/1/09, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
On Mon, Nov 30, 2009 at 4:54 PM, Dimitri Fontaine
<dfontaine@hi-media.com> wrote:Le 30 nov. 2009 à 22:38, Robert Haas a écrit :
I still don't really understand why we wouldn't want RESET ALL to
reset the application name. In what circumstances would you want the
application name to stay the same across a RESET ALL?I can't see any use case, but SET/RESET is tied to SESSION whereas application_name is a CONNECTION property. So it's a hard sell that reseting the session will change connection properties.
Is there any technical difference between a connection property and a
session property? If so, what is it?The point is that every other thing you can set in a libpq connection
string is persistent throughout the connection. For the ones that you
can change at all, such as client_encoding, *RESET ALL actually resets
it to what was specified in the connection string*. It does not satisfy
the POLA for application_name to behave differently.
+1
This SESSION/CONNECITION terminology is confusing, better would be
talk about client connection/session (client->pooler) and server
connection/session (pooler->server) if you are talking about pooling.
I think the argument about poolers expecting something different is
hogwash. A pooler would want RESET ALL to revert the connection state
to what it was at establishment. That would include whatever
application name the pooler would have specified when it started the
connection, I should think.The only reason we're even having this discussion is that libpq
isn't able to make application_name work exactly like its other
connection parameters because of the backwards-compatibility issue.
Maybe we should think a bit harder about that. Or else give up
having libpq manage it like a connection parameter.
Making it work in session pooling mode (pgpool) is easy - RESET ALL
and SET needs to work.
The question is whether it should work also in transaction
pooling mode (pgbouncer / JDBC). I see 2 variants:
1. Clients are allowed to specify it only in startup packet.
But, uh, poolers can set it also in the middle of session.
2. Make it into protocol-tracked variable.
The 1) seems inconsistent and backwards-incompatible - client does
not know server version yet and old servers dont accept it.
I don't see problems with 2).
Or we could decide it is not meant for transaction pooling environments.
--
marko
On Tue, Dec 1, 2009 at 12:26 AM, Andres Freund <andres@anarazel.de> wrote:
Actually I think the poolers make a good case for a SET variant which emulates
connection set variables...RESET ALL in a connection pooler does different things than RESET ALL outside
of one.
Eh? Not sure I follow that, but then I haven't had a coffee yet.
I do see the argument that RESET ALL should revert user changes to
application_name though, but I maintain they should reset to the value
set at connection time, not to null. As has been pointed out already,
other values set at connection time cannot be reset, so allowing that
for application name does seem like a POLA violation.
Upthread, Tom suggested a new 'SET DEFAULT ...' variant of SET which
could be used to set the default GUC value that RESET would revert to.
This seems to me to be the ideal solution, and I'd somewhat hesitantly
volunteer to work on it (hesitantly as it means touching the parser
and other areas of the code I currently have no experience of).
--
Dave Page
EnterpriseDB UK: http://www.enterprisedb.com
On Tuesday 01 December 2009 09:59:17 Dave Page wrote:
On Tue, Dec 1, 2009 at 12:26 AM, Andres Freund <andres@anarazel.de> wrote:
Actually I think the poolers make a good case for a SET variant which
emulates connection set variables...RESET ALL in a connection pooler does different things than RESET ALL
outside of one.Eh? Not sure I follow that, but then I haven't had a coffee yet.
Well. RESET ALL in a pooler sets values to the initial connection values the
pooler had, not the ones of pooled connection.
On the same time there are multiple people complaining about such default
values being contraproductive to pooling environments because they reset to
the wrong values.
I dont really get that argument - the pooler should just issue a SET
CONNECTION DEFAULT for all connection values. That would make it far more
transparent than before...
Upthread, Tom suggested a new 'SET DEFAULT ...' variant of SET which
could be used to set the default GUC value that RESET would revert to.
This seems to me to be the ideal solution, and I'd somewhat hesitantly
volunteer to work on it (hesitantly as it means touching the parser
and other areas of the code I currently have no experience of).
As I had initially suggested something like that I agree here.
Andres
Dave Page wrote:
Upthread, Tom suggested a new 'SET DEFAULT ...' variant of SET which
could be used to set the default GUC value that RESET would revert to.
This seems to me to be the ideal solution, and I'd somewhat hesitantly
volunteer to work on it (hesitantly as it means touching the parser
and other areas of the code I currently have no experience of).
If an application can do SET DEFAULT, how does the connection pooler
*really* reset the value back to what it was?
--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com
On Tue, Dec 1, 2009 at 9:16 AM, Heikki Linnakangas
<heikki.linnakangas@enterprisedb.com> wrote:
Dave Page wrote:
Upthread, Tom suggested a new 'SET DEFAULT ...' variant of SET which
could be used to set the default GUC value that RESET would revert to.
This seems to me to be the ideal solution, and I'd somewhat hesitantly
volunteer to work on it (hesitantly as it means touching the parser
and other areas of the code I currently have no experience of).If an application can do SET DEFAULT, how does the connection pooler
*really* reset the value back to what it was?
There has to be some level of trust here :-). As the alternative would
involve bumping the fe-be protocol version, it seems like a reasonable
approach to me.
--
Dave Page
EnterpriseDB UK: http://www.enterprisedb.com
On Tuesday 01 December 2009 10:16:45 Heikki Linnakangas wrote:
Dave Page wrote:
Upthread, Tom suggested a new 'SET DEFAULT ...' variant of SET which
could be used to set the default GUC value that RESET would revert to.
This seems to me to be the ideal solution, and I'd somewhat hesitantly
volunteer to work on it (hesitantly as it means touching the parser
and other areas of the code I currently have no experience of).If an application can do SET DEFAULT, how does the connection pooler
*really* reset the value back to what it was?
Why does it need to? SET DEFAULT should imho only be allowed for values whcih
can be set during connection initiation. For those it can simply issue the
sets anyway.
Andres
The point is that every other thing you can set in a libpq connection
string is persistent throughout the connection. For the ones that you
can change at all, such as client_encoding, *RESET ALL actually resets
it to what was specified in the connection string*. It does not satisfy
the POLA for application_name to behave differently.I think the argument about poolers expecting something different is
hogwash. A pooler would want RESET ALL to revert the connection state
to what it was at establishment. That would include whatever
application name the pooler would have specified when it started the
connection, I should think.
+1. Connection poolers shoud be transparent to the clients.
If some connection poolers want to behavior differently, then probably
they would be better to be called "TP monitor" or some such. TP
monitor has its own API and it is at liberty behave what it
wants. Don't get me wrong. I would not say TP monitor is useless,
rather it has very usefull use cases I think. However, pushing its
semantics about sessions to PostgreSQL side, would be
counterproductive for both TP monitor and PostgreSQL.
--
Tatsuo Ishii
SRA OSS, Inc. Japan
On 12/1/09, Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> wrote:
Dave Page wrote:
Upthread, Tom suggested a new 'SET DEFAULT ...' variant of SET which
could be used to set the default GUC value that RESET would revert to.
This seems to me to be the ideal solution, and I'd somewhat hesitantly
volunteer to work on it (hesitantly as it means touching the parser
and other areas of the code I currently have no experience of).If an application can do SET DEFAULT, how does the connection pooler
*really* reset the value back to what it was?
By doing SET DEFAULT...
There actually *is* a problem that SET DEFAULT would solve:
1) Pooler creates a connection with one default value.
2) Client creates a connection with another default value (giving param
in startup pkt)
3) Pooler does SET to apply client's default values.
4) Client does SET to some random value
5) Client does RESET foo/ALL; expecting get default value from 2), instead
it gets poolers default value from 1).
The inconsistency would be fixed if pooler could do SET DEFAULT in 3).
Note - client doing SET DEFAULT itself would not break anything.
As long we are talking about protocol-tracked parameters...
OTOH, the only app that exhibits the such RESET problem is src/test/regress
so I'm not sure it's worth spending effort to fix this. Especially
as this open door on app doing SET DEFAULT on non-tracked GUC vars,
which seems to be a much bigger problem.
I don't see how this SET DEFAULT would fix the appname vs. poolers problem
in any way.
--
marko
On Tue, 1 Dec 2009 09:59:17 +0100, Dave Page <dpage@pgadmin.org> wrote:
I do see the argument that RESET ALL should revert user changes to
application_name though, but I maintain they should reset to the value
set at connection time, not to null. As has been pointed out already,
other values set at connection time cannot be reset, so allowing that
for application name does seem like a POLA violation.
I'd like to support this Argument.
As I understand this patch from
http://archives.postgresql.org/pgsql-hackers/2009-10/msg00711.php it is
intended to support some kind of feature like the SQL Server
"...;Application Name=MyApp;..." connection string value, making the
name of the user level (or whatever) application name available at the
Database/SQL level.
I don't know about pgpool but as far as I know, some client side
connection pooling implementations use one pool per connection
string/url (.Net Data Providers, JDBC).
They would probably want set the application_name in the startup message
and will expect it to fall back to this value when calling RESET ALL (or
what ever you like to be the command to go back to the values that were
requested on connection startup) on recycling a connection from the pool.
Any other solution would greatly complicate recycling of connections for
per connection string pooling szenarios.
Regards,
Brar
Dave Page <dpage@pgadmin.org> writes:
On Tue, Dec 1, 2009 at 9:16 AM, Heikki Linnakangas
<heikki.linnakangas@enterprisedb.com> wrote:If an application can do SET DEFAULT, how does the connection pooler
*really* reset the value back to what it was?
There has to be some level of trust here :-). As the alternative would
involve bumping the fe-be protocol version, it seems like a reasonable
approach to me.
I don't think that we need to bump the protocol version. The real
alternative here would be that libpq sends a startup packet that
includes application_name, and if it gets an error back from that,
it starts over without the app name. The main disadvantage would
be that you'd get a double connection attempt == more overhead
anytime you use an 8.5+ libpq to connect to 8.4- server. People
never complained that hard about the similar double connection attempt
when 7.4+ libpq connected to 7.3- servers, so maybe we should just
go that way.
regards, tom lane
On Tue, Dec 1, 2009 at 4:19 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
I don't think that we need to bump the protocol version. The real
alternative here would be that libpq sends a startup packet that
includes application_name, and if it gets an error back from that,
it starts over without the app name. The main disadvantage would
be that you'd get a double connection attempt == more overhead
anytime you use an 8.5+ libpq to connect to 8.4- server. People
never complained that hard about the similar double connection attempt
when 7.4+ libpq connected to 7.3- servers, so maybe we should just
go that way.
I looked (briefly) at doing that when we first ran into this
suggestion. As you pointed out at the time, it seemed like that would
require some fairly ugly hackery in fe-connect.c
--
Dave Page
EnterpriseDB UK: http://www.enterprisedb.com
Dave Page <dpage@pgadmin.org> writes:
On Tue, Dec 1, 2009 at 4:19 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
I don't think that we need to bump the protocol version. �The real
alternative here would be that libpq sends a startup packet that
includes application_name, and if it gets an error back from that,
it starts over without the app name.
I looked (briefly) at doing that when we first ran into this
suggestion. As you pointed out at the time, it seemed like that would
require some fairly ugly hackery in fe-connect.c
Perhaps, but at the time it wasn't apparent that issuing a separate SET
would create user-visible behavioral inconsistencies. Now that we've
realized that, I think we should reconsider.
If people are agreed that double connect is a better alternative
I'm willing to go look at how to make it happen.
regards, tom lane
On Tue, Dec 1, 2009 at 4:28 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
If people are agreed that double connect is a better alternative
I still kinda like 'SET DEFAULT', but I'm far from wed to it. A double
connect certainly seems like it would be better than the
inconsistency.
I'm willing to go look at how to make it happen.
That's good, 'cos I'm sure it'll end up being a whole lot less ugly
than if I did it :-)
--
Dave Page
EnterpriseDB UK: http://www.enterprisedb.com
On 12/1/09, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Dave Page <dpage@pgadmin.org> writes:
On Tue, Dec 1, 2009 at 4:19 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
I don't think that we need to bump the protocol version. The real
alternative here would be that libpq sends a startup packet that
includes application_name, and if it gets an error back from that,
it starts over without the app name.I looked (briefly) at doing that when we first ran into this
suggestion. As you pointed out at the time, it seemed like that would
require some fairly ugly hackery in fe-connect.cPerhaps, but at the time it wasn't apparent that issuing a separate SET
would create user-visible behavioral inconsistencies. Now that we've
realized that, I think we should reconsider.If people are agreed that double connect is a better alternative
I'm willing to go look at how to make it happen.
Is it supposed to work with pooling or not?
If the pooler gets new connection with same username:database
as some existing connection, but with different appname,
what it is supposed to do?
--
marko
Marko Kreen <markokr@gmail.com> writes:
If the pooler gets new connection with same username:database
as some existing connection, but with different appname,
what it is supposed to do?
Whatever it wants to. People seem to be imagining that the appname
isn't under the control of the pooler. It's a connection property,
remember? It won't be set at all unless the pooler explicitly sets it
or allows it to be set.
I would imagine that typically a pooler would consider the whole
connection string as defining connection properties and so appname would
work the same as username or anything else, ie, you get shunted into
a different connection pool if you ask for a different appname.
regards, tom lane
On 12/1/09, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Marko Kreen <markokr@gmail.com> writes:
If the pooler gets new connection with same username:database
as some existing connection, but with different appname,
what it is supposed to do?Whatever it wants to. People seem to be imagining that the appname
isn't under the control of the pooler. It's a connection property,
remember? It won't be set at all unless the pooler explicitly sets it
or allows it to be set.I would imagine that typically a pooler would consider the whole
connection string as defining connection properties and so appname would
work the same as username or anything else, ie, you get shunted into
a different connection pool if you ask for a different appname.
No, at least both pgbouncer and pgpool consider only (username, database)
pair as pool identifier. Rest of the startup params are tuned on the fly.
And I think that should stay that way.
Instead, could we make it equal to rest of startup params and track
it's changes via ParamStatus?
That makes it possible for poolers to handle it transparently.
(IOW, you can put several poolers between client and server and
nothing breaks)
--
marko
Marko Kreen <markokr@gmail.com> writes:
No, at least both pgbouncer and pgpool consider only (username, database)
pair as pool identifier. Rest of the startup params are tuned on the fly.
And I think that should stay that way.
If you're happy with handling the existing connection parameters in a given
way, why would you not want application_name behaving that same way?
regards, tom lane
On 12/1/09, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Marko Kreen <markokr@gmail.com> writes:
No, at least both pgbouncer and pgpool consider only (username, database)
pair as pool identifier. Rest of the startup params are tuned on the fly.
And I think that should stay that way.If you're happy with handling the existing connection parameters in a given
way, why would you not want application_name behaving that same way?
Well, in pgbouncer case, the parameters tracked via ParamStatus are
handled transparently. (client_encoding, datestyle, timezone,
standard_conforming_strings)
Any other parameter is handled via "ignore_startup_parameters" option:
if client supplies random option not appearing there, it is kicked out.
The point being that as pgbouncer cannot handle it transparently, the
admin needs to set the param in postgresql.conf if it is important,
fix the client or let pgbouncer ignore it if client is unfixable.
I have no problem handling appname with latter method, I just wanted
to clarify the target audience for the feature.
--
marko
Marko Kreen <markokr@gmail.com> writes:
On 12/1/09, Tom Lane <tgl@sss.pgh.pa.us> wrote:
If you're happy with handling the existing connection parameters in a given
way, why would you not want application_name behaving that same way?
Well, in pgbouncer case, the parameters tracked via ParamStatus are
handled transparently. (client_encoding, datestyle, timezone,
standard_conforming_strings)
Hmm, I had not thought about that. Is it sensible to mark
application_name as GUC_REPORT so that pgbouncer can be smart about it?
The actual overhead of such a thing would be probably be unmeasurable in
the normal case where it's only set via the startup packet, but it seems
a bit odd.
regards, tom lane
On 12/1/09, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Marko Kreen <markokr@gmail.com> writes:
On 12/1/09, Tom Lane <tgl@sss.pgh.pa.us> wrote:
If you're happy with handling the existing connection parameters in a given
way, why would you not want application_name behaving that same way?Well, in pgbouncer case, the parameters tracked via ParamStatus are
handled transparently. (client_encoding, datestyle, timezone,
standard_conforming_strings)Hmm, I had not thought about that. Is it sensible to mark
application_name as GUC_REPORT so that pgbouncer can be smart about it?
The actual overhead of such a thing would be probably be unmeasurable in
the normal case where it's only set via the startup packet, but it seems
a bit odd.
IMHO it is sensible, if we really want the option to follow client.
--
marko
Dave Page <dpage@pgadmin.org> writes:
On Tue, Dec 1, 2009 at 4:19 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
I don't think that we need to bump the protocol version. �The real
alternative here would be that libpq sends a startup packet that
includes application_name, and if it gets an error back from that,
it starts over without the app name.
I looked (briefly) at doing that when we first ran into this
suggestion. As you pointed out at the time, it seemed like that would
require some fairly ugly hackery in fe-connect.c
I've committed a change for this. It turns out not to be quite as ugly
as I thought, and in fact quite a bit less code than the other method.
The reason it's less intertwined with the other retry logic than I was
expecting is that the server only looks at the startup options after
it's completed the authentication process. So the failure retry for
this amounts to an outer loop around the SSL and protocol-version
retries. Logically anyway --- as far as the actual code goes it's
another path in the state machine, and just requires a few more lines.
I tested it with some simple cases such as password authentication,
but it would be good to confirm that it does the right thing in more
complex cases like SSL prefer/allow/require and Kerberos auth. Anyone
set up to try CVS HEAD against an older server with configurations
like that?
BTW, it strikes me that it would only be a matter of a couple of lines
to persuade older servers to ignore application_name in the startup
packet, instead of throwing a tantrum. Obviously we must make libpq
work against unpatched older servers, but if we can save a connection
cycle (and some bleating in the postmaster log) when talking to an 8.5
application, it might be worth doing:
*** src/backend/tcop/postgres.c.orig Thu Jun 18 06:08:08 2009
--- src/backend/tcop/postgres.c Wed Dec 2 00:05:05 2009
***************
*** 3159,3164 ****
--- 3159,3168 ----
value = lfirst(gucopts);
gucopts = lnext(gucopts);
+ /* Ignore application_name for compatibility with 8.5 libpq */
+ if (strcmp(name, "application_name") == 0)
+ continue;
+
if (IsSuperuserConfigOption(name))
PendingConfigOption(name, value);
else
If we patch the back branches like that, anyone who's annoyed by the
extra connection cycle just has to update to latest minor release
of their server to make it work more smoothly. Comments?
regards, tom lane
2009/12/2 Tom Lane <tgl@sss.pgh.pa.us>:
Dave Page <dpage@pgadmin.org> writes:
On Tue, Dec 1, 2009 at 4:19 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
I don't think that we need to bump the protocol version. The real
alternative here would be that libpq sends a startup packet that
includes application_name, and if it gets an error back from that,
it starts over without the app name.I looked (briefly) at doing that when we first ran into this
suggestion. As you pointed out at the time, it seemed like that would
require some fairly ugly hackery in fe-connect.cI've committed a change for this. It turns out not to be quite as ugly
as I thought, and in fact quite a bit less code than the other method.
The reason it's less intertwined with the other retry logic than I was
expecting is that the server only looks at the startup options after
it's completed the authentication process. So the failure retry for
this amounts to an outer loop around the SSL and protocol-version
retries. Logically anyway --- as far as the actual code goes it's
another path in the state machine, and just requires a few more lines.I tested it with some simple cases such as password authentication,
but it would be good to confirm that it does the right thing in more
complex cases like SSL prefer/allow/require and Kerberos auth. Anyone
set up to try CVS HEAD against an older server with configurations
like that?BTW, it strikes me that it would only be a matter of a couple of lines
to persuade older servers to ignore application_name in the startup
packet, instead of throwing a tantrum. Obviously we must make libpq
work against unpatched older servers, but if we can save a connection
cycle (and some bleating in the postmaster log) when talking to an 8.5
application, it might be worth doing:*** src/backend/tcop/postgres.c.orig Thu Jun 18 06:08:08 2009 --- src/backend/tcop/postgres.c Wed Dec 2 00:05:05 2009 *************** *** 3159,3164 **** --- 3159,3168 ---- value = lfirst(gucopts); gucopts = lnext(gucopts);+ /* Ignore application_name for compatibility with 8.5 libpq */ + if (strcmp(name, "application_name") == 0) + continue; + if (IsSuperuserConfigOption(name)) PendingConfigOption(name, value); elseIf we patch the back branches like that, anyone who's annoyed by the
extra connection cycle just has to update to latest minor release
of their server to make it work more smoothly. Comments?regards, tom lane
Given that this can probably be considered an *extremely* safe patch
:-), I say go for it. It'll certainly make for less error reports
around something that's not an error.
If the patch was in any way complex I'd object against it, but this
clearly isn't...
--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/
On Wed, Dec 2, 2009 at 8:14 AM, Magnus Hagander <magnus@hagander.net> wrote:
If we patch the back branches like that, anyone who's annoyed by the
extra connection cycle just has to update to latest minor release
of their server to make it work more smoothly. Comments?regards, tom lane
Given that this can probably be considered an *extremely* safe patch
:-), I say go for it. It'll certainly make for less error reports
around something that's not an error.If the patch was in any way complex I'd object against it, but this
clearly isn't...
Agreed.
--
Dave Page
EnterpriseDB UK: http://www.enterprisedb.com
Magnus Hagander <magnus@hagander.net> writes:
2009/12/2 Tom Lane <tgl@sss.pgh.pa.us>:
BTW, it strikes me that it would only be a matter of a couple of lines
to persuade older servers to ignore application_name in the startup
packet, instead of throwing a tantrum. �Obviously we must make libpq
work against unpatched older servers, but if we can save a connection
cycle (and some bleating in the postmaster log) when talking to an 8.5
application, it might be worth doing:
Given that this can probably be considered an *extremely* safe patch
:-), I say go for it. It'll certainly make for less error reports
around something that's not an error.
Yeah. I wouldn't even propose this, except that given the new code
an unpatched older server will log
FATAL: unrecognized configuration parameter "application_name"
anytime it gets a connection from newer libpq. I'm sure we'll get
some complaints/bugreports about it if we allow that to be the norm.
However, if we backpatch now, there will be relatively few situations
in the field where anyone tries to use 8.5 libpq against an unpatched
older server.
regards, tom lane