Application name patch - v3

Started by Dave Pageabout 16 years ago25 messages
#1Dave Page
dpage@pgadmin.org
1 attachment(s)

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.

Other features are as per the last version.

--
Dave Page
EnterpriseDB UK: http://www.enterprisedb.com
PGDay.EU 2009 Conference: http://2009.pgday.eu/start

Attachments:

appname-v3.diffapplication/octet-stream; name=appname-v3.diffDownload
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index a12a42b..c8a82a3 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..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 9e25da1..40d06fa 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
*** 1723,1728 ****
--- 1724,1739 ----
  		/* 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)
*** 2026,2033 ****
--- 2037,2049 ----
  			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 776efe3..5e4b546 100644
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
*************** char	   *pgstat_temp_directory;
*** 387,392 ****
--- 387,394 ----
  
  char	   *default_do_language;
  
+ char       *application_name;
+ 
  int			tcp_keepalives_idle;
  int			tcp_keepalives_interval;
  int			tcp_keepalives_count;
*************** static struct config_string ConfigureNam
*** 2551,2556 ****
--- 2553,2567 ----
  		"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
*************** InitializeGUCOptions(void)
*** 3309,3315 ****
  	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
--- 3320,3326 ----
  	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
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index d6cf7e2..d0024c3 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..ca2673d 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->setenv_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;
#2Andres Freund
andres@anarazel.de
In reply to: Dave Page (#1)
1 attachment(s)
Re: Application name patch - v3

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

Attachments:

0001-Dave-Page-Application-name-patch-v3.patchtext/x-patch; charset=UTF-8; name=0001-Dave-Page-Application-name-patch-v3.patchDownload
From 962861a38ea28c769bb28c18f3142f0339e97f5c Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@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

#3Dave Page
dpage@pgadmin.org
In reply to: Andres Freund (#2)
Re: Application name patch - v3

Hi Andres,

On Thu, Nov 12, 2009 at 11:32 PM, Andres Freund <andres@anarazel.de> wrote:

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?

Yup, well spotted.

The attached patch fixes this and also a couple occurances of trailing
whitespace.

Thanks.

What about pg_dump/psql setting fallback_application_name?

Per Tom, I'm waiting on the possible new array-based libpq connect API
which will make a conversion of those utilities from PQsetdbLogin a
lot cleaner than moving to PQconnectdb (and all the ugly connection
string building that would require).

--
Dave Page
EnterpriseDB UK: http://www.enterprisedb.com

#4Andres Freund
andres@anarazel.de
In reply to: Dave Page (#1)
Re: Application name patch - v3

Hi,

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.

Other features are as per the last version.

One more question: Per my reading of the discussion (which very well might be
flawed), wasnt the plan to limit the availale characters in the application
name to ascii?

Greetings,

Andres

#5Dave Page
dpage@pgadmin.org
In reply to: Andres Freund (#4)
Re: Application name patch - v3

On Wed, Nov 25, 2009 at 1:22 PM, Andres Freund <andres@anarazel.de> wrote:

Hi,

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.

Other features are as per the last version.

One more question: Per my reading of the discussion (which very well might be
flawed), wasnt the plan to limit the availale characters in the application
name to ascii?

That was suggested, but I thought the eventual outcome was to not bother.

--
Dave Page
EnterpriseDB UK: http://www.enterprisedb.com

#6Andres Freund
andres@anarazel.de
In reply to: Dave Page (#5)
Re: Application name patch - v3

On Wednesday 25 November 2009 14:28:14 Dave Page wrote:

On Wed, Nov 25, 2009 at 1:22 PM, Andres Freund <andres@anarazel.de> wrote:

Hi,

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.

Other features are as per the last version.

One more question: Per my reading of the discussion (which very well
might be flawed), wasnt the plan to limit the availale characters in the
application name to ascii?

That was suggested, but I thought the eventual outcome was to not bother.

Then I dont see any reason to delay any further (sorry!). I personally would
prefer making it a bit more strict but it surely is not imporant.

Markes as ready for comitter.

Andres

#7Tom Lane
tgl@sss.pgh.pa.us
In reply to: Dave Page (#5)
Re: Application name patch - v3

Dave Page <dpage@pgadmin.org> writes:

On Wed, Nov 25, 2009 at 1:22 PM, Andres Freund <andres@anarazel.de> wrote:

One more question: Per my reading of the discussion (which very well might be
flawed), wasnt the plan to limit the availale characters in the application
name to ascii?

That was suggested, but I thought the eventual outcome was to not bother.

I think that's really essential, not optional. The proposed patch will
transfer the application name from one backend to another without any
encoding conversion. If it contains non-ASCII characters that will
result in injection of badly-encoded data inside the backend, which is
something we have been trying hard to avoid in recent versions.

The only other thing you could do about this would be to try to convert
the data from the source backend's encoding to the target's. Which
would lead to assorted failure scenarios when no conversion is possible.

ISTM restricting the name to ASCII-only is the most reasonable tradeoff.
Of course, as a speaker of English I may be a bit biased here --- but
doing nothing about the issue doesn't seem acceptable.

regards, tom lane

#8Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#7)
Re: Application name patch - v3

On Wednesday 25 November 2009 23:01:35 Tom Lane wrote:

Dave Page <dpage@pgadmin.org> writes:

On Wed, Nov 25, 2009 at 1:22 PM, Andres Freund <andres@anarazel.de> wrote:

One more question: Per my reading of the discussion (which very well
might be flawed), wasnt the plan to limit the availale characters in the
application name to ascii?

That was suggested, but I thought the eventual outcome was to not bother.

I think that's really essential, not optional. The proposed patch will
transfer the application name from one backend to another without any
encoding conversion. If it contains non-ASCII characters that will
result in injection of badly-encoded data inside the backend, which is
something we have been trying hard to avoid in recent versions.

Isn't that similarly the case with pg_stat_activity?

ISTM restricting the name to ASCII-only is the most reasonable tradeoff.
Of course, as a speaker of English I may be a bit biased here --- but
doing nothing about the issue doesn't seem acceptable.

I actually having a hard time imaging a use case where this would be a real
problem...

I have to admit though that while I am not from a English speaking country but
from Germany the amount of non ASCII chars used there routinely is not that
big, so ...

Andres

#9Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#8)
Re: Application name patch - v3

Andres Freund <andres@anarazel.de> writes:

On Wednesday 25 November 2009 23:01:35 Tom Lane wrote:

I think that's really essential, not optional. The proposed patch will
transfer the application name from one backend to another without any
encoding conversion. If it contains non-ASCII characters that will
result in injection of badly-encoded data inside the backend, which is
something we have been trying hard to avoid in recent versions.

Isn't that similarly the case with pg_stat_activity?

Well, we do still have some un-plugged holes there, but that's not an
excuse for adding more.

regards, tom lane

#10Dave Page
dpage@pgadmin.org
In reply to: Tom Lane (#7)
Re: Application name patch - v3

On Wed, Nov 25, 2009 at 10:01 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

ISTM restricting the name to ASCII-only is the most reasonable tradeoff.
Of course, as a speaker of English I may be a bit biased here --- but
doing nothing about the issue doesn't seem acceptable.

OK - something like this? Should keep non-printable/control characters
out of logs too...

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;
}

--
Dave Page
EnterpriseDB UK: http://www.enterprisedb.com

#11Tom Lane
tgl@sss.pgh.pa.us
In reply to: Dave Page (#10)
Re: Application name patch - v3

Dave Page <dpage@pgadmin.org> writes:

OK - something like this? Should keep non-printable/control characters
out of logs too...

Personally I'd use guc_strdup and then modify the string in-place,
but that's just a matter of taste I guess. Otherwise it seems
reasonable.

regards, tom lane

#12Guillaume Lelarge
guillaume@lelarge.info
In reply to: Dave Page (#3)
Re: Application name patch - v3

Le 13/11/2009 12:11, Dave Page a �crit :

[...]

What about pg_dump/psql setting fallback_application_name?

Per Tom, I'm waiting on the possible new array-based libpq connect API
which will make a conversion of those utilities from PQsetdbLogin a
lot cleaner than moving to PQconnectdb (and all the ugly connection
string building that would require).

Is it still to be done? I don't see psql pr pg_dump set an application
name on alpha 3. There are also pg_restore, vacuumdb, reindexdb, etc.

--
Guillaume.
http://www.postgresqlfr.org
http://dalibo.com

#13Dave Page
dpage@pgadmin.org
In reply to: Guillaume Lelarge (#12)
Re: Application name patch - v3

On Sun, Dec 27, 2009 at 11:15 PM, Guillaume Lelarge
<guillaume@lelarge.info> wrote:

Le 13/11/2009 12:11, Dave Page a écrit :

[...]

What about pg_dump/psql setting fallback_application_name?

Per Tom, I'm waiting on the possible new array-based libpq connect API
which will make a conversion of those utilities from PQsetdbLogin a
lot cleaner than moving to PQconnectdb (and all the ugly connection
string building that would require).

Is it still to be done? I don't see psql pr pg_dump set an application
name on alpha 3. There are also pg_restore, vacuumdb, reindexdb, etc.

Yes, still waiting on the new API.

--
Dave Page
EnterpriseDB UK: http://www.enterprisedb.com

#14Guillaume Lelarge
guillaume@lelarge.info
In reply to: Dave Page (#13)
Re: Application name patch - v3

Le 28/12/2009 10:07, Dave Page a �crit :

On Sun, Dec 27, 2009 at 11:15 PM, Guillaume Lelarge
<guillaume@lelarge.info> wrote:

Le 13/11/2009 12:11, Dave Page a �crit :

[...]

What about pg_dump/psql setting fallback_application_name?

Per Tom, I'm waiting on the possible new array-based libpq connect API
which will make a conversion of those utilities from PQsetdbLogin a
lot cleaner than moving to PQconnectdb (and all the ugly connection
string building that would require).

Is it still to be done? I don't see psql pr pg_dump set an application
name on alpha 3. There are also pg_restore, vacuumdb, reindexdb, etc.

Yes, still waiting on the new API.

Is there something I can do to make this move forward?

--
Guillaume.
http://www.postgresqlfr.org
http://dalibo.com

#15Tom Lane
tgl@sss.pgh.pa.us
In reply to: Guillaume Lelarge (#14)
Re: Application name patch - v3

Guillaume Lelarge <guillaume@lelarge.info> writes:

Le 28/12/2009 10:07, Dave Page a �crit :

Yes, still waiting on the new API.

Is there something I can do to make this move forward?

I think we were stalled on the question of whether to use one array
or two parallel arrays. Do you want to try coding up a sample usage
of each possibility so we can see which one seems more useful?

regards, tom lane

#16Guillaume Lelarge
guillaume@lelarge.info
In reply to: Tom Lane (#15)
Re: Application name patch - v3

Le 28/12/2009 17:06, Tom Lane a �crit :

Guillaume Lelarge <guillaume@lelarge.info> writes:

Le 28/12/2009 10:07, Dave Page a �crit :

Yes, still waiting on the new API.

Is there something I can do to make this move forward?

I think we were stalled on the question of whether to use one array
or two parallel arrays. Do you want to try coding up a sample usage
of each possibility so we can see which one seems more useful?

I'm interested in working on this. But I don't find the thread that talk
about this. I feel pretty dumb, but I re-read every mail on "Application
name patch - v2", "Application name patch - v3", and "Application name
patch - v4" threads. I also re-read the "Client application name"
thread. The only mail I see that relates to the new API is the one from
Dave (the one I answered today).

So, can someone point me to the thread that deals with this "new
array-based libpq connect API"? or can someone explain it to me?

Thanks.

--
Guillaume.
http://www.postgresqlfr.org
http://dalibo.com

#17Tom Lane
tgl@sss.pgh.pa.us
In reply to: Guillaume Lelarge (#16)
Re: Application name patch - v3

Guillaume Lelarge <guillaume@lelarge.info> writes:

Le 28/12/2009 17:06, Tom Lane a �crit :

I think we were stalled on the question of whether to use one array
or two parallel arrays. Do you want to try coding up a sample usage
of each possibility so we can see which one seems more useful?

I'm interested in working on this. But I don't find the thread that talk
about this.

Try here
http://archives.postgresql.org/message-id/4AAE8CCF.9070808@esilo.com

regards, tom lane

#18Guillaume Lelarge
guillaume@lelarge.info
In reply to: Tom Lane (#17)
Re: Application name patch - v3

Le 28/12/2009 22:59, Tom Lane a �crit :

Guillaume Lelarge <guillaume@lelarge.info> writes:

Le 28/12/2009 17:06, Tom Lane a �crit :

I think we were stalled on the question of whether to use one array
or two parallel arrays. Do you want to try coding up a sample usage
of each possibility so we can see which one seems more useful?

I'm interested in working on this. But I don't find the thread that talk
about this.

Try here
http://archives.postgresql.org/message-id/4AAE8CCF.9070808@esilo.com

Thanks. I've read all the "new version of PQconnectdb" and "Determining
client_encoding from client locale" threads. I think I understand the
goal. Still need to re-read this one
(http://archives.postgresql.org/message-id/6222.1253734019@sss.pgh.pa.us) and
completely understand it (will probably need to look at the code, at
least the PQconnectdb one). But I'm definitely working on this.

--
Guillaume.
http://www.postgresqlfr.org
http://dalibo.com

#19Guillaume Lelarge
guillaume@lelarge.info
In reply to: Guillaume Lelarge (#18)
Re: Application name patch - v3

Le 29/12/2009 00:03, Guillaume Lelarge a �crit :

Le 28/12/2009 22:59, Tom Lane a �crit :

Guillaume Lelarge <guillaume@lelarge.info> writes:

Le 28/12/2009 17:06, Tom Lane a �crit :

I think we were stalled on the question of whether to use one array
or two parallel arrays. Do you want to try coding up a sample usage
of each possibility so we can see which one seems more useful?

I'm interested in working on this. But I don't find the thread that talk
about this.

Try here
http://archives.postgresql.org/message-id/4AAE8CCF.9070808@esilo.com

Thanks. I've read all the "new version of PQconnectdb" and "Determining
client_encoding from client locale" threads. I think I understand the
goal. Still need to re-read this one
(http://archives.postgresql.org/message-id/6222.1253734019@sss.pgh.pa.us) and
completely understand it (will probably need to look at the code, at
least the PQconnectdb one). But I'm definitely working on this.

If I try to sum up my readings so far, this is what we still have to do:

1. try the one-array approach
PGconn *PQconnectParams(const char **params)

2. try the two-arrays approach
PGconn *PQconnectParams(const char **keywords, const char **values)

Instead of doing a wrapper around PQconnectdb, we need to refactor the
whole function, so that we can get rid of the parsing of the conninfo
string (which is quite complicated).

Using psql as an example would be a good idea, AFAICT.

Am I right? did I misunderstand or forget something?

--
Guillaume.
http://www.postgresqlfr.org
http://dalibo.com

#20Guillaume Lelarge
guillaume@lelarge.info
In reply to: Guillaume Lelarge (#19)
2 attachment(s)
Re: Application name patch - v3

Le 29/12/2009 14:12, Guillaume Lelarge a �crit :

Le 29/12/2009 00:03, Guillaume Lelarge a �crit :

Le 28/12/2009 22:59, Tom Lane a �crit :

Guillaume Lelarge <guillaume@lelarge.info> writes:

Le 28/12/2009 17:06, Tom Lane a �crit :

I think we were stalled on the question of whether to use one array
or two parallel arrays. Do you want to try coding up a sample usage
of each possibility so we can see which one seems more useful?

I'm interested in working on this. But I don't find the thread that talk
about this.

Try here
http://archives.postgresql.org/message-id/4AAE8CCF.9070808@esilo.com

Thanks. I've read all the "new version of PQconnectdb" and "Determining
client_encoding from client locale" threads. I think I understand the
goal. Still need to re-read this one
(http://archives.postgresql.org/message-id/6222.1253734019@sss.pgh.pa.us) and
completely understand it (will probably need to look at the code, at
least the PQconnectdb one). But I'm definitely working on this.

If I try to sum up my readings so far, this is what we still have to do:

1. try the one-array approach
PGconn *PQconnectParams(const char **params)

2. try the two-arrays approach
PGconn *PQconnectParams(const char **keywords, const char **values)

Instead of doing a wrapper around PQconnectdb, we need to refactor the
whole function, so that we can get rid of the parsing of the conninfo
string (which is quite complicated).

Using psql as an example would be a good idea, AFAICT.

Am I right? did I misunderstand or forget something?

I supposed I was right since noone yell at me :)

I worked on this tonight. You'll find two patches attached, one for the
one-array approach, one for the two-arrays approach. I know some more
factoring can be done (at least, the "get the fallback resources..."
part). I'm OK to do them. I just need to know if I'm on the right track.

--
Guillaume.
http://www.postgresqlfr.org
http://dalibo.com

Attachments:

libpqParams1.difftext/x-patch; name=libpqParams1.diffDownload
Index: src/bin/psql/startup.c
===================================================================
RCS file: /opt/cvsroot_postgresql/pgsql/src/bin/psql/startup.c,v
retrieving revision 1.158
diff -c -p -c -r1.158 startup.c
*** src/bin/psql/startup.c	2 Jan 2010 16:57:59 -0000	1.158
--- src/bin/psql/startup.c	4 Jan 2010 21:04:13 -0000
*************** main(int argc, char *argv[])
*** 171,181 ****
  	/* loop until we have a password if requested by backend */
  	do
  	{
! 		new_pass = false;
! 		pset.db = PQsetdbLogin(options.host, options.port, NULL, NULL,
! 					options.action == ACT_LIST_DB && options.dbname == NULL ?
! 							   "postgres" : options.dbname,
! 							   options.username, password);
  
  		if (PQstatus(pset.db) == CONNECTION_BAD &&
  			PQconnectionNeedsPassword(pset.db) &&
--- 171,190 ----
  	/* loop until we have a password if requested by backend */
  	do
  	{
!         const char *params[] = {
!                   "host", options.host,
!                   "port", options.port,
!                   "dbname", (options.action == ACT_LIST_DB && 
!                                options.dbname == NULL) ? "postgres" : options.dbname,
!                   "user", options.username,
!                   "password", password,
!                   "application_name", pset.progname,
!                   NULL, NULL
!               };
!         
!         new_pass = false;
! 
!         pset.db = PQconnectdbParams(params);
  
  		if (PQstatus(pset.db) == CONNECTION_BAD &&
  			PQconnectionNeedsPassword(pset.db) &&
Index: src/interfaces/libpq/exports.txt
===================================================================
RCS file: /opt/cvsroot_postgresql/pgsql/src/interfaces/libpq/exports.txt,v
retrieving revision 1.23
diff -c -p -c -r1.23 exports.txt
*** src/interfaces/libpq/exports.txt	31 Mar 2009 01:41:27 -0000	1.23
--- src/interfaces/libpq/exports.txt	4 Jan 2010 20:51:13 -0000
*************** PQresultSetInstanceData   150
*** 153,155 ****
--- 153,157 ----
  PQfireResultCreateEvents  151
  PQconninfoParse           152
  PQinitOpenSSL             153
+ PQconnectdbParams         154
+ PQconnectStartParams      155
Index: src/interfaces/libpq/fe-connect.c
===================================================================
RCS file: /opt/cvsroot_postgresql/pgsql/src/interfaces/libpq/fe-connect.c,v
retrieving revision 1.382
diff -c -p -c -r1.382 fe-connect.c
*** src/interfaces/libpq/fe-connect.c	2 Jan 2010 16:58:11 -0000	1.382
--- src/interfaces/libpq/fe-connect.c	4 Jan 2010 20:54:12 -0000
*************** static bool connectOptions2(PGconn *conn
*** 259,264 ****
--- 259,265 ----
  static int	connectDBStart(PGconn *conn);
  static int	connectDBComplete(PGconn *conn);
  static PGconn *makeEmptyPGconn(void);
+ static void fillPGconn(PGconn *conn, PQconninfoOption *connOptions);
  static void freePGconn(PGconn *conn);
  static void closePGconn(PGconn *conn);
  static PQconninfoOption *conninfo_parse(const char *conninfo,
*************** pgthreadlock_t pg_g_threadlock = default
*** 299,304 ****
--- 300,337 ----
   */
  
  /*
+  *		PQconnectdbParams
+  *
+  * establishes a connection to a postgres backend through the postmaster
+  * using connection information in a struct.
+  *
+  * The params struct string is defined as
+  *
+  *	   const char *params[] = {option1, value1, option2, value2, NULL}
+  *
+  * definitions.
+  *
+  * Returns a PGconn* which is needed for all subsequent libpq calls, or NULL
+  * if a memory allocation failed.
+  * If the status field of the connection returned is CONNECTION_BAD,
+  * then some fields may be null'ed out instead of having valid values.
+  *
+  * You should call PQfinish (if conn is not NULL) regardless of whether this
+  * call succeeded.
+  */
+ PGconn *
+ PQconnectdbParams(const char * const *params)
+ {
+ 	PGconn	   *conn = PQconnectStartParams(params);
+ 
+ 	if (conn && conn->status != CONNECTION_BAD)
+ 		(void) connectDBComplete(conn);
+ 
+ 	return conn;
+ 
+ }
+ 
+ /*
   *		PQconnectdb
   *
   * establishes a connection to a postgres backend through the postmaster
*************** PQconnectdb(const char *conninfo)
*** 332,343 ****
  }
  
  /*
!  *		PQconnectStart
   *
   * Begins the establishment of a connection to a postgres backend through the
!  * postmaster using connection information in a string.
   *
!  * See comment for PQconnectdb for the definition of the string format.
   *
   * Returns a PGconn*.  If NULL is returned, a malloc error has occurred, and
   * you should not attempt to proceed with this connection.	If the status
--- 365,376 ----
  }
  
  /*
!  *		PQconnectStartParams
   *
   * Begins the establishment of a connection to a postgres backend through the
!  * postmaster using connection information in a struct.
   *
!  * See comment for PQconnectdbParams for the definition of the string format.
   *
   * Returns a PGconn*.  If NULL is returned, a malloc error has occurred, and
   * you should not attempt to proceed with this connection.	If the status
*************** PQconnectdb(const char *conninfo)
*** 351,359 ****
   * See PQconnectPoll for more info.
   */
  PGconn *
! PQconnectStart(const char *conninfo)
  {
! 	PGconn	   *conn;
  
  	/*
  	 * Allocate memory for the conn structure
--- 384,395 ----
   * See PQconnectPoll for more info.
   */
  PGconn *
! PQconnectStartParams(const char * const *params)
  {
!     PGconn *conn;
! 	PQconninfoOption *options;
! 	PQconninfoOption *option;
! 	char	   *tmp;
  
  	/*
  	 * Allocate memory for the conn structure
*************** PQconnectStart(const char *conninfo)
*** 363,372 ****
  		return NULL;
  
  	/*
! 	 * Parse the conninfo string
  	 */
! 	if (!connectOptions1(conn, conninfo))
! 		return conn;
  
  	/*
  	 * Compute derived options
--- 399,546 ----
  		return NULL;
  
  	/*
!      * Make a working copy of PQconninfoOptions
!      */
! 	options = malloc(sizeof(PQconninfoOptions));
! 	if (options == NULL)
! 	{
! 		printfPQExpBuffer(&conn->errorMessage,
! 						  libpq_gettext("out of memory\n"));
! 		return NULL;
! 	}
! 	memcpy(options, PQconninfoOptions, sizeof(PQconninfoOptions));
! 
! 	/*
! 	 * Parse the params struct
  	 */
!     while(*params)
!     {
!         const char *pname = params[0];
!         const char *pvalue  = params[1];
!  
!         if (pvalue != NULL)
!         {
! 		    /*
! 		     * Now we have the name and the value. Search for the param record.
! 		     */
! 		    for (option = options; option->keyword != NULL; option++)
! 		    {
! 		    	if (strcmp(option->keyword, pname) == 0)
! 		    		break;
! 		    }
! 
!             /*
!              * Check for invalid connection option
!              */
! 		    if (option->keyword == NULL)
! 		    {
! 		    	printfPQExpBuffer(&conn->errorMessage,
! 		    				 libpq_gettext("invalid connection option \"%s\"\n"),
! 		    					  pname);
! 		    	PQconninfoFree(options);
! 		    	return NULL;
! 		    }
! 
! 		    /*
! 		     * Store the value
! 		     */
! 		    if (option->val)
! 		    	free(option->val);
! 		    option->val = strdup(pvalue);
! 		    if (!option->val)
! 		    {
! 		    	printfPQExpBuffer(&conn->errorMessage,
! 		    					  libpq_gettext("out of memory\n"));
! 		    	PQconninfoFree(options);
! 		    	return NULL;
! 		    }
!         }
! 
!         params+=2;
! 	}
! 
! 	/*
! 	 * If there's a service spec, use it to obtain any not-explicitly-given
! 	 * parameters.
! 	 */
! 	if (parseServiceInfo(options, &conn->errorMessage))
! 	{
! 		PQconninfoFree(options);
! 		return NULL;
! 	}
! 
! 	/*
! 	 * Get the fallback resources for parameters not specified in the conninfo
! 	 * string nor the service.
! 	 */
! 	for (option = options; option->keyword != NULL; option++)
! 	{
! 		if (option->val != NULL)
! 			continue;			/* Value was in conninfo or service */
! 
! 		/*
! 		 * Try to get the environment variable fallback
! 		 */
! 		if (option->envvar != NULL)
! 		{
! 			if ((tmp = getenv(option->envvar)) != NULL)
! 			{
! 				option->val = strdup(tmp);
! 				if (!option->val)
! 				{
! 					printfPQExpBuffer(&conn->errorMessage,
! 									  libpq_gettext("out of memory\n"));
! 					PQconninfoFree(options);
! 					return NULL;
! 				}
! 				continue;
! 			}
! 		}
! 
! 		/*
! 		 * No environment variable specified or this one isn't set - try
! 		 * compiled in
! 		 */
! 		if (option->compiled != NULL)
! 		{
! 			option->val = strdup(option->compiled);
! 			if (!option->val)
! 			{
! 				printfPQExpBuffer(&conn->errorMessage,
! 								  libpq_gettext("out of memory\n"));
! 				PQconninfoFree(options);
! 				return NULL;
! 			}
! 			continue;
! 		}
! 
! 		/*
! 		 * Special handling for user
! 		 */
! 		if (strcmp(option->keyword, "user") == 0)
! 		{
! 			option->val = pg_fe_getauthname(&conn->errorMessage);
! 			continue;
! 		}
! 	}
! 
! 	if (options == NULL)
! 	{
! 		conn->status = CONNECTION_BAD;
! 		/* errorMessage is already set */
! 		return false;
! 	}
! 
! 	/*
! 	 * Move option values into conn structure
! 	 */
!     fillPGconn(conn, options);
! 
! 
! 	/*
! 	 * Free the option info - all is in conn now
! 	 */
! 	PQconninfoFree(options);
  
  	/*
  	 * Compute derived options
*************** PQconnectStart(const char *conninfo)
*** 383,419 ****
  		conn->status = CONNECTION_BAD;
  	}
  
! 	return conn;
  }
  
  /*
!  *		connectOptions1
   *
!  * Internal subroutine to set up connection parameters given an already-
!  * created PGconn and a conninfo string.  Derived settings should be
!  * processed by calling connectOptions2 next.  (We split them because
!  * PQsetdbLogin overrides defaults in between.)
   *
!  * Returns true if OK, false if trouble (in which case errorMessage is set
!  * and so is conn->status).
   */
! static bool
! connectOptions1(PGconn *conn, const char *conninfo)
  {
! 	PQconninfoOption *connOptions;
! 	char	   *tmp;
  
  	/*
  	 * Parse the conninfo string
  	 */
! 	connOptions = conninfo_parse(conninfo, &conn->errorMessage, true);
! 	if (connOptions == NULL)
  	{
  		conn->status = CONNECTION_BAD;
- 		/* errorMessage is already set */
- 		return false;
  	}
  
  	/*
  	 * Move option values into conn structure
  	 *
--- 557,625 ----
  		conn->status = CONNECTION_BAD;
  	}
  
!     return conn;
  }
  
  /*
!  *		PQconnectStart
   *
!  * Begins the establishment of a connection to a postgres backend through the
!  * postmaster using connection information in a string.
   *
!  * See comment for PQconnectdb for the definition of the string format.
!  *
!  * Returns a PGconn*.  If NULL is returned, a malloc error has occurred, and
!  * you should not attempt to proceed with this connection.	If the status
!  * field of the connection returned is CONNECTION_BAD, an error has
!  * occurred. In this case you should call PQfinish on the result, (perhaps
!  * inspecting the error message first).  Other fields of the structure may not
!  * be valid if that occurs.  If the status field is not CONNECTION_BAD, then
!  * this stage has succeeded - call PQconnectPoll, using select(2) to see when
!  * this is necessary.
!  *
!  * See PQconnectPoll for more info.
   */
! PGconn *
! PQconnectStart(const char *conninfo)
  {
! 	PGconn	   *conn;
! 
! 	/*
! 	 * Allocate memory for the conn structure
! 	 */
! 	conn = makeEmptyPGconn();
! 	if (conn == NULL)
! 		return NULL;
  
  	/*
  	 * Parse the conninfo string
  	 */
! 	if (!connectOptions1(conn, conninfo))
! 		return conn;
! 
! 	/*
! 	 * Compute derived options
! 	 */
! 	if (!connectOptions2(conn))
! 		return conn;
! 
! 	/*
! 	 * Connect to the database
! 	 */
! 	if (!connectDBStart(conn))
  	{
+ 		/* Just in case we failed to set it in connectDBStart */
  		conn->status = CONNECTION_BAD;
  	}
  
+ 	return conn;
+ }
+ 
+ static void
+ fillPGconn(PGconn *conn, PQconninfoOption *connOptions)
+ {
+ 	char	   *tmp;
+ 
  	/*
  	 * Move option values into conn structure
  	 *
*************** connectOptions1(PGconn *conn, const char
*** 472,477 ****
--- 678,716 ----
  	tmp = conninfo_getval(connOptions, "gsslib");
  	conn->gsslib = tmp ? strdup(tmp) : NULL;
  #endif
+ }
+ 
+ /*
+  *		connectOptions1
+  *
+  * Internal subroutine to set up connection parameters given an already-
+  * created PGconn and a conninfo string.  Derived settings should be
+  * processed by calling connectOptions2 next.  (We split them because
+  * PQsetdbLogin overrides defaults in between.)
+  *
+  * Returns true if OK, false if trouble (in which case errorMessage is set
+  * and so is conn->status).
+  */
+ static bool
+ connectOptions1(PGconn *conn, const char *conninfo)
+ {
+ 	PQconninfoOption *connOptions;
+ 
+ 	/*
+ 	 * Parse the conninfo string
+ 	 */
+ 	connOptions = conninfo_parse(conninfo, &conn->errorMessage, true);
+ 	if (connOptions == NULL)
+ 	{
+ 		conn->status = CONNECTION_BAD;
+ 		/* errorMessage is already set */
+ 		return false;
+ 	}
+ 
+ 	/*
+ 	 * Move option values into conn structure
+ 	 */
+     fillPGconn(conn, connOptions);
  
  	/*
  	 * Free the option info - all is in conn now
Index: src/interfaces/libpq/libpq-fe.h
===================================================================
RCS file: /opt/cvsroot_postgresql/pgsql/src/interfaces/libpq/libpq-fe.h,v
retrieving revision 1.148
diff -c -p -c -r1.148 libpq-fe.h
*** src/interfaces/libpq/libpq-fe.h	2 Jan 2010 16:58:12 -0000	1.148
--- src/interfaces/libpq/libpq-fe.h	4 Jan 2010 20:51:53 -0000
*************** typedef struct pgresAttDesc
*** 226,235 ****
--- 226,237 ----
  /* make a new client connection to the backend */
  /* Asynchronous (non-blocking) */
  extern PGconn *PQconnectStart(const char *conninfo);
+ extern PGconn *PQconnectStartParams(const char * const *params);
  extern PostgresPollingStatusType PQconnectPoll(PGconn *conn);
  
  /* Synchronous (blocking) */
  extern PGconn *PQconnectdb(const char *conninfo);
+ extern PGconn *PQconnectdbParams(const char * const *params);
  extern PGconn *PQsetdbLogin(const char *pghost, const char *pgport,
  			 const char *pgoptions, const char *pgtty,
  			 const char *dbName,
libpqParams2.difftext/x-patch; name=libpqParams2.diffDownload
Index: src/bin/psql/startup.c
===================================================================
RCS file: /opt/cvsroot_postgresql/pgsql/src/bin/psql/startup.c,v
retrieving revision 1.158
diff -c -p -c -r1.158 startup.c
*** src/bin/psql/startup.c	2 Jan 2010 16:57:59 -0000	1.158
--- src/bin/psql/startup.c	4 Jan 2010 21:27:12 -0000
*************** main(int argc, char *argv[])
*** 168,181 ****
  	if (pset.getPassword == TRI_YES)
  		password = simple_prompt(password_prompt, 100, false);
  
  	/* loop until we have a password if requested by backend */
  	do
  	{
! 		new_pass = false;
! 		pset.db = PQsetdbLogin(options.host, options.port, NULL, NULL,
! 					options.action == ACT_LIST_DB && options.dbname == NULL ?
! 							   "postgres" : options.dbname,
! 							   options.username, password);
  
  		if (PQstatus(pset.db) == CONNECTION_BAD &&
  			PQconnectionNeedsPassword(pset.db) &&
--- 168,200 ----
  	if (pset.getPassword == TRI_YES)
  		password = simple_prompt(password_prompt, 100, false);
  
+     const char *keywords[] = {
+         "host",
+         "port",
+         "dbname",
+         "user",
+         "password",
+         "application_name",
+         NULL
+         };
+         
  	/* loop until we have a password if requested by backend */
  	do
  	{
!         const char *values[] = {
!                   options.host,
!                   options.port,
!                   (options.action == ACT_LIST_DB && 
!                                options.dbname == NULL) ? "postgres" : options.dbname,
!                   options.username,
!                   password,
!                   pset.progname,
!                   NULL
!               };
!         
!         new_pass = false;
! 
!         pset.db = PQconnectdbParams(keywords, values);
  
  		if (PQstatus(pset.db) == CONNECTION_BAD &&
  			PQconnectionNeedsPassword(pset.db) &&
Index: src/interfaces/libpq/exports.txt
===================================================================
RCS file: /opt/cvsroot_postgresql/pgsql/src/interfaces/libpq/exports.txt,v
retrieving revision 1.23
diff -c -p -c -r1.23 exports.txt
*** src/interfaces/libpq/exports.txt	31 Mar 2009 01:41:27 -0000	1.23
--- src/interfaces/libpq/exports.txt	4 Jan 2010 21:07:49 -0000
*************** PQresultSetInstanceData   150
*** 153,155 ****
--- 153,157 ----
  PQfireResultCreateEvents  151
  PQconninfoParse           152
  PQinitOpenSSL             153
+ PQconnectdbParams         154
+ PQconnectStartParams      155
Index: src/interfaces/libpq/fe-connect.c
===================================================================
RCS file: /opt/cvsroot_postgresql/pgsql/src/interfaces/libpq/fe-connect.c,v
retrieving revision 1.382
diff -c -p -c -r1.382 fe-connect.c
*** src/interfaces/libpq/fe-connect.c	2 Jan 2010 16:58:11 -0000	1.382
--- src/interfaces/libpq/fe-connect.c	4 Jan 2010 21:18:52 -0000
*************** static bool connectOptions2(PGconn *conn
*** 259,264 ****
--- 259,265 ----
  static int	connectDBStart(PGconn *conn);
  static int	connectDBComplete(PGconn *conn);
  static PGconn *makeEmptyPGconn(void);
+ static void fillPGconn(PGconn *conn, PQconninfoOption *connOptions);
  static void freePGconn(PGconn *conn);
  static void closePGconn(PGconn *conn);
  static PQconninfoOption *conninfo_parse(const char *conninfo,
*************** pgthreadlock_t pg_g_threadlock = default
*** 299,304 ****
--- 300,339 ----
   */
  
  /*
+  *		PQconnectdbParams
+  *
+  * establishes a connection to a postgres backend through the postmaster
+  * using connection information in two structs.
+  *
+  * The keywords struct is defined as
+  *
+  *	   const char *params[] = {option1, option2, NULL}
+  *
+  * The values struct is defined as
+  *
+  *	   const char *params[] = {option1, option2, NULL}
+  *
+  * Returns a PGconn* which is needed for all subsequent libpq calls, or NULL
+  * if a memory allocation failed.
+  * If the status field of the connection returned is CONNECTION_BAD,
+  * then some fields may be null'ed out instead of having valid values.
+  *
+  * You should call PQfinish (if conn is not NULL) regardless of whether this
+  * call succeeded.
+  */
+ PGconn *
+ PQconnectdbParams(const char **keywords, const char **values)
+ {
+ 	PGconn	   *conn = PQconnectStartParams(keywords, values);
+ 
+ 	if (conn && conn->status != CONNECTION_BAD)
+ 		(void) connectDBComplete(conn);
+ 
+ 	return conn;
+ 
+ }
+ 
+ /*
   *		PQconnectdb
   *
   * establishes a connection to a postgres backend through the postmaster
*************** PQconnectdb(const char *conninfo)
*** 332,343 ****
  }
  
  /*
!  *		PQconnectStart
   *
   * Begins the establishment of a connection to a postgres backend through the
!  * postmaster using connection information in a string.
   *
!  * See comment for PQconnectdb for the definition of the string format.
   *
   * Returns a PGconn*.  If NULL is returned, a malloc error has occurred, and
   * you should not attempt to proceed with this connection.	If the status
--- 367,378 ----
  }
  
  /*
!  *		PQconnectStartParams
   *
   * Begins the establishment of a connection to a postgres backend through the
!  * postmaster using connection information in a struct.
   *
!  * See comment for PQconnectdbParams for the definition of the string format.
   *
   * Returns a PGconn*.  If NULL is returned, a malloc error has occurred, and
   * you should not attempt to proceed with this connection.	If the status
*************** PQconnectdb(const char *conninfo)
*** 351,359 ****
   * See PQconnectPoll for more info.
   */
  PGconn *
! PQconnectStart(const char *conninfo)
  {
! 	PGconn	   *conn;
  
  	/*
  	 * Allocate memory for the conn structure
--- 386,397 ----
   * See PQconnectPoll for more info.
   */
  PGconn *
! PQconnectStartParams(const char **keywords, const char **values)
  {
!     PGconn *conn;
! 	PQconninfoOption *options;
! 	PQconninfoOption *option;
! 	char	   *tmp;
  
  	/*
  	 * Allocate memory for the conn structure
*************** PQconnectStart(const char *conninfo)
*** 363,372 ****
  		return NULL;
  
  	/*
! 	 * Parse the conninfo string
  	 */
! 	if (!connectOptions1(conn, conninfo))
! 		return conn;
  
  	/*
  	 * Compute derived options
--- 401,549 ----
  		return NULL;
  
  	/*
!      * Make a working copy of PQconninfoOptions
!      */
! 	options = malloc(sizeof(PQconninfoOptions));
! 	if (options == NULL)
! 	{
! 		printfPQExpBuffer(&conn->errorMessage,
! 						  libpq_gettext("out of memory\n"));
! 		return NULL;
! 	}
! 	memcpy(options, PQconninfoOptions, sizeof(PQconninfoOptions));
! 
! 	/*
! 	 * Parse the params struct
  	 */
!     while(*keywords)
!     {
!         const char *pname = keywords[0];
!         const char *pvalue  = values[0];
!  
!         if (pvalue != NULL)
!         {
! 		    /*
! 		     * Now we have the name and the value. Search for the param record.
! 		     */
! 		    for (option = options; option->keyword != NULL; option++)
! 		    {
! 		    	if (strcmp(option->keyword, pname) == 0)
! 		    		break;
! 		    }
! 
!             /*
!              * Check for invalid connection option
!              */
! 		    if (option->keyword == NULL)
! 		    {
! 		    	printfPQExpBuffer(&conn->errorMessage,
! 		    				 libpq_gettext("invalid connection option \"%s\"\n"),
! 		    					  pname);
! 		    	PQconninfoFree(options);
! 		    	return NULL;
! 		    }
! 
! 		    /*
! 		     * Store the value
! 		     */
! 		    if (option->val)
! 		    	free(option->val);
! 		    option->val = strdup(pvalue);
! 		    if (!option->val)
! 		    {
! 		    	printfPQExpBuffer(&conn->errorMessage,
! 		    					  libpq_gettext("out of memory\n"));
! 		    	PQconninfoFree(options);
! 		    	return NULL;
! 		    }
!         }
! 
!         keywords+=1;
!         values+=1;
! 	}
! 
! 	/*
! 	 * If there's a service spec, use it to obtain any not-explicitly-given
! 	 * parameters.
! 	 */
! 	if (parseServiceInfo(options, &conn->errorMessage))
! 	{
! 		PQconninfoFree(options);
! 		return NULL;
! 	}
! 
! 	/*
! 	 * Get the fallback resources for parameters not specified in the conninfo
! 	 * string nor the service.
! 	 */
! 	for (option = options; option->keyword != NULL; option++)
! 	{
! 		if (option->val != NULL)
! 			continue;			/* Value was in conninfo or service */
! 
! 		/*
! 		 * Try to get the environment variable fallback
! 		 */
! 		if (option->envvar != NULL)
! 		{
! 			if ((tmp = getenv(option->envvar)) != NULL)
! 			{
! 				option->val = strdup(tmp);
! 				if (!option->val)
! 				{
! 					printfPQExpBuffer(&conn->errorMessage,
! 									  libpq_gettext("out of memory\n"));
! 					PQconninfoFree(options);
! 					return NULL;
! 				}
! 				continue;
! 			}
! 		}
! 
! 		/*
! 		 * No environment variable specified or this one isn't set - try
! 		 * compiled in
! 		 */
! 		if (option->compiled != NULL)
! 		{
! 			option->val = strdup(option->compiled);
! 			if (!option->val)
! 			{
! 				printfPQExpBuffer(&conn->errorMessage,
! 								  libpq_gettext("out of memory\n"));
! 				PQconninfoFree(options);
! 				return NULL;
! 			}
! 			continue;
! 		}
! 
! 		/*
! 		 * Special handling for user
! 		 */
! 		if (strcmp(option->keyword, "user") == 0)
! 		{
! 			option->val = pg_fe_getauthname(&conn->errorMessage);
! 			continue;
! 		}
! 	}
! 
! 	if (options == NULL)
! 	{
! 		conn->status = CONNECTION_BAD;
! 		/* errorMessage is already set */
! 		return false;
! 	}
! 
! 	/*
! 	 * Move option values into conn structure
! 	 */
!     fillPGconn(conn, options);
! 
! 
! 	/*
! 	 * Free the option info - all is in conn now
! 	 */
! 	PQconninfoFree(options);
  
  	/*
  	 * Compute derived options
*************** PQconnectStart(const char *conninfo)
*** 383,419 ****
  		conn->status = CONNECTION_BAD;
  	}
  
! 	return conn;
  }
  
  /*
!  *		connectOptions1
   *
!  * Internal subroutine to set up connection parameters given an already-
!  * created PGconn and a conninfo string.  Derived settings should be
!  * processed by calling connectOptions2 next.  (We split them because
!  * PQsetdbLogin overrides defaults in between.)
   *
!  * Returns true if OK, false if trouble (in which case errorMessage is set
!  * and so is conn->status).
   */
! static bool
! connectOptions1(PGconn *conn, const char *conninfo)
  {
! 	PQconninfoOption *connOptions;
! 	char	   *tmp;
  
  	/*
  	 * Parse the conninfo string
  	 */
! 	connOptions = conninfo_parse(conninfo, &conn->errorMessage, true);
! 	if (connOptions == NULL)
  	{
  		conn->status = CONNECTION_BAD;
- 		/* errorMessage is already set */
- 		return false;
  	}
  
  	/*
  	 * Move option values into conn structure
  	 *
--- 560,628 ----
  		conn->status = CONNECTION_BAD;
  	}
  
!     return conn;
  }
  
  /*
!  *		PQconnectStart
   *
!  * Begins the establishment of a connection to a postgres backend through the
!  * postmaster using connection information in a string.
   *
!  * See comment for PQconnectdb for the definition of the string format.
!  *
!  * Returns a PGconn*.  If NULL is returned, a malloc error has occurred, and
!  * you should not attempt to proceed with this connection.	If the status
!  * field of the connection returned is CONNECTION_BAD, an error has
!  * occurred. In this case you should call PQfinish on the result, (perhaps
!  * inspecting the error message first).  Other fields of the structure may not
!  * be valid if that occurs.  If the status field is not CONNECTION_BAD, then
!  * this stage has succeeded - call PQconnectPoll, using select(2) to see when
!  * this is necessary.
!  *
!  * See PQconnectPoll for more info.
   */
! PGconn *
! PQconnectStart(const char *conninfo)
  {
! 	PGconn	   *conn;
! 
! 	/*
! 	 * Allocate memory for the conn structure
! 	 */
! 	conn = makeEmptyPGconn();
! 	if (conn == NULL)
! 		return NULL;
  
  	/*
  	 * Parse the conninfo string
  	 */
! 	if (!connectOptions1(conn, conninfo))
! 		return conn;
! 
! 	/*
! 	 * Compute derived options
! 	 */
! 	if (!connectOptions2(conn))
! 		return conn;
! 
! 	/*
! 	 * Connect to the database
! 	 */
! 	if (!connectDBStart(conn))
  	{
+ 		/* Just in case we failed to set it in connectDBStart */
  		conn->status = CONNECTION_BAD;
  	}
  
+ 	return conn;
+ }
+ 
+ static void
+ fillPGconn(PGconn *conn, PQconninfoOption *connOptions)
+ {
+ 	char	   *tmp;
+ 
  	/*
  	 * Move option values into conn structure
  	 *
*************** connectOptions1(PGconn *conn, const char
*** 472,477 ****
--- 681,719 ----
  	tmp = conninfo_getval(connOptions, "gsslib");
  	conn->gsslib = tmp ? strdup(tmp) : NULL;
  #endif
+ }
+ 
+ /*
+  *		connectOptions1
+  *
+  * Internal subroutine to set up connection parameters given an already-
+  * created PGconn and a conninfo string.  Derived settings should be
+  * processed by calling connectOptions2 next.  (We split them because
+  * PQsetdbLogin overrides defaults in between.)
+  *
+  * Returns true if OK, false if trouble (in which case errorMessage is set
+  * and so is conn->status).
+  */
+ static bool
+ connectOptions1(PGconn *conn, const char *conninfo)
+ {
+ 	PQconninfoOption *connOptions;
+ 
+ 	/*
+ 	 * Parse the conninfo string
+ 	 */
+ 	connOptions = conninfo_parse(conninfo, &conn->errorMessage, true);
+ 	if (connOptions == NULL)
+ 	{
+ 		conn->status = CONNECTION_BAD;
+ 		/* errorMessage is already set */
+ 		return false;
+ 	}
+ 
+ 	/*
+ 	 * Move option values into conn structure
+ 	 */
+     fillPGconn(conn, connOptions);
  
  	/*
  	 * Free the option info - all is in conn now
Index: src/interfaces/libpq/libpq-fe.h
===================================================================
RCS file: /opt/cvsroot_postgresql/pgsql/src/interfaces/libpq/libpq-fe.h,v
retrieving revision 1.148
diff -c -p -c -r1.148 libpq-fe.h
*** src/interfaces/libpq/libpq-fe.h	2 Jan 2010 16:58:12 -0000	1.148
--- src/interfaces/libpq/libpq-fe.h	4 Jan 2010 21:14:47 -0000
*************** typedef struct pgresAttDesc
*** 226,235 ****
--- 226,237 ----
  /* make a new client connection to the backend */
  /* Asynchronous (non-blocking) */
  extern PGconn *PQconnectStart(const char *conninfo);
+ extern PGconn *PQconnectStartParams(const char **keywords, const char **values);
  extern PostgresPollingStatusType PQconnectPoll(PGconn *conn);
  
  /* Synchronous (blocking) */
  extern PGconn *PQconnectdb(const char *conninfo);
+ extern PGconn *PQconnectdbParams(const char **keywords, const char **values);
  extern PGconn *PQsetdbLogin(const char *pghost, const char *pgport,
  			 const char *pgoptions, const char *pgtty,
  			 const char *dbName,
#21Guillaume Lelarge
guillaume@lelarge.info
In reply to: Guillaume Lelarge (#20)
Re: Application name patch - v3

Le 04/01/2010 22:36, Guillaume Lelarge a �crit :

Le 29/12/2009 14:12, Guillaume Lelarge a �crit :

Le 29/12/2009 00:03, Guillaume Lelarge a �crit :

Le 28/12/2009 22:59, Tom Lane a �crit :

Guillaume Lelarge <guillaume@lelarge.info> writes:

Le 28/12/2009 17:06, Tom Lane a �crit :

I think we were stalled on the question of whether to use one array
or two parallel arrays. Do you want to try coding up a sample usage
of each possibility so we can see which one seems more useful?

I'm interested in working on this. But I don't find the thread that talk
about this.

Try here
http://archives.postgresql.org/message-id/4AAE8CCF.9070808@esilo.com

Thanks. I've read all the "new version of PQconnectdb" and "Determining
client_encoding from client locale" threads. I think I understand the
goal. Still need to re-read this one
(http://archives.postgresql.org/message-id/6222.1253734019@sss.pgh.pa.us) and
completely understand it (will probably need to look at the code, at
least the PQconnectdb one). But I'm definitely working on this.

If I try to sum up my readings so far, this is what we still have to do:

1. try the one-array approach
PGconn *PQconnectParams(const char **params)

2. try the two-arrays approach
PGconn *PQconnectParams(const char **keywords, const char **values)

Instead of doing a wrapper around PQconnectdb, we need to refactor the
whole function, so that we can get rid of the parsing of the conninfo
string (which is quite complicated).

Using psql as an example would be a good idea, AFAICT.

Am I right? did I misunderstand or forget something?

I supposed I was right since noone yell at me :)

I worked on this tonight. You'll find two patches attached, one for the
one-array approach, one for the two-arrays approach. I know some more
factoring can be done (at least, the "get the fallback resources..."
part). I'm OK to do them. I just need to know if I'm on the right track.

Hmmm... sorry but... can i have some comments on these two patches, please?

--
Guillaume.
http://www.postgresqlfr.org
http://dalibo.com

#22Robert Haas
robertmhaas@gmail.com
In reply to: Guillaume Lelarge (#21)
Re: Application name patch - v3

On Thu, Jan 7, 2010 at 10:33 AM, Guillaume Lelarge
<guillaume@lelarge.info> wrote:

Le 04/01/2010 22:36, Guillaume Lelarge a écrit :

Le 29/12/2009 14:12, Guillaume Lelarge a écrit :

Le 29/12/2009 00:03, Guillaume Lelarge a écrit :

Le 28/12/2009 22:59, Tom Lane a écrit :

Guillaume Lelarge <guillaume@lelarge.info> writes:

Le 28/12/2009 17:06, Tom Lane a écrit :

I think we were stalled on the question of whether to use one array
or two parallel arrays.  Do you want to try coding up a sample usage
of each possibility so we can see which one seems more useful?

I'm interested in working on this. But I don't find the thread that talk
about this.

Try here
http://archives.postgresql.org/message-id/4AAE8CCF.9070808@esilo.com

Thanks. I've read all the "new version of PQconnectdb" and "Determining
client_encoding from client locale" threads. I think I understand the
goal. Still need to re-read this one
(http://archives.postgresql.org/message-id/6222.1253734019@sss.pgh.pa.us) and
completely understand it (will probably need to look at the code, at
least the PQconnectdb one). But I'm definitely working on this.

If I try to sum up my readings so far, this is what we still have to do:

1. try the one-array approach
   PGconn *PQconnectParams(const char **params)

2. try the two-arrays approach
   PGconn *PQconnectParams(const char **keywords, const char **values)

Instead of doing a wrapper around PQconnectdb, we need to refactor the
whole function, so that we can get rid of the parsing of the conninfo
string (which is quite complicated).

Using psql as an example would be a good idea, AFAICT.

Am I right? did I misunderstand or forget something?

I supposed I was right since noone yell at me :)

I worked on this tonight. You'll find two patches attached, one for the
one-array approach, one for the two-arrays approach. I know some more
factoring can be done (at least, the "get the fallback resources..."
part). I'm OK to do them. I just need to know if I'm on the right track.

Hmmm... sorry but... can i have some comments on these two patches, please?

I would suggest adding your patch(es) to:

https://commitfest.postgresql.org/action/commitfest_view/open

Probably just one entry for the two of them would be most appropriate.

...Robert

#23Guillaume Lelarge
guillaume@lelarge.info
In reply to: Robert Haas (#22)
Re: Application name patch - v3

Le 07/01/2010 19:13, Robert Haas a �crit :

On Thu, Jan 7, 2010 at 10:33 AM, Guillaume Lelarge
<guillaume@lelarge.info> wrote:

Le 04/01/2010 22:36, Guillaume Lelarge a �crit :

Le 29/12/2009 14:12, Guillaume Lelarge a �crit :

Le 29/12/2009 00:03, Guillaume Lelarge a �crit :

Le 28/12/2009 22:59, Tom Lane a �crit :

Guillaume Lelarge <guillaume@lelarge.info> writes:

Le 28/12/2009 17:06, Tom Lane a �crit :

I think we were stalled on the question of whether to use one array
or two parallel arrays. Do you want to try coding up a sample usage
of each possibility so we can see which one seems more useful?

I'm interested in working on this. But I don't find the thread that talk
about this.

Try here
http://archives.postgresql.org/message-id/4AAE8CCF.9070808@esilo.com

Thanks. I've read all the "new version of PQconnectdb" and "Determining
client_encoding from client locale" threads. I think I understand the
goal. Still need to re-read this one
(http://archives.postgresql.org/message-id/6222.1253734019@sss.pgh.pa.us) and
completely understand it (will probably need to look at the code, at
least the PQconnectdb one). But I'm definitely working on this.

If I try to sum up my readings so far, this is what we still have to do:

1. try the one-array approach
PGconn *PQconnectParams(const char **params)

2. try the two-arrays approach
PGconn *PQconnectParams(const char **keywords, const char **values)

Instead of doing a wrapper around PQconnectdb, we need to refactor the
whole function, so that we can get rid of the parsing of the conninfo
string (which is quite complicated).

Using psql as an example would be a good idea, AFAICT.

Am I right? did I misunderstand or forget something?

I supposed I was right since noone yell at me :)

I worked on this tonight. You'll find two patches attached, one for the
one-array approach, one for the two-arrays approach. I know some more
factoring can be done (at least, the "get the fallback resources..."
part). I'm OK to do them. I just need to know if I'm on the right track.

Hmmm... sorry but... can i have some comments on these two patches, please?

I would suggest adding your patch(es) to:

https://commitfest.postgresql.org/action/commitfest_view/open

Probably just one entry for the two of them would be most appropriate.

Done. Thanks.

--
Guillaume.
http://www.postgresqlfr.org
http://dalibo.com

#24Guillaume Lelarge
guillaume@lelarge.info
In reply to: Guillaume Lelarge (#23)
2 attachment(s)
Re: Application name patch - v3

Le 08/01/2010 23:22, Guillaume Lelarge a �crit :

Le 07/01/2010 19:13, Robert Haas a �crit :

On Thu, Jan 7, 2010 at 10:33 AM, Guillaume Lelarge
<guillaume@lelarge.info> wrote:

Le 04/01/2010 22:36, Guillaume Lelarge a �crit :

Le 29/12/2009 14:12, Guillaume Lelarge a �crit :

Le 29/12/2009 00:03, Guillaume Lelarge a �crit :

Le 28/12/2009 22:59, Tom Lane a �crit :

Guillaume Lelarge <guillaume@lelarge.info> writes:

Le 28/12/2009 17:06, Tom Lane a �crit :

I think we were stalled on the question of whether to use one array
or two parallel arrays. Do you want to try coding up a sample usage
of each possibility so we can see which one seems more useful?

I'm interested in working on this. But I don't find the thread that talk
about this.

Try here
http://archives.postgresql.org/message-id/4AAE8CCF.9070808@esilo.com

Thanks. I've read all the "new version of PQconnectdb" and "Determining
client_encoding from client locale" threads. I think I understand the
goal. Still need to re-read this one
(http://archives.postgresql.org/message-id/6222.1253734019@sss.pgh.pa.us) and
completely understand it (will probably need to look at the code, at
least the PQconnectdb one). But I'm definitely working on this.

If I try to sum up my readings so far, this is what we still have to do:

1. try the one-array approach
PGconn *PQconnectParams(const char **params)

2. try the two-arrays approach
PGconn *PQconnectParams(const char **keywords, const char **values)

Instead of doing a wrapper around PQconnectdb, we need to refactor the
whole function, so that we can get rid of the parsing of the conninfo
string (which is quite complicated).

Using psql as an example would be a good idea, AFAICT.

Am I right? did I misunderstand or forget something?

I supposed I was right since noone yell at me :)

I worked on this tonight. You'll find two patches attached, one for the
one-array approach, one for the two-arrays approach. I know some more
factoring can be done (at least, the "get the fallback resources..."
part). I'm OK to do them. I just need to know if I'm on the right track.

Hmmm... sorry but... can i have some comments on these two patches, please?

I would suggest adding your patch(es) to:

https://commitfest.postgresql.org/action/commitfest_view/open

Probably just one entry for the two of them would be most appropriate.

Done. Thanks.

New patches because the old ones didn't apply anymore, due to recent CVS
commits.

--
Guillaume.
http://www.postgresqlfr.org
http://dalibo.com

Attachments:

libpqParams1_v2.patchtext/x-patch; name=libpqParams1_v2.patchDownload
Index: src/bin/psql/startup.c
===================================================================
RCS file: /opt/cvsroot_postgresql/pgsql/src/bin/psql/startup.c,v
retrieving revision 1.158
diff -c -p -c -r1.158 startup.c
*** src/bin/psql/startup.c	2 Jan 2010 16:57:59 -0000	1.158
--- src/bin/psql/startup.c	4 Jan 2010 21:04:13 -0000
*************** main(int argc, char *argv[])
*** 171,181 ****
  	/* loop until we have a password if requested by backend */
  	do
  	{
! 		new_pass = false;
! 		pset.db = PQsetdbLogin(options.host, options.port, NULL, NULL,
! 					options.action == ACT_LIST_DB && options.dbname == NULL ?
! 							   "postgres" : options.dbname,
! 							   options.username, password);
  
  		if (PQstatus(pset.db) == CONNECTION_BAD &&
  			PQconnectionNeedsPassword(pset.db) &&
--- 171,190 ----
  	/* loop until we have a password if requested by backend */
  	do
  	{
!         const char *params[] = {
!                   "host", options.host,
!                   "port", options.port,
!                   "dbname", (options.action == ACT_LIST_DB && 
!                                options.dbname == NULL) ? "postgres" : options.dbname,
!                   "user", options.username,
!                   "password", password,
!                   "application_name", pset.progname,
!                   NULL, NULL
!               };
!         
!         new_pass = false;
! 
!         pset.db = PQconnectdbParams(params);
  
  		if (PQstatus(pset.db) == CONNECTION_BAD &&
  			PQconnectionNeedsPassword(pset.db) &&
Index: src/interfaces/libpq/exports.txt
===================================================================
RCS file: /opt/cvsroot_postgresql/pgsql/src/interfaces/libpq/exports.txt,v
retrieving revision 1.23
diff -c -p -c -r1.23 exports.txt
*** src/interfaces/libpq/exports.txt	31 Mar 2009 01:41:27 -0000	1.23
--- src/interfaces/libpq/exports.txt	4 Jan 2010 20:51:13 -0000
*************** PQresultSetInstanceData   150
*** 153,155 ****
--- 153,157 ----
  PQfireResultCreateEvents  151
  PQconninfoParse           152
  PQinitOpenSSL             153
+ PQconnectdbParams         154
+ PQconnectStartParams      155
Index: src/interfaces/libpq/fe-connect.c
===================================================================
RCS file: /opt/cvsroot_postgresql/pgsql/src/interfaces/libpq/fe-connect.c,v
retrieving revision 1.383
diff -c -p -c -r1.383 fe-connect.c
*** src/interfaces/libpq/fe-connect.c	15 Jan 2010 09:19:10 -0000	1.383
--- src/interfaces/libpq/fe-connect.c	15 Jan 2010 17:37:03 -0000
*************** static bool connectOptions2(PGconn *conn
*** 262,267 ****
--- 262,268 ----
  static int	connectDBStart(PGconn *conn);
  static int	connectDBComplete(PGconn *conn);
  static PGconn *makeEmptyPGconn(void);
+ static void fillPGconn(PGconn *conn, PQconninfoOption *connOptions);
  static void freePGconn(PGconn *conn);
  static void closePGconn(PGconn *conn);
  static PQconninfoOption *conninfo_parse(const char *conninfo,
*************** pgthreadlock_t pg_g_threadlock = default
*** 302,307 ****
--- 303,340 ----
   */
  
  /*
+  *		PQconnectdbParams
+  *
+  * establishes a connection to a postgres backend through the postmaster
+  * using connection information in a struct.
+  *
+  * The params struct string is defined as
+  *
+  *	   const char *params[] = {option1, value1, option2, value2, NULL}
+  *
+  * definitions.
+  *
+  * Returns a PGconn* which is needed for all subsequent libpq calls, or NULL
+  * if a memory allocation failed.
+  * If the status field of the connection returned is CONNECTION_BAD,
+  * then some fields may be null'ed out instead of having valid values.
+  *
+  * You should call PQfinish (if conn is not NULL) regardless of whether this
+  * call succeeded.
+  */
+ PGconn *
+ PQconnectdbParams(const char * const *params)
+ {
+ 	PGconn	   *conn = PQconnectStartParams(params);
+ 
+ 	if (conn && conn->status != CONNECTION_BAD)
+ 		(void) connectDBComplete(conn);
+ 
+ 	return conn;
+ 
+ }
+ 
+ /*
   *		PQconnectdb
   *
   * establishes a connection to a postgres backend through the postmaster
*************** PQconnectdb(const char *conninfo)
*** 335,346 ****
  }
  
  /*
!  *		PQconnectStart
   *
   * Begins the establishment of a connection to a postgres backend through the
!  * postmaster using connection information in a string.
   *
!  * See comment for PQconnectdb for the definition of the string format.
   *
   * Returns a PGconn*.  If NULL is returned, a malloc error has occurred, and
   * you should not attempt to proceed with this connection.	If the status
--- 368,379 ----
  }
  
  /*
!  *		PQconnectStartParams
   *
   * Begins the establishment of a connection to a postgres backend through the
!  * postmaster using connection information in a struct.
   *
!  * See comment for PQconnectdbParams for the definition of the string format.
   *
   * Returns a PGconn*.  If NULL is returned, a malloc error has occurred, and
   * you should not attempt to proceed with this connection.	If the status
*************** PQconnectdb(const char *conninfo)
*** 354,362 ****
   * See PQconnectPoll for more info.
   */
  PGconn *
! PQconnectStart(const char *conninfo)
  {
! 	PGconn	   *conn;
  
  	/*
  	 * Allocate memory for the conn structure
--- 387,398 ----
   * See PQconnectPoll for more info.
   */
  PGconn *
! PQconnectStartParams(const char * const *params)
  {
!     PGconn *conn;
! 	PQconninfoOption *options;
! 	PQconninfoOption *option;
! 	char	   *tmp;
  
  	/*
  	 * Allocate memory for the conn structure
*************** PQconnectStart(const char *conninfo)
*** 366,375 ****
  		return NULL;
  
  	/*
! 	 * Parse the conninfo string
  	 */
! 	if (!connectOptions1(conn, conninfo))
! 		return conn;
  
  	/*
  	 * Compute derived options
--- 402,549 ----
  		return NULL;
  
  	/*
!      * Make a working copy of PQconninfoOptions
!      */
! 	options = malloc(sizeof(PQconninfoOptions));
! 	if (options == NULL)
! 	{
! 		printfPQExpBuffer(&conn->errorMessage,
! 						  libpq_gettext("out of memory\n"));
! 		return NULL;
! 	}
! 	memcpy(options, PQconninfoOptions, sizeof(PQconninfoOptions));
! 
! 	/*
! 	 * Parse the params struct
  	 */
!     while(*params)
!     {
!         const char *pname = params[0];
!         const char *pvalue  = params[1];
!  
!         if (pvalue != NULL)
!         {
! 		    /*
! 		     * Now we have the name and the value. Search for the param record.
! 		     */
! 		    for (option = options; option->keyword != NULL; option++)
! 		    {
! 		    	if (strcmp(option->keyword, pname) == 0)
! 		    		break;
! 		    }
! 
!             /*
!              * Check for invalid connection option
!              */
! 		    if (option->keyword == NULL)
! 		    {
! 		    	printfPQExpBuffer(&conn->errorMessage,
! 		    				 libpq_gettext("invalid connection option \"%s\"\n"),
! 		    					  pname);
! 		    	PQconninfoFree(options);
! 		    	return NULL;
! 		    }
! 
! 		    /*
! 		     * Store the value
! 		     */
! 		    if (option->val)
! 		    	free(option->val);
! 		    option->val = strdup(pvalue);
! 		    if (!option->val)
! 		    {
! 		    	printfPQExpBuffer(&conn->errorMessage,
! 		    					  libpq_gettext("out of memory\n"));
! 		    	PQconninfoFree(options);
! 		    	return NULL;
! 		    }
!         }
! 
!         params+=2;
! 	}
! 
! 	/*
! 	 * If there's a service spec, use it to obtain any not-explicitly-given
! 	 * parameters.
! 	 */
! 	if (parseServiceInfo(options, &conn->errorMessage))
! 	{
! 		PQconninfoFree(options);
! 		return NULL;
! 	}
! 
! 	/*
! 	 * Get the fallback resources for parameters not specified in the conninfo
! 	 * string nor the service.
! 	 */
! 	for (option = options; option->keyword != NULL; option++)
! 	{
! 		if (option->val != NULL)
! 			continue;			/* Value was in conninfo or service */
! 
! 		/*
! 		 * Try to get the environment variable fallback
! 		 */
! 		if (option->envvar != NULL)
! 		{
! 			if ((tmp = getenv(option->envvar)) != NULL)
! 			{
! 				option->val = strdup(tmp);
! 				if (!option->val)
! 				{
! 					printfPQExpBuffer(&conn->errorMessage,
! 									  libpq_gettext("out of memory\n"));
! 					PQconninfoFree(options);
! 					return NULL;
! 				}
! 				continue;
! 			}
! 		}
! 
! 		/*
! 		 * No environment variable specified or this one isn't set - try
! 		 * compiled in
! 		 */
! 		if (option->compiled != NULL)
! 		{
! 			option->val = strdup(option->compiled);
! 			if (!option->val)
! 			{
! 				printfPQExpBuffer(&conn->errorMessage,
! 								  libpq_gettext("out of memory\n"));
! 				PQconninfoFree(options);
! 				return NULL;
! 			}
! 			continue;
! 		}
! 
! 		/*
! 		 * Special handling for user
! 		 */
! 		if (strcmp(option->keyword, "user") == 0)
! 		{
! 			option->val = pg_fe_getauthname(&conn->errorMessage);
! 			continue;
! 		}
! 	}
! 
! 	if (options == NULL)
! 	{
! 		conn->status = CONNECTION_BAD;
! 		/* errorMessage is already set */
! 		return false;
! 	}
! 
! 	/*
! 	 * Move option values into conn structure
! 	 */
!     fillPGconn(conn, options);
! 
! 
! 	/*
! 	 * Free the option info - all is in conn now
! 	 */
! 	PQconninfoFree(options);
  
  	/*
  	 * Compute derived options
*************** PQconnectStart(const char *conninfo)
*** 386,422 ****
  		conn->status = CONNECTION_BAD;
  	}
  
! 	return conn;
  }
  
  /*
!  *		connectOptions1
   *
!  * Internal subroutine to set up connection parameters given an already-
!  * created PGconn and a conninfo string.  Derived settings should be
!  * processed by calling connectOptions2 next.  (We split them because
!  * PQsetdbLogin overrides defaults in between.)
   *
!  * Returns true if OK, false if trouble (in which case errorMessage is set
!  * and so is conn->status).
   */
! static bool
! connectOptions1(PGconn *conn, const char *conninfo)
  {
! 	PQconninfoOption *connOptions;
! 	char	   *tmp;
  
  	/*
  	 * Parse the conninfo string
  	 */
! 	connOptions = conninfo_parse(conninfo, &conn->errorMessage, true);
! 	if (connOptions == NULL)
  	{
  		conn->status = CONNECTION_BAD;
- 		/* errorMessage is already set */
- 		return false;
  	}
  
  	/*
  	 * Move option values into conn structure
  	 *
--- 560,628 ----
  		conn->status = CONNECTION_BAD;
  	}
  
!     return conn;
  }
  
  /*
!  *		PQconnectStart
   *
!  * Begins the establishment of a connection to a postgres backend through the
!  * postmaster using connection information in a string.
   *
!  * See comment for PQconnectdb for the definition of the string format.
!  *
!  * Returns a PGconn*.  If NULL is returned, a malloc error has occurred, and
!  * you should not attempt to proceed with this connection.	If the status
!  * field of the connection returned is CONNECTION_BAD, an error has
!  * occurred. In this case you should call PQfinish on the result, (perhaps
!  * inspecting the error message first).  Other fields of the structure may not
!  * be valid if that occurs.  If the status field is not CONNECTION_BAD, then
!  * this stage has succeeded - call PQconnectPoll, using select(2) to see when
!  * this is necessary.
!  *
!  * See PQconnectPoll for more info.
   */
! PGconn *
! PQconnectStart(const char *conninfo)
  {
! 	PGconn	   *conn;
! 
! 	/*
! 	 * Allocate memory for the conn structure
! 	 */
! 	conn = makeEmptyPGconn();
! 	if (conn == NULL)
! 		return NULL;
  
  	/*
  	 * Parse the conninfo string
  	 */
! 	if (!connectOptions1(conn, conninfo))
! 		return conn;
! 
! 	/*
! 	 * Compute derived options
! 	 */
! 	if (!connectOptions2(conn))
! 		return conn;
! 
! 	/*
! 	 * Connect to the database
! 	 */
! 	if (!connectDBStart(conn))
  	{
+ 		/* Just in case we failed to set it in connectDBStart */
  		conn->status = CONNECTION_BAD;
  	}
  
+ 	return conn;
+ }
+ 
+ static void
+ fillPGconn(PGconn *conn, PQconninfoOption *connOptions)
+ {
+ 	char	   *tmp;
+ 
  	/*
  	 * Move option values into conn structure
  	 *
*************** connectOptions1(PGconn *conn, const char
*** 477,482 ****
--- 683,721 ----
  #endif
  	tmp = conninfo_getval(connOptions, "replication");
  	conn->replication = tmp ? strdup(tmp) : NULL;
+ }
+ 
+ /*
+  *		connectOptions1
+  *
+  * Internal subroutine to set up connection parameters given an already-
+  * created PGconn and a conninfo string.  Derived settings should be
+  * processed by calling connectOptions2 next.  (We split them because
+  * PQsetdbLogin overrides defaults in between.)
+  *
+  * Returns true if OK, false if trouble (in which case errorMessage is set
+  * and so is conn->status).
+  */
+ static bool
+ connectOptions1(PGconn *conn, const char *conninfo)
+ {
+ 	PQconninfoOption *connOptions;
+ 
+ 	/*
+ 	 * Parse the conninfo string
+ 	 */
+ 	connOptions = conninfo_parse(conninfo, &conn->errorMessage, true);
+ 	if (connOptions == NULL)
+ 	{
+ 		conn->status = CONNECTION_BAD;
+ 		/* errorMessage is already set */
+ 		return false;
+ 	}
+ 
+ 	/*
+ 	 * Move option values into conn structure
+ 	 */
+     fillPGconn(conn, connOptions);
  
  	/*
  	 * Free the option info - all is in conn now
Index: src/interfaces/libpq/libpq-fe.h
===================================================================
RCS file: /opt/cvsroot_postgresql/pgsql/src/interfaces/libpq/libpq-fe.h,v
retrieving revision 1.148
diff -c -p -c -r1.148 libpq-fe.h
*** src/interfaces/libpq/libpq-fe.h	2 Jan 2010 16:58:12 -0000	1.148
--- src/interfaces/libpq/libpq-fe.h	4 Jan 2010 20:51:53 -0000
*************** typedef struct pgresAttDesc
*** 226,235 ****
--- 226,237 ----
  /* make a new client connection to the backend */
  /* Asynchronous (non-blocking) */
  extern PGconn *PQconnectStart(const char *conninfo);
+ extern PGconn *PQconnectStartParams(const char * const *params);
  extern PostgresPollingStatusType PQconnectPoll(PGconn *conn);
  
  /* Synchronous (blocking) */
  extern PGconn *PQconnectdb(const char *conninfo);
+ extern PGconn *PQconnectdbParams(const char * const *params);
  extern PGconn *PQsetdbLogin(const char *pghost, const char *pgport,
  			 const char *pgoptions, const char *pgtty,
  			 const char *dbName,
libpqParams2_v2.patchtext/x-patch; name=libpqParams2_v2.patchDownload
Index: src/bin/psql/startup.c
===================================================================
RCS file: /opt/cvsroot_postgresql/pgsql/src/bin/psql/startup.c,v
retrieving revision 1.158
diff -c -p -c -r1.158 startup.c
*** src/bin/psql/startup.c	2 Jan 2010 16:57:59 -0000	1.158
--- src/bin/psql/startup.c	4 Jan 2010 21:27:12 -0000
*************** main(int argc, char *argv[])
*** 168,181 ****
  	if (pset.getPassword == TRI_YES)
  		password = simple_prompt(password_prompt, 100, false);
  
  	/* loop until we have a password if requested by backend */
  	do
  	{
! 		new_pass = false;
! 		pset.db = PQsetdbLogin(options.host, options.port, NULL, NULL,
! 					options.action == ACT_LIST_DB && options.dbname == NULL ?
! 							   "postgres" : options.dbname,
! 							   options.username, password);
  
  		if (PQstatus(pset.db) == CONNECTION_BAD &&
  			PQconnectionNeedsPassword(pset.db) &&
--- 168,200 ----
  	if (pset.getPassword == TRI_YES)
  		password = simple_prompt(password_prompt, 100, false);
  
+     const char *keywords[] = {
+         "host",
+         "port",
+         "dbname",
+         "user",
+         "password",
+         "application_name",
+         NULL
+         };
+         
  	/* loop until we have a password if requested by backend */
  	do
  	{
!         const char *values[] = {
!                   options.host,
!                   options.port,
!                   (options.action == ACT_LIST_DB && 
!                                options.dbname == NULL) ? "postgres" : options.dbname,
!                   options.username,
!                   password,
!                   pset.progname,
!                   NULL
!               };
!         
!         new_pass = false;
! 
!         pset.db = PQconnectdbParams(keywords, values);
  
  		if (PQstatus(pset.db) == CONNECTION_BAD &&
  			PQconnectionNeedsPassword(pset.db) &&
Index: src/interfaces/libpq/exports.txt
===================================================================
RCS file: /opt/cvsroot_postgresql/pgsql/src/interfaces/libpq/exports.txt,v
retrieving revision 1.23
diff -c -p -c -r1.23 exports.txt
*** src/interfaces/libpq/exports.txt	31 Mar 2009 01:41:27 -0000	1.23
--- src/interfaces/libpq/exports.txt	4 Jan 2010 21:07:49 -0000
*************** PQresultSetInstanceData   150
*** 153,155 ****
--- 153,157 ----
  PQfireResultCreateEvents  151
  PQconninfoParse           152
  PQinitOpenSSL             153
+ PQconnectdbParams         154
+ PQconnectStartParams      155
Index: src/interfaces/libpq/fe-connect.c
===================================================================
RCS file: /opt/cvsroot_postgresql/pgsql/src/interfaces/libpq/fe-connect.c,v
retrieving revision 1.383
diff -c -p -c -r1.383 fe-connect.c
*** src/interfaces/libpq/fe-connect.c	15 Jan 2010 09:19:10 -0000	1.383
--- src/interfaces/libpq/fe-connect.c	15 Jan 2010 17:37:52 -0000
*************** static bool connectOptions2(PGconn *conn
*** 262,267 ****
--- 262,268 ----
  static int	connectDBStart(PGconn *conn);
  static int	connectDBComplete(PGconn *conn);
  static PGconn *makeEmptyPGconn(void);
+ static void fillPGconn(PGconn *conn, PQconninfoOption *connOptions);
  static void freePGconn(PGconn *conn);
  static void closePGconn(PGconn *conn);
  static PQconninfoOption *conninfo_parse(const char *conninfo,
*************** pgthreadlock_t pg_g_threadlock = default
*** 302,307 ****
--- 303,342 ----
   */
  
  /*
+  *		PQconnectdbParams
+  *
+  * establishes a connection to a postgres backend through the postmaster
+  * using connection information in two structs.
+  *
+  * The keywords struct is defined as
+  *
+  *	   const char *params[] = {option1, option2, NULL}
+  *
+  * The values struct is defined as
+  *
+  *	   const char *params[] = {option1, option2, NULL}
+  *
+  * Returns a PGconn* which is needed for all subsequent libpq calls, or NULL
+  * if a memory allocation failed.
+  * If the status field of the connection returned is CONNECTION_BAD,
+  * then some fields may be null'ed out instead of having valid values.
+  *
+  * You should call PQfinish (if conn is not NULL) regardless of whether this
+  * call succeeded.
+  */
+ PGconn *
+ PQconnectdbParams(const char **keywords, const char **values)
+ {
+ 	PGconn	   *conn = PQconnectStartParams(keywords, values);
+ 
+ 	if (conn && conn->status != CONNECTION_BAD)
+ 		(void) connectDBComplete(conn);
+ 
+ 	return conn;
+ 
+ }
+ 
+ /*
   *		PQconnectdb
   *
   * establishes a connection to a postgres backend through the postmaster
*************** PQconnectdb(const char *conninfo)
*** 335,346 ****
  }
  
  /*
!  *		PQconnectStart
   *
   * Begins the establishment of a connection to a postgres backend through the
!  * postmaster using connection information in a string.
   *
!  * See comment for PQconnectdb for the definition of the string format.
   *
   * Returns a PGconn*.  If NULL is returned, a malloc error has occurred, and
   * you should not attempt to proceed with this connection.	If the status
--- 370,381 ----
  }
  
  /*
!  *		PQconnectStartParams
   *
   * Begins the establishment of a connection to a postgres backend through the
!  * postmaster using connection information in a struct.
   *
!  * See comment for PQconnectdbParams for the definition of the string format.
   *
   * Returns a PGconn*.  If NULL is returned, a malloc error has occurred, and
   * you should not attempt to proceed with this connection.	If the status
*************** PQconnectdb(const char *conninfo)
*** 354,362 ****
   * See PQconnectPoll for more info.
   */
  PGconn *
! PQconnectStart(const char *conninfo)
  {
! 	PGconn	   *conn;
  
  	/*
  	 * Allocate memory for the conn structure
--- 389,400 ----
   * See PQconnectPoll for more info.
   */
  PGconn *
! PQconnectStartParams(const char **keywords, const char **values)
  {
!     PGconn *conn;
! 	PQconninfoOption *options;
! 	PQconninfoOption *option;
! 	char	   *tmp;
  
  	/*
  	 * Allocate memory for the conn structure
*************** PQconnectStart(const char *conninfo)
*** 366,375 ****
  		return NULL;
  
  	/*
! 	 * Parse the conninfo string
  	 */
! 	if (!connectOptions1(conn, conninfo))
! 		return conn;
  
  	/*
  	 * Compute derived options
--- 404,552 ----
  		return NULL;
  
  	/*
!      * Make a working copy of PQconninfoOptions
!      */
! 	options = malloc(sizeof(PQconninfoOptions));
! 	if (options == NULL)
! 	{
! 		printfPQExpBuffer(&conn->errorMessage,
! 						  libpq_gettext("out of memory\n"));
! 		return NULL;
! 	}
! 	memcpy(options, PQconninfoOptions, sizeof(PQconninfoOptions));
! 
! 	/*
! 	 * Parse the params struct
  	 */
!     while(*keywords)
!     {
!         const char *pname = keywords[0];
!         const char *pvalue  = values[0];
!  
!         if (pvalue != NULL)
!         {
! 		    /*
! 		     * Now we have the name and the value. Search for the param record.
! 		     */
! 		    for (option = options; option->keyword != NULL; option++)
! 		    {
! 		    	if (strcmp(option->keyword, pname) == 0)
! 		    		break;
! 		    }
! 
!             /*
!              * Check for invalid connection option
!              */
! 		    if (option->keyword == NULL)
! 		    {
! 		    	printfPQExpBuffer(&conn->errorMessage,
! 		    				 libpq_gettext("invalid connection option \"%s\"\n"),
! 		    					  pname);
! 		    	PQconninfoFree(options);
! 		    	return NULL;
! 		    }
! 
! 		    /*
! 		     * Store the value
! 		     */
! 		    if (option->val)
! 		    	free(option->val);
! 		    option->val = strdup(pvalue);
! 		    if (!option->val)
! 		    {
! 		    	printfPQExpBuffer(&conn->errorMessage,
! 		    					  libpq_gettext("out of memory\n"));
! 		    	PQconninfoFree(options);
! 		    	return NULL;
! 		    }
!         }
! 
!         keywords+=1;
!         values+=1;
! 	}
! 
! 	/*
! 	 * If there's a service spec, use it to obtain any not-explicitly-given
! 	 * parameters.
! 	 */
! 	if (parseServiceInfo(options, &conn->errorMessage))
! 	{
! 		PQconninfoFree(options);
! 		return NULL;
! 	}
! 
! 	/*
! 	 * Get the fallback resources for parameters not specified in the conninfo
! 	 * string nor the service.
! 	 */
! 	for (option = options; option->keyword != NULL; option++)
! 	{
! 		if (option->val != NULL)
! 			continue;			/* Value was in conninfo or service */
! 
! 		/*
! 		 * Try to get the environment variable fallback
! 		 */
! 		if (option->envvar != NULL)
! 		{
! 			if ((tmp = getenv(option->envvar)) != NULL)
! 			{
! 				option->val = strdup(tmp);
! 				if (!option->val)
! 				{
! 					printfPQExpBuffer(&conn->errorMessage,
! 									  libpq_gettext("out of memory\n"));
! 					PQconninfoFree(options);
! 					return NULL;
! 				}
! 				continue;
! 			}
! 		}
! 
! 		/*
! 		 * No environment variable specified or this one isn't set - try
! 		 * compiled in
! 		 */
! 		if (option->compiled != NULL)
! 		{
! 			option->val = strdup(option->compiled);
! 			if (!option->val)
! 			{
! 				printfPQExpBuffer(&conn->errorMessage,
! 								  libpq_gettext("out of memory\n"));
! 				PQconninfoFree(options);
! 				return NULL;
! 			}
! 			continue;
! 		}
! 
! 		/*
! 		 * Special handling for user
! 		 */
! 		if (strcmp(option->keyword, "user") == 0)
! 		{
! 			option->val = pg_fe_getauthname(&conn->errorMessage);
! 			continue;
! 		}
! 	}
! 
! 	if (options == NULL)
! 	{
! 		conn->status = CONNECTION_BAD;
! 		/* errorMessage is already set */
! 		return false;
! 	}
! 
! 	/*
! 	 * Move option values into conn structure
! 	 */
!     fillPGconn(conn, options);
! 
! 
! 	/*
! 	 * Free the option info - all is in conn now
! 	 */
! 	PQconninfoFree(options);
  
  	/*
  	 * Compute derived options
*************** PQconnectStart(const char *conninfo)
*** 386,422 ****
  		conn->status = CONNECTION_BAD;
  	}
  
! 	return conn;
  }
  
  /*
!  *		connectOptions1
   *
!  * Internal subroutine to set up connection parameters given an already-
!  * created PGconn and a conninfo string.  Derived settings should be
!  * processed by calling connectOptions2 next.  (We split them because
!  * PQsetdbLogin overrides defaults in between.)
   *
!  * Returns true if OK, false if trouble (in which case errorMessage is set
!  * and so is conn->status).
   */
! static bool
! connectOptions1(PGconn *conn, const char *conninfo)
  {
! 	PQconninfoOption *connOptions;
! 	char	   *tmp;
  
  	/*
  	 * Parse the conninfo string
  	 */
! 	connOptions = conninfo_parse(conninfo, &conn->errorMessage, true);
! 	if (connOptions == NULL)
  	{
  		conn->status = CONNECTION_BAD;
- 		/* errorMessage is already set */
- 		return false;
  	}
  
  	/*
  	 * Move option values into conn structure
  	 *
--- 563,631 ----
  		conn->status = CONNECTION_BAD;
  	}
  
!     return conn;
  }
  
  /*
!  *		PQconnectStart
   *
!  * Begins the establishment of a connection to a postgres backend through the
!  * postmaster using connection information in a string.
   *
!  * See comment for PQconnectdb for the definition of the string format.
!  *
!  * Returns a PGconn*.  If NULL is returned, a malloc error has occurred, and
!  * you should not attempt to proceed with this connection.	If the status
!  * field of the connection returned is CONNECTION_BAD, an error has
!  * occurred. In this case you should call PQfinish on the result, (perhaps
!  * inspecting the error message first).  Other fields of the structure may not
!  * be valid if that occurs.  If the status field is not CONNECTION_BAD, then
!  * this stage has succeeded - call PQconnectPoll, using select(2) to see when
!  * this is necessary.
!  *
!  * See PQconnectPoll for more info.
   */
! PGconn *
! PQconnectStart(const char *conninfo)
  {
! 	PGconn	   *conn;
! 
! 	/*
! 	 * Allocate memory for the conn structure
! 	 */
! 	conn = makeEmptyPGconn();
! 	if (conn == NULL)
! 		return NULL;
  
  	/*
  	 * Parse the conninfo string
  	 */
! 	if (!connectOptions1(conn, conninfo))
! 		return conn;
! 
! 	/*
! 	 * Compute derived options
! 	 */
! 	if (!connectOptions2(conn))
! 		return conn;
! 
! 	/*
! 	 * Connect to the database
! 	 */
! 	if (!connectDBStart(conn))
  	{
+ 		/* Just in case we failed to set it in connectDBStart */
  		conn->status = CONNECTION_BAD;
  	}
  
+ 	return conn;
+ }
+ 
+ static void
+ fillPGconn(PGconn *conn, PQconninfoOption *connOptions)
+ {
+ 	char	   *tmp;
+ 
  	/*
  	 * Move option values into conn structure
  	 *
*************** connectOptions1(PGconn *conn, const char
*** 477,482 ****
--- 686,724 ----
  #endif
  	tmp = conninfo_getval(connOptions, "replication");
  	conn->replication = tmp ? strdup(tmp) : NULL;
+ }
+ 
+ /*
+  *		connectOptions1
+  *
+  * Internal subroutine to set up connection parameters given an already-
+  * created PGconn and a conninfo string.  Derived settings should be
+  * processed by calling connectOptions2 next.  (We split them because
+  * PQsetdbLogin overrides defaults in between.)
+  *
+  * Returns true if OK, false if trouble (in which case errorMessage is set
+  * and so is conn->status).
+  */
+ static bool
+ connectOptions1(PGconn *conn, const char *conninfo)
+ {
+ 	PQconninfoOption *connOptions;
+ 
+ 	/*
+ 	 * Parse the conninfo string
+ 	 */
+ 	connOptions = conninfo_parse(conninfo, &conn->errorMessage, true);
+ 	if (connOptions == NULL)
+ 	{
+ 		conn->status = CONNECTION_BAD;
+ 		/* errorMessage is already set */
+ 		return false;
+ 	}
+ 
+ 	/*
+ 	 * Move option values into conn structure
+ 	 */
+     fillPGconn(conn, connOptions);
  
  	/*
  	 * Free the option info - all is in conn now
Index: src/interfaces/libpq/libpq-fe.h
===================================================================
RCS file: /opt/cvsroot_postgresql/pgsql/src/interfaces/libpq/libpq-fe.h,v
retrieving revision 1.148
diff -c -p -c -r1.148 libpq-fe.h
*** src/interfaces/libpq/libpq-fe.h	2 Jan 2010 16:58:12 -0000	1.148
--- src/interfaces/libpq/libpq-fe.h	4 Jan 2010 21:14:47 -0000
*************** typedef struct pgresAttDesc
*** 226,235 ****
--- 226,237 ----
  /* make a new client connection to the backend */
  /* Asynchronous (non-blocking) */
  extern PGconn *PQconnectStart(const char *conninfo);
+ extern PGconn *PQconnectStartParams(const char **keywords, const char **values);
  extern PostgresPollingStatusType PQconnectPoll(PGconn *conn);
  
  /* Synchronous (blocking) */
  extern PGconn *PQconnectdb(const char *conninfo);
+ extern PGconn *PQconnectdbParams(const char **keywords, const char **values);
  extern PGconn *PQsetdbLogin(const char *pghost, const char *pgport,
  			 const char *pgoptions, const char *pgtty,
  			 const char *dbName,
#25Guillaume Lelarge
guillaume@lelarge.info
In reply to: Guillaume Lelarge (#24)
2 attachment(s)
Re: Application name patch - v3

Le 15/01/2010 18:53, Guillaume Lelarge a �crit :

Le 08/01/2010 23:22, Guillaume Lelarge a �crit :

Le 07/01/2010 19:13, Robert Haas a �crit :

On Thu, Jan 7, 2010 at 10:33 AM, Guillaume Lelarge
<guillaume@lelarge.info> wrote:

Le 04/01/2010 22:36, Guillaume Lelarge a �crit :

Le 29/12/2009 14:12, Guillaume Lelarge a �crit :

Le 29/12/2009 00:03, Guillaume Lelarge a �crit :

Le 28/12/2009 22:59, Tom Lane a �crit :

Guillaume Lelarge <guillaume@lelarge.info> writes:

Le 28/12/2009 17:06, Tom Lane a �crit :

I think we were stalled on the question of whether to use one array
or two parallel arrays. Do you want to try coding up a sample usage
of each possibility so we can see which one seems more useful?

I'm interested in working on this. But I don't find the thread that talk
about this.

Try here
http://archives.postgresql.org/message-id/4AAE8CCF.9070808@esilo.com

Thanks. I've read all the "new version of PQconnectdb" and "Determining
client_encoding from client locale" threads. I think I understand the
goal. Still need to re-read this one
(http://archives.postgresql.org/message-id/6222.1253734019@sss.pgh.pa.us) and
completely understand it (will probably need to look at the code, at
least the PQconnectdb one). But I'm definitely working on this.

If I try to sum up my readings so far, this is what we still have to do:

1. try the one-array approach
PGconn *PQconnectParams(const char **params)

2. try the two-arrays approach
PGconn *PQconnectParams(const char **keywords, const char **values)

Instead of doing a wrapper around PQconnectdb, we need to refactor the
whole function, so that we can get rid of the parsing of the conninfo
string (which is quite complicated).

Using psql as an example would be a good idea, AFAICT.

Am I right? did I misunderstand or forget something?

I supposed I was right since noone yell at me :)

I worked on this tonight. You'll find two patches attached, one for the
one-array approach, one for the two-arrays approach. I know some more
factoring can be done (at least, the "get the fallback resources..."
part). I'm OK to do them. I just need to know if I'm on the right track.

Hmmm... sorry but... can i have some comments on these two patches, please?

I would suggest adding your patch(es) to:

https://commitfest.postgresql.org/action/commitfest_view/open

Probably just one entry for the two of them would be most appropriate.

Done. Thanks.

New patches because the old ones didn't apply anymore, due to recent CVS
commits.

New patches for same reason.

--
Guillaume.
http://www.postgresqlfr.org
http://dalibo.com

Attachments:

libpqParams1_v3.patchtext/x-patch; name=libpqParams1_v3.patchDownload
Index: src/bin/psql/startup.c
===================================================================
RCS file: /opt/cvsroot_postgresql/pgsql/src/bin/psql/startup.c,v
retrieving revision 1.158
diff -c -p -c -r1.158 startup.c
*** src/bin/psql/startup.c	2 Jan 2010 16:57:59 -0000	1.158
--- src/bin/psql/startup.c	4 Jan 2010 21:04:13 -0000
*************** main(int argc, char *argv[])
*** 171,181 ****
  	/* loop until we have a password if requested by backend */
  	do
  	{
! 		new_pass = false;
! 		pset.db = PQsetdbLogin(options.host, options.port, NULL, NULL,
! 					options.action == ACT_LIST_DB && options.dbname == NULL ?
! 							   "postgres" : options.dbname,
! 							   options.username, password);
  
  		if (PQstatus(pset.db) == CONNECTION_BAD &&
  			PQconnectionNeedsPassword(pset.db) &&
--- 171,190 ----
  	/* loop until we have a password if requested by backend */
  	do
  	{
!         const char *params[] = {
!                   "host", options.host,
!                   "port", options.port,
!                   "dbname", (options.action == ACT_LIST_DB && 
!                                options.dbname == NULL) ? "postgres" : options.dbname,
!                   "user", options.username,
!                   "password", password,
!                   "application_name", pset.progname,
!                   NULL, NULL
!               };
!         
!         new_pass = false;
! 
!         pset.db = PQconnectdbParams(params);
  
  		if (PQstatus(pset.db) == CONNECTION_BAD &&
  			PQconnectionNeedsPassword(pset.db) &&
Index: src/interfaces/libpq/exports.txt
===================================================================
RCS file: /opt/cvsroot_postgresql/pgsql/src/interfaces/libpq/exports.txt,v
retrieving revision 1.24
diff -c -p -c -r1.24 exports.txt
*** src/interfaces/libpq/exports.txt	21 Jan 2010 14:58:53 -0000	1.24
--- src/interfaces/libpq/exports.txt	21 Jan 2010 19:40:50 -0000
*************** PQconninfoParse           152
*** 155,157 ****
--- 155,159 ----
  PQinitOpenSSL             153
  PQescapeLiteral           154
  PQescapeIdentifier        155
+ PQconnectdbParams         156
+ PQconnectStartParams      157
Index: src/interfaces/libpq/fe-connect.c
===================================================================
RCS file: /opt/cvsroot_postgresql/pgsql/src/interfaces/libpq/fe-connect.c,v
retrieving revision 1.384
diff -c -p -c -r1.384 fe-connect.c
*** src/interfaces/libpq/fe-connect.c	20 Jan 2010 21:15:21 -0000	1.384
--- src/interfaces/libpq/fe-connect.c	21 Jan 2010 19:32:11 -0000
*************** static bool connectOptions2(PGconn *conn
*** 262,267 ****
--- 262,268 ----
  static int	connectDBStart(PGconn *conn);
  static int	connectDBComplete(PGconn *conn);
  static PGconn *makeEmptyPGconn(void);
+ static void fillPGconn(PGconn *conn, PQconninfoOption *connOptions);
  static void freePGconn(PGconn *conn);
  static void closePGconn(PGconn *conn);
  static PQconninfoOption *conninfo_parse(const char *conninfo,
*************** pgthreadlock_t pg_g_threadlock = default
*** 307,312 ****
--- 308,345 ----
   */
  
  /*
+  *		PQconnectdbParams
+  *
+  * establishes a connection to a postgres backend through the postmaster
+  * using connection information in a struct.
+  *
+  * The params struct string is defined as
+  *
+  *	   const char *params[] = {option1, value1, option2, value2, NULL}
+  *
+  * definitions.
+  *
+  * Returns a PGconn* which is needed for all subsequent libpq calls, or NULL
+  * if a memory allocation failed.
+  * If the status field of the connection returned is CONNECTION_BAD,
+  * then some fields may be null'ed out instead of having valid values.
+  *
+  * You should call PQfinish (if conn is not NULL) regardless of whether this
+  * call succeeded.
+  */
+ PGconn *
+ PQconnectdbParams(const char * const *params)
+ {
+ 	PGconn	   *conn = PQconnectStartParams(params);
+ 
+ 	if (conn && conn->status != CONNECTION_BAD)
+ 		(void) connectDBComplete(conn);
+ 
+ 	return conn;
+ 
+ }
+ 
+ /*
   *		PQconnectdb
   *
   * establishes a connection to a postgres backend through the postmaster
*************** PQconnectdb(const char *conninfo)
*** 340,351 ****
  }
  
  /*
!  *		PQconnectStart
   *
   * Begins the establishment of a connection to a postgres backend through the
!  * postmaster using connection information in a string.
   *
!  * See comment for PQconnectdb for the definition of the string format.
   *
   * Returns a PGconn*.  If NULL is returned, a malloc error has occurred, and
   * you should not attempt to proceed with this connection.	If the status
--- 373,384 ----
  }
  
  /*
!  *		PQconnectStartParams
   *
   * Begins the establishment of a connection to a postgres backend through the
!  * postmaster using connection information in a struct.
   *
!  * See comment for PQconnectdbParams for the definition of the string format.
   *
   * Returns a PGconn*.  If NULL is returned, a malloc error has occurred, and
   * you should not attempt to proceed with this connection.	If the status
*************** PQconnectdb(const char *conninfo)
*** 359,367 ****
   * See PQconnectPoll for more info.
   */
  PGconn *
! PQconnectStart(const char *conninfo)
  {
! 	PGconn	   *conn;
  
  	/*
  	 * Allocate memory for the conn structure
--- 392,403 ----
   * See PQconnectPoll for more info.
   */
  PGconn *
! PQconnectStartParams(const char * const *params)
  {
!     PGconn *conn;
! 	PQconninfoOption *options;
! 	PQconninfoOption *option;
! 	char	   *tmp;
  
  	/*
  	 * Allocate memory for the conn structure
*************** PQconnectStart(const char *conninfo)
*** 371,380 ****
  		return NULL;
  
  	/*
! 	 * Parse the conninfo string
  	 */
! 	if (!connectOptions1(conn, conninfo))
! 		return conn;
  
  	/*
  	 * Compute derived options
--- 407,554 ----
  		return NULL;
  
  	/*
!      * Make a working copy of PQconninfoOptions
!      */
! 	options = malloc(sizeof(PQconninfoOptions));
! 	if (options == NULL)
! 	{
! 		printfPQExpBuffer(&conn->errorMessage,
! 						  libpq_gettext("out of memory\n"));
! 		return NULL;
! 	}
! 	memcpy(options, PQconninfoOptions, sizeof(PQconninfoOptions));
! 
! 	/*
! 	 * Parse the params struct
  	 */
!     while(*params)
!     {
!         const char *pname = params[0];
!         const char *pvalue  = params[1];
!  
!         if (pvalue != NULL)
!         {
! 		    /*
! 		     * Now we have the name and the value. Search for the param record.
! 		     */
! 		    for (option = options; option->keyword != NULL; option++)
! 		    {
! 		    	if (strcmp(option->keyword, pname) == 0)
! 		    		break;
! 		    }
! 
!             /*
!              * Check for invalid connection option
!              */
! 		    if (option->keyword == NULL)
! 		    {
! 		    	printfPQExpBuffer(&conn->errorMessage,
! 		    				 libpq_gettext("invalid connection option \"%s\"\n"),
! 		    					  pname);
! 		    	PQconninfoFree(options);
! 		    	return NULL;
! 		    }
! 
! 		    /*
! 		     * Store the value
! 		     */
! 		    if (option->val)
! 		    	free(option->val);
! 		    option->val = strdup(pvalue);
! 		    if (!option->val)
! 		    {
! 		    	printfPQExpBuffer(&conn->errorMessage,
! 		    					  libpq_gettext("out of memory\n"));
! 		    	PQconninfoFree(options);
! 		    	return NULL;
! 		    }
!         }
! 
!         params+=2;
! 	}
! 
! 	/*
! 	 * If there's a service spec, use it to obtain any not-explicitly-given
! 	 * parameters.
! 	 */
! 	if (parseServiceInfo(options, &conn->errorMessage))
! 	{
! 		PQconninfoFree(options);
! 		return NULL;
! 	}
! 
! 	/*
! 	 * Get the fallback resources for parameters not specified in the conninfo
! 	 * string nor the service.
! 	 */
! 	for (option = options; option->keyword != NULL; option++)
! 	{
! 		if (option->val != NULL)
! 			continue;			/* Value was in conninfo or service */
! 
! 		/*
! 		 * Try to get the environment variable fallback
! 		 */
! 		if (option->envvar != NULL)
! 		{
! 			if ((tmp = getenv(option->envvar)) != NULL)
! 			{
! 				option->val = strdup(tmp);
! 				if (!option->val)
! 				{
! 					printfPQExpBuffer(&conn->errorMessage,
! 									  libpq_gettext("out of memory\n"));
! 					PQconninfoFree(options);
! 					return NULL;
! 				}
! 				continue;
! 			}
! 		}
! 
! 		/*
! 		 * No environment variable specified or this one isn't set - try
! 		 * compiled in
! 		 */
! 		if (option->compiled != NULL)
! 		{
! 			option->val = strdup(option->compiled);
! 			if (!option->val)
! 			{
! 				printfPQExpBuffer(&conn->errorMessage,
! 								  libpq_gettext("out of memory\n"));
! 				PQconninfoFree(options);
! 				return NULL;
! 			}
! 			continue;
! 		}
! 
! 		/*
! 		 * Special handling for user
! 		 */
! 		if (strcmp(option->keyword, "user") == 0)
! 		{
! 			option->val = pg_fe_getauthname(&conn->errorMessage);
! 			continue;
! 		}
! 	}
! 
! 	if (options == NULL)
! 	{
! 		conn->status = CONNECTION_BAD;
! 		/* errorMessage is already set */
! 		return false;
! 	}
! 
! 	/*
! 	 * Move option values into conn structure
! 	 */
!     fillPGconn(conn, options);
! 
! 
! 	/*
! 	 * Free the option info - all is in conn now
! 	 */
! 	PQconninfoFree(options);
  
  	/*
  	 * Compute derived options
*************** PQconnectStart(const char *conninfo)
*** 391,427 ****
  		conn->status = CONNECTION_BAD;
  	}
  
! 	return conn;
  }
  
  /*
!  *		connectOptions1
   *
!  * Internal subroutine to set up connection parameters given an already-
!  * created PGconn and a conninfo string.  Derived settings should be
!  * processed by calling connectOptions2 next.  (We split them because
!  * PQsetdbLogin overrides defaults in between.)
   *
!  * Returns true if OK, false if trouble (in which case errorMessage is set
!  * and so is conn->status).
   */
! static bool
! connectOptions1(PGconn *conn, const char *conninfo)
  {
! 	PQconninfoOption *connOptions;
! 	char	   *tmp;
  
  	/*
  	 * Parse the conninfo string
  	 */
! 	connOptions = conninfo_parse(conninfo, &conn->errorMessage, true);
! 	if (connOptions == NULL)
  	{
  		conn->status = CONNECTION_BAD;
- 		/* errorMessage is already set */
- 		return false;
  	}
  
  	/*
  	 * Move option values into conn structure
  	 *
--- 565,633 ----
  		conn->status = CONNECTION_BAD;
  	}
  
!     return conn;
  }
  
  /*
!  *		PQconnectStart
   *
!  * Begins the establishment of a connection to a postgres backend through the
!  * postmaster using connection information in a string.
   *
!  * See comment for PQconnectdb for the definition of the string format.
!  *
!  * Returns a PGconn*.  If NULL is returned, a malloc error has occurred, and
!  * you should not attempt to proceed with this connection.	If the status
!  * field of the connection returned is CONNECTION_BAD, an error has
!  * occurred. In this case you should call PQfinish on the result, (perhaps
!  * inspecting the error message first).  Other fields of the structure may not
!  * be valid if that occurs.  If the status field is not CONNECTION_BAD, then
!  * this stage has succeeded - call PQconnectPoll, using select(2) to see when
!  * this is necessary.
!  *
!  * See PQconnectPoll for more info.
   */
! PGconn *
! PQconnectStart(const char *conninfo)
  {
! 	PGconn	   *conn;
! 
! 	/*
! 	 * Allocate memory for the conn structure
! 	 */
! 	conn = makeEmptyPGconn();
! 	if (conn == NULL)
! 		return NULL;
  
  	/*
  	 * Parse the conninfo string
  	 */
! 	if (!connectOptions1(conn, conninfo))
! 		return conn;
! 
! 	/*
! 	 * Compute derived options
! 	 */
! 	if (!connectOptions2(conn))
! 		return conn;
! 
! 	/*
! 	 * Connect to the database
! 	 */
! 	if (!connectDBStart(conn))
  	{
+ 		/* Just in case we failed to set it in connectDBStart */
  		conn->status = CONNECTION_BAD;
  	}
  
+ 	return conn;
+ }
+ 
+ static void
+ fillPGconn(PGconn *conn, PQconninfoOption *connOptions)
+ {
+ 	char	   *tmp;
+ 
  	/*
  	 * Move option values into conn structure
  	 *
*************** connectOptions1(PGconn *conn, const char
*** 482,487 ****
--- 688,726 ----
  #endif
  	tmp = conninfo_getval(connOptions, "replication");
  	conn->replication = tmp ? strdup(tmp) : NULL;
+ }
+ 
+ /*
+  *		connectOptions1
+  *
+  * Internal subroutine to set up connection parameters given an already-
+  * created PGconn and a conninfo string.  Derived settings should be
+  * processed by calling connectOptions2 next.  (We split them because
+  * PQsetdbLogin overrides defaults in between.)
+  *
+  * Returns true if OK, false if trouble (in which case errorMessage is set
+  * and so is conn->status).
+  */
+ static bool
+ connectOptions1(PGconn *conn, const char *conninfo)
+ {
+ 	PQconninfoOption *connOptions;
+ 
+ 	/*
+ 	 * Parse the conninfo string
+ 	 */
+ 	connOptions = conninfo_parse(conninfo, &conn->errorMessage, true);
+ 	if (connOptions == NULL)
+ 	{
+ 		conn->status = CONNECTION_BAD;
+ 		/* errorMessage is already set */
+ 		return false;
+ 	}
+ 
+ 	/*
+ 	 * Move option values into conn structure
+ 	 */
+     fillPGconn(conn, connOptions);
  
  	/*
  	 * Free the option info - all is in conn now
Index: src/interfaces/libpq/libpq-fe.h
===================================================================
RCS file: /opt/cvsroot_postgresql/pgsql/src/interfaces/libpq/libpq-fe.h,v
retrieving revision 1.149
diff -c -p -c -r1.149 libpq-fe.h
*** src/interfaces/libpq/libpq-fe.h	21 Jan 2010 14:58:53 -0000	1.149
--- src/interfaces/libpq/libpq-fe.h	21 Jan 2010 19:32:11 -0000
*************** typedef struct pgresAttDesc
*** 226,235 ****
--- 226,237 ----
  /* make a new client connection to the backend */
  /* Asynchronous (non-blocking) */
  extern PGconn *PQconnectStart(const char *conninfo);
+ extern PGconn *PQconnectStartParams(const char * const *params);
  extern PostgresPollingStatusType PQconnectPoll(PGconn *conn);
  
  /* Synchronous (blocking) */
  extern PGconn *PQconnectdb(const char *conninfo);
+ extern PGconn *PQconnectdbParams(const char * const *params);
  extern PGconn *PQsetdbLogin(const char *pghost, const char *pgport,
  			 const char *pgoptions, const char *pgtty,
  			 const char *dbName,
libpqParams2_v3.patchtext/x-patch; name=libpqParams2_v3.patchDownload
Index: src/bin/psql/startup.c
===================================================================
RCS file: /opt/cvsroot_postgresql/pgsql/src/bin/psql/startup.c,v
retrieving revision 1.158
diff -c -p -c -r1.158 startup.c
*** src/bin/psql/startup.c	2 Jan 2010 16:57:59 -0000	1.158
--- src/bin/psql/startup.c	4 Jan 2010 21:27:12 -0000
*************** main(int argc, char *argv[])
*** 168,181 ****
  	if (pset.getPassword == TRI_YES)
  		password = simple_prompt(password_prompt, 100, false);
  
  	/* loop until we have a password if requested by backend */
  	do
  	{
! 		new_pass = false;
! 		pset.db = PQsetdbLogin(options.host, options.port, NULL, NULL,
! 					options.action == ACT_LIST_DB && options.dbname == NULL ?
! 							   "postgres" : options.dbname,
! 							   options.username, password);
  
  		if (PQstatus(pset.db) == CONNECTION_BAD &&
  			PQconnectionNeedsPassword(pset.db) &&
--- 168,200 ----
  	if (pset.getPassword == TRI_YES)
  		password = simple_prompt(password_prompt, 100, false);
  
+     const char *keywords[] = {
+         "host",
+         "port",
+         "dbname",
+         "user",
+         "password",
+         "application_name",
+         NULL
+         };
+         
  	/* loop until we have a password if requested by backend */
  	do
  	{
!         const char *values[] = {
!                   options.host,
!                   options.port,
!                   (options.action == ACT_LIST_DB && 
!                                options.dbname == NULL) ? "postgres" : options.dbname,
!                   options.username,
!                   password,
!                   pset.progname,
!                   NULL
!               };
!         
!         new_pass = false;
! 
!         pset.db = PQconnectdbParams(keywords, values);
  
  		if (PQstatus(pset.db) == CONNECTION_BAD &&
  			PQconnectionNeedsPassword(pset.db) &&
Index: src/interfaces/libpq/exports.txt
===================================================================
RCS file: /opt/cvsroot_postgresql/pgsql/src/interfaces/libpq/exports.txt,v
retrieving revision 1.24
diff -c -p -c -r1.24 exports.txt
*** src/interfaces/libpq/exports.txt	21 Jan 2010 14:58:53 -0000	1.24
--- src/interfaces/libpq/exports.txt	21 Jan 2010 19:41:09 -0000
*************** PQconninfoParse           152
*** 155,157 ****
--- 155,159 ----
  PQinitOpenSSL             153
  PQescapeLiteral           154
  PQescapeIdentifier        155
+ PQconnectdbParams         156
+ PQconnectStartParams      157
Index: src/interfaces/libpq/fe-connect.c
===================================================================
RCS file: /opt/cvsroot_postgresql/pgsql/src/interfaces/libpq/fe-connect.c,v
retrieving revision 1.384
diff -c -p -c -r1.384 fe-connect.c
*** src/interfaces/libpq/fe-connect.c	20 Jan 2010 21:15:21 -0000	1.384
--- src/interfaces/libpq/fe-connect.c	21 Jan 2010 19:32:31 -0000
*************** static bool connectOptions2(PGconn *conn
*** 262,267 ****
--- 262,268 ----
  static int	connectDBStart(PGconn *conn);
  static int	connectDBComplete(PGconn *conn);
  static PGconn *makeEmptyPGconn(void);
+ static void fillPGconn(PGconn *conn, PQconninfoOption *connOptions);
  static void freePGconn(PGconn *conn);
  static void closePGconn(PGconn *conn);
  static PQconninfoOption *conninfo_parse(const char *conninfo,
*************** pgthreadlock_t pg_g_threadlock = default
*** 307,312 ****
--- 308,347 ----
   */
  
  /*
+  *		PQconnectdbParams
+  *
+  * establishes a connection to a postgres backend through the postmaster
+  * using connection information in two structs.
+  *
+  * The keywords struct is defined as
+  *
+  *	   const char *params[] = {option1, option2, NULL}
+  *
+  * The values struct is defined as
+  *
+  *	   const char *params[] = {option1, option2, NULL}
+  *
+  * Returns a PGconn* which is needed for all subsequent libpq calls, or NULL
+  * if a memory allocation failed.
+  * If the status field of the connection returned is CONNECTION_BAD,
+  * then some fields may be null'ed out instead of having valid values.
+  *
+  * You should call PQfinish (if conn is not NULL) regardless of whether this
+  * call succeeded.
+  */
+ PGconn *
+ PQconnectdbParams(const char **keywords, const char **values)
+ {
+ 	PGconn	   *conn = PQconnectStartParams(keywords, values);
+ 
+ 	if (conn && conn->status != CONNECTION_BAD)
+ 		(void) connectDBComplete(conn);
+ 
+ 	return conn;
+ 
+ }
+ 
+ /*
   *		PQconnectdb
   *
   * establishes a connection to a postgres backend through the postmaster
*************** PQconnectdb(const char *conninfo)
*** 340,351 ****
  }
  
  /*
!  *		PQconnectStart
   *
   * Begins the establishment of a connection to a postgres backend through the
!  * postmaster using connection information in a string.
   *
!  * See comment for PQconnectdb for the definition of the string format.
   *
   * Returns a PGconn*.  If NULL is returned, a malloc error has occurred, and
   * you should not attempt to proceed with this connection.	If the status
--- 375,386 ----
  }
  
  /*
!  *		PQconnectStartParams
   *
   * Begins the establishment of a connection to a postgres backend through the
!  * postmaster using connection information in a struct.
   *
!  * See comment for PQconnectdbParams for the definition of the string format.
   *
   * Returns a PGconn*.  If NULL is returned, a malloc error has occurred, and
   * you should not attempt to proceed with this connection.	If the status
*************** PQconnectdb(const char *conninfo)
*** 359,367 ****
   * See PQconnectPoll for more info.
   */
  PGconn *
! PQconnectStart(const char *conninfo)
  {
! 	PGconn	   *conn;
  
  	/*
  	 * Allocate memory for the conn structure
--- 394,405 ----
   * See PQconnectPoll for more info.
   */
  PGconn *
! PQconnectStartParams(const char **keywords, const char **values)
  {
!     PGconn *conn;
! 	PQconninfoOption *options;
! 	PQconninfoOption *option;
! 	char	   *tmp;
  
  	/*
  	 * Allocate memory for the conn structure
*************** PQconnectStart(const char *conninfo)
*** 371,380 ****
  		return NULL;
  
  	/*
! 	 * Parse the conninfo string
  	 */
! 	if (!connectOptions1(conn, conninfo))
! 		return conn;
  
  	/*
  	 * Compute derived options
--- 409,557 ----
  		return NULL;
  
  	/*
!      * Make a working copy of PQconninfoOptions
!      */
! 	options = malloc(sizeof(PQconninfoOptions));
! 	if (options == NULL)
! 	{
! 		printfPQExpBuffer(&conn->errorMessage,
! 						  libpq_gettext("out of memory\n"));
! 		return NULL;
! 	}
! 	memcpy(options, PQconninfoOptions, sizeof(PQconninfoOptions));
! 
! 	/*
! 	 * Parse the params struct
  	 */
!     while(*keywords)
!     {
!         const char *pname = keywords[0];
!         const char *pvalue  = values[0];
!  
!         if (pvalue != NULL)
!         {
! 		    /*
! 		     * Now we have the name and the value. Search for the param record.
! 		     */
! 		    for (option = options; option->keyword != NULL; option++)
! 		    {
! 		    	if (strcmp(option->keyword, pname) == 0)
! 		    		break;
! 		    }
! 
!             /*
!              * Check for invalid connection option
!              */
! 		    if (option->keyword == NULL)
! 		    {
! 		    	printfPQExpBuffer(&conn->errorMessage,
! 		    				 libpq_gettext("invalid connection option \"%s\"\n"),
! 		    					  pname);
! 		    	PQconninfoFree(options);
! 		    	return NULL;
! 		    }
! 
! 		    /*
! 		     * Store the value
! 		     */
! 		    if (option->val)
! 		    	free(option->val);
! 		    option->val = strdup(pvalue);
! 		    if (!option->val)
! 		    {
! 		    	printfPQExpBuffer(&conn->errorMessage,
! 		    					  libpq_gettext("out of memory\n"));
! 		    	PQconninfoFree(options);
! 		    	return NULL;
! 		    }
!         }
! 
!         keywords+=1;
!         values+=1;
! 	}
! 
! 	/*
! 	 * If there's a service spec, use it to obtain any not-explicitly-given
! 	 * parameters.
! 	 */
! 	if (parseServiceInfo(options, &conn->errorMessage))
! 	{
! 		PQconninfoFree(options);
! 		return NULL;
! 	}
! 
! 	/*
! 	 * Get the fallback resources for parameters not specified in the conninfo
! 	 * string nor the service.
! 	 */
! 	for (option = options; option->keyword != NULL; option++)
! 	{
! 		if (option->val != NULL)
! 			continue;			/* Value was in conninfo or service */
! 
! 		/*
! 		 * Try to get the environment variable fallback
! 		 */
! 		if (option->envvar != NULL)
! 		{
! 			if ((tmp = getenv(option->envvar)) != NULL)
! 			{
! 				option->val = strdup(tmp);
! 				if (!option->val)
! 				{
! 					printfPQExpBuffer(&conn->errorMessage,
! 									  libpq_gettext("out of memory\n"));
! 					PQconninfoFree(options);
! 					return NULL;
! 				}
! 				continue;
! 			}
! 		}
! 
! 		/*
! 		 * No environment variable specified or this one isn't set - try
! 		 * compiled in
! 		 */
! 		if (option->compiled != NULL)
! 		{
! 			option->val = strdup(option->compiled);
! 			if (!option->val)
! 			{
! 				printfPQExpBuffer(&conn->errorMessage,
! 								  libpq_gettext("out of memory\n"));
! 				PQconninfoFree(options);
! 				return NULL;
! 			}
! 			continue;
! 		}
! 
! 		/*
! 		 * Special handling for user
! 		 */
! 		if (strcmp(option->keyword, "user") == 0)
! 		{
! 			option->val = pg_fe_getauthname(&conn->errorMessage);
! 			continue;
! 		}
! 	}
! 
! 	if (options == NULL)
! 	{
! 		conn->status = CONNECTION_BAD;
! 		/* errorMessage is already set */
! 		return false;
! 	}
! 
! 	/*
! 	 * Move option values into conn structure
! 	 */
!     fillPGconn(conn, options);
! 
! 
! 	/*
! 	 * Free the option info - all is in conn now
! 	 */
! 	PQconninfoFree(options);
  
  	/*
  	 * Compute derived options
*************** PQconnectStart(const char *conninfo)
*** 391,427 ****
  		conn->status = CONNECTION_BAD;
  	}
  
! 	return conn;
  }
  
  /*
!  *		connectOptions1
   *
!  * Internal subroutine to set up connection parameters given an already-
!  * created PGconn and a conninfo string.  Derived settings should be
!  * processed by calling connectOptions2 next.  (We split them because
!  * PQsetdbLogin overrides defaults in between.)
   *
!  * Returns true if OK, false if trouble (in which case errorMessage is set
!  * and so is conn->status).
   */
! static bool
! connectOptions1(PGconn *conn, const char *conninfo)
  {
! 	PQconninfoOption *connOptions;
! 	char	   *tmp;
  
  	/*
  	 * Parse the conninfo string
  	 */
! 	connOptions = conninfo_parse(conninfo, &conn->errorMessage, true);
! 	if (connOptions == NULL)
  	{
  		conn->status = CONNECTION_BAD;
- 		/* errorMessage is already set */
- 		return false;
  	}
  
  	/*
  	 * Move option values into conn structure
  	 *
--- 568,636 ----
  		conn->status = CONNECTION_BAD;
  	}
  
!     return conn;
  }
  
  /*
!  *		PQconnectStart
   *
!  * Begins the establishment of a connection to a postgres backend through the
!  * postmaster using connection information in a string.
   *
!  * See comment for PQconnectdb for the definition of the string format.
!  *
!  * Returns a PGconn*.  If NULL is returned, a malloc error has occurred, and
!  * you should not attempt to proceed with this connection.	If the status
!  * field of the connection returned is CONNECTION_BAD, an error has
!  * occurred. In this case you should call PQfinish on the result, (perhaps
!  * inspecting the error message first).  Other fields of the structure may not
!  * be valid if that occurs.  If the status field is not CONNECTION_BAD, then
!  * this stage has succeeded - call PQconnectPoll, using select(2) to see when
!  * this is necessary.
!  *
!  * See PQconnectPoll for more info.
   */
! PGconn *
! PQconnectStart(const char *conninfo)
  {
! 	PGconn	   *conn;
! 
! 	/*
! 	 * Allocate memory for the conn structure
! 	 */
! 	conn = makeEmptyPGconn();
! 	if (conn == NULL)
! 		return NULL;
  
  	/*
  	 * Parse the conninfo string
  	 */
! 	if (!connectOptions1(conn, conninfo))
! 		return conn;
! 
! 	/*
! 	 * Compute derived options
! 	 */
! 	if (!connectOptions2(conn))
! 		return conn;
! 
! 	/*
! 	 * Connect to the database
! 	 */
! 	if (!connectDBStart(conn))
  	{
+ 		/* Just in case we failed to set it in connectDBStart */
  		conn->status = CONNECTION_BAD;
  	}
  
+ 	return conn;
+ }
+ 
+ static void
+ fillPGconn(PGconn *conn, PQconninfoOption *connOptions)
+ {
+ 	char	   *tmp;
+ 
  	/*
  	 * Move option values into conn structure
  	 *
*************** connectOptions1(PGconn *conn, const char
*** 482,487 ****
--- 691,729 ----
  #endif
  	tmp = conninfo_getval(connOptions, "replication");
  	conn->replication = tmp ? strdup(tmp) : NULL;
+ }
+ 
+ /*
+  *		connectOptions1
+  *
+  * Internal subroutine to set up connection parameters given an already-
+  * created PGconn and a conninfo string.  Derived settings should be
+  * processed by calling connectOptions2 next.  (We split them because
+  * PQsetdbLogin overrides defaults in between.)
+  *
+  * Returns true if OK, false if trouble (in which case errorMessage is set
+  * and so is conn->status).
+  */
+ static bool
+ connectOptions1(PGconn *conn, const char *conninfo)
+ {
+ 	PQconninfoOption *connOptions;
+ 
+ 	/*
+ 	 * Parse the conninfo string
+ 	 */
+ 	connOptions = conninfo_parse(conninfo, &conn->errorMessage, true);
+ 	if (connOptions == NULL)
+ 	{
+ 		conn->status = CONNECTION_BAD;
+ 		/* errorMessage is already set */
+ 		return false;
+ 	}
+ 
+ 	/*
+ 	 * Move option values into conn structure
+ 	 */
+     fillPGconn(conn, connOptions);
  
  	/*
  	 * Free the option info - all is in conn now
Index: src/interfaces/libpq/libpq-fe.h
===================================================================
RCS file: /opt/cvsroot_postgresql/pgsql/src/interfaces/libpq/libpq-fe.h,v
retrieving revision 1.149
diff -c -p -c -r1.149 libpq-fe.h
*** src/interfaces/libpq/libpq-fe.h	21 Jan 2010 14:58:53 -0000	1.149
--- src/interfaces/libpq/libpq-fe.h	21 Jan 2010 19:32:32 -0000
*************** typedef struct pgresAttDesc
*** 226,235 ****
--- 226,237 ----
  /* make a new client connection to the backend */
  /* Asynchronous (non-blocking) */
  extern PGconn *PQconnectStart(const char *conninfo);
+ extern PGconn *PQconnectStartParams(const char **keywords, const char **values);
  extern PostgresPollingStatusType PQconnectPoll(PGconn *conn);
  
  /* Synchronous (blocking) */
  extern PGconn *PQconnectdb(const char *conninfo);
+ extern PGconn *PQconnectdbParams(const char **keywords, const char **values);
  extern PGconn *PQsetdbLogin(const char *pghost, const char *pgport,
  			 const char *pgoptions, const char *pgtty,
  			 const char *dbName,