mirror of
https://github.com/postgres/postgres.git
synced 2025-06-14 18:42:34 +03:00
postgres_fdw: Allow postgres_fdw.application_name to include escape sequences.
application_name that used when postgres_fdw establishes a connection to a foreign server can be specified in either or both a connection parameter of a server object and GUC postgres_fdw.application_name. This commit allows those parameters to include escape sequences that begins with % character. Then postgres_fdw replaces those escape sequences with status information. For example, %d and %u are replaced with user name and database name in local server, respectively. This feature enables us to add information more easily to track remote transactions or queries, into application_name of a remote connection. Author: Hayato Kuroda Reviewed-by: Kyotaro Horiguchi, Masahiro Ikeda, Hou Zhijie, Fujii Masao Discussion: https://postgr.es/m/TYAPR01MB5866FAE71C66547C64616584F5EB9@TYAPR01MB5866.jpnprd01.prod.outlook.com Discussion: https://postgr.es/m/TYCPR01MB5870D1E8B949DAF6D3B84E02F5F29@TYCPR01MB5870.jpnprd01.prod.outlook.com
This commit is contained in:
@ -348,6 +348,7 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
|
||||
{
|
||||
const char **keywords;
|
||||
const char **values;
|
||||
char *appname = NULL;
|
||||
int n;
|
||||
|
||||
/*
|
||||
@ -383,6 +384,39 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
|
||||
n++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Search the parameter arrays to find application_name setting, and
|
||||
* replace escape sequences in it with status information if found.
|
||||
* The arrays are searched backwards because the last value is used if
|
||||
* application_name is repeatedly set.
|
||||
*/
|
||||
for (int i = n - 1; i >= 0; i--)
|
||||
{
|
||||
if (strcmp(keywords[i], "application_name") == 0 &&
|
||||
*(values[i]) != '\0')
|
||||
{
|
||||
/*
|
||||
* Use this application_name setting if it's not empty string
|
||||
* even after any escape sequences in it are replaced.
|
||||
*/
|
||||
appname = process_pgfdw_appname(values[i]);
|
||||
if (appname[0] != '\0')
|
||||
{
|
||||
values[i] = appname;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* This empty application_name is not used, so we set
|
||||
* values[i] to NULL and keep searching the array to find the
|
||||
* next one.
|
||||
*/
|
||||
values[i] = NULL;
|
||||
pfree(appname);
|
||||
appname = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Use "postgres_fdw" as fallback_application_name */
|
||||
keywords[n] = "fallback_application_name";
|
||||
values[n] = "postgres_fdw";
|
||||
@ -452,6 +486,8 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
|
||||
/* Prepare new session for use */
|
||||
configure_remote_session(conn);
|
||||
|
||||
if (appname != NULL)
|
||||
pfree(appname);
|
||||
pfree(keywords);
|
||||
pfree(values);
|
||||
}
|
||||
|
@ -10825,3 +10825,35 @@ ERROR: invalid value for integer option "batch_size": 100$%$#$#
|
||||
ALTER FOREIGN DATA WRAPPER postgres_fdw OPTIONS (nonexistent 'fdw');
|
||||
ERROR: invalid option "nonexistent"
|
||||
HINT: There are no valid options in this context.
|
||||
-- ===================================================================
|
||||
-- test postgres_fdw.application_name GUC
|
||||
-- ===================================================================
|
||||
-- Close all the existing cached connections so that new connection
|
||||
-- will be established with new setting of postgres_fdw.application_name.
|
||||
SELECT 1 FROM postgres_fdw_disconnect_all();
|
||||
?column?
|
||||
----------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
-- Add some escape sequences into postgres_fdw.application_name
|
||||
-- so as to test that they are replaced with status information expectedly.
|
||||
SET postgres_fdw.application_name TO '%a%u%d%p%%';
|
||||
BEGIN;
|
||||
SELECT 1 FROM ft6 LIMIT 1;
|
||||
?column?
|
||||
----------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT count(*) FROM pg_stat_activity
|
||||
WHERE application_name = current_setting('application_name') ||
|
||||
CURRENT_USER || current_database() || pg_backend_pid() || '%';
|
||||
count
|
||||
-------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
COMMIT;
|
||||
--Clean up
|
||||
RESET postgres_fdw.application_name;
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "catalog/pg_user_mapping.h"
|
||||
#include "commands/defrem.h"
|
||||
#include "commands/extension.h"
|
||||
#include "libpq/libpq-be.h"
|
||||
#include "postgres_fdw.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/guc.h"
|
||||
@ -445,6 +446,67 @@ ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
|
||||
return extensionOids;
|
||||
}
|
||||
|
||||
/*
|
||||
* Replace escape sequences beginning with % character in the given
|
||||
* application_name with status information, and return it.
|
||||
*
|
||||
* This function always returns a palloc'd string, so the caller is
|
||||
* responsible for pfreeing it.
|
||||
*/
|
||||
char *
|
||||
process_pgfdw_appname(const char *appname)
|
||||
{
|
||||
const char *p;
|
||||
StringInfoData buf;
|
||||
|
||||
Assert(MyProcPort != NULL);
|
||||
|
||||
initStringInfo(&buf);
|
||||
|
||||
for (p = appname; *p != '\0'; p++)
|
||||
{
|
||||
if (*p != '%')
|
||||
{
|
||||
/* literal char, just copy */
|
||||
appendStringInfoChar(&buf, *p);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* must be a '%', so skip to the next char */
|
||||
p++;
|
||||
if (*p == '\0')
|
||||
break; /* format error - ignore it */
|
||||
else if (*p == '%')
|
||||
{
|
||||
/* string contains %% */
|
||||
appendStringInfoChar(&buf, '%');
|
||||
continue;
|
||||
}
|
||||
|
||||
/* process the option */
|
||||
switch (*p)
|
||||
{
|
||||
case 'a':
|
||||
appendStringInfoString(&buf, application_name);
|
||||
break;
|
||||
case 'd':
|
||||
appendStringInfoString(&buf, MyProcPort->database_name);
|
||||
break;
|
||||
case 'p':
|
||||
appendStringInfo(&buf, "%d", MyProcPid);
|
||||
break;
|
||||
case 'u':
|
||||
appendStringInfoString(&buf, MyProcPort->user_name);
|
||||
break;
|
||||
default:
|
||||
/* format error - ignore it */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return buf.data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module load callback
|
||||
*/
|
||||
|
@ -158,6 +158,7 @@ extern int ExtractConnectionOptions(List *defelems,
|
||||
const char **values);
|
||||
extern List *ExtractExtensionList(const char *extensionsString,
|
||||
bool warnOnMissing);
|
||||
extern char *process_pgfdw_appname(const char *appname);
|
||||
extern char *pgfdw_application_name;
|
||||
|
||||
/* in deparse.c */
|
||||
|
@ -3452,3 +3452,24 @@ CREATE FOREIGN TABLE inv_bsz (c1 int )
|
||||
|
||||
-- No option is allowed to be specified at foreign data wrapper level
|
||||
ALTER FOREIGN DATA WRAPPER postgres_fdw OPTIONS (nonexistent 'fdw');
|
||||
|
||||
-- ===================================================================
|
||||
-- test postgres_fdw.application_name GUC
|
||||
-- ===================================================================
|
||||
-- Close all the existing cached connections so that new connection
|
||||
-- will be established with new setting of postgres_fdw.application_name.
|
||||
SELECT 1 FROM postgres_fdw_disconnect_all();
|
||||
|
||||
-- Add some escape sequences into postgres_fdw.application_name
|
||||
-- so as to test that they are replaced with status information expectedly.
|
||||
SET postgres_fdw.application_name TO '%a%u%d%p%%';
|
||||
|
||||
BEGIN;
|
||||
SELECT 1 FROM ft6 LIMIT 1;
|
||||
SELECT count(*) FROM pg_stat_activity
|
||||
WHERE application_name = current_setting('application_name') ||
|
||||
CURRENT_USER || current_database() || pg_backend_pid() || '%';
|
||||
COMMIT;
|
||||
|
||||
--Clean up
|
||||
RESET postgres_fdw.application_name;
|
||||
|
Reference in New Issue
Block a user