diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c index 82aa14a65d..da62b0f604 100644 --- a/contrib/postgres_fdw/connection.c +++ b/contrib/postgres_fdw/connection.c @@ -349,16 +349,20 @@ connect_pg_server(ForeignServer *server, UserMapping *user) const char **keywords; const char **values; int n; + int i; + StringInfoData buf; /* * Construct connection params from generic options of ForeignServer * and UserMapping. (Some of them might not be libpq options, in - * which case we'll just waste a few array slots.) Add 3 extra slots - * for fallback_application_name, client_encoding, end marker. + * which case we'll just waste a few array slots.) Add 4 extra slots + * for application_name, fallback_application_name, client_encoding, + * end marker. */ - n = list_length(server->options) + list_length(user->options) + 3; + n = list_length(server->options) + list_length(user->options) + 4; keywords = (const char **) palloc(n * sizeof(char *)); values = (const char **) palloc(n * sizeof(char *)); + initStringInfo(&buf); n = 0; n += ExtractConnectionOptions(server->options, @@ -366,7 +370,26 @@ connect_pg_server(ForeignServer *server, UserMapping *user) n += ExtractConnectionOptions(user->options, keywords + n, values + n); - /* Use "postgres_fdw" as fallback_application_name. */ + /* Use GUC paramter if set */ + if (pgfdw_application_name && *pgfdw_application_name != '\0') + { + keywords[n] = "application_name"; + values[n] = pgfdw_application_name; + n++; + } + + /* Search application_name and replace it */ + for (i = n - 1; i >= 0; i--) + { + if (strcmp(keywords[i], "application_name") == 0) + { + parse_application_name(&buf, values[i]); + values[i] = buf.data; + break; + } + } + + /* Use "postgres_fdw" as fallback_application_name */ keywords[n] = "fallback_application_name"; values[n] = "postgres_fdw"; n++; @@ -437,6 +460,7 @@ connect_pg_server(ForeignServer *server, UserMapping *user) pfree(keywords); pfree(values); + pfree(buf.data); } PG_CATCH(); { diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c index c574ca2cf3..b9dfedf6c7 100644 --- a/contrib/postgres_fdw/option.c +++ b/contrib/postgres_fdw/option.c @@ -1,7 +1,7 @@ /*------------------------------------------------------------------------- * * option.c - * FDW option handling for postgres_fdw + * FDW and GUC option handling for postgres_fdw * * Portions Copyright (c) 2012-2021, PostgreSQL Global Development Group * @@ -18,6 +18,9 @@ #include "catalog/pg_user_mapping.h" #include "commands/defrem.h" #include "commands/extension.h" +#include "common/string.h" +#include "lib/stringinfo.h" +#include "libpq/libpq-be.h" #include "postgres_fdw.h" #include "utils/builtins.h" #include "utils/guc.h" @@ -52,6 +55,18 @@ static void InitPgFdwOptions(void); static bool is_valid_option(const char *keyword, Oid context); static bool is_libpq_option(const char *keyword); +/* + * GUC parameters + */ +char* pgfdw_application_name = NULL; + +/* + * _PG_init() and check hook + */ + +static bool check_pgfdw_application_name(char **newval, void **extra, GucSource source); +void _PG_init(void); + #include "miscadmin.h" /* @@ -435,3 +450,118 @@ ExtractExtensionList(const char *extensionsString, bool warnOnMissing) list_free(extlist); return extensionOids; } + +/* + * parse application_name and set escaped string. + * This function is almost same as log_line_prefix(), but + * accepted escape sequence is different. + * + * buf must be initialized. + */ +void +parse_application_name(StringInfo buf, const char *name) +{ + int padding; + const char *p; + + for(p = name; *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; + } + if (*p > '9') + padding = 0; + else if ((p = process_padding(p, &padding)) == NULL) + break; + + /* process the option */ + switch (*p) + { + case 'a': + { + const char *appname = application_name; + + if (*appname == '\0') + appname = "[unknown]"; + if (padding != 0) + appendStringInfo(buf, "%*s", padding, appname); + else + appendStringInfoString(buf, appname); + } + break; + case 'u': + { + const char *username = MyProcPort->user_name; + + if (padding != 0) + appendStringInfo(buf, "%*s", padding, username); + else + appendStringInfoString(buf, username); + } + break; + case 'd': + { + const char *dbname = MyProcPort->database_name; + + if (padding != 0) + appendStringInfo(buf, "%*s", padding, dbname); + else + appendStringInfoString(buf, dbname); + } + break; + case 'p': + if (padding != 0) + appendStringInfo(buf, "%*d", padding, MyProcPid); + else + appendStringInfo(buf, "%d", MyProcPid); + break; + default: + /* format error - ignore it */ + break; + } + } +} + +/* + * Completely same as server-side. + */ +static bool +check_pgfdw_application_name(char **newval, void **extra, GucSource source) +{ + /* Only allow clean ASCII chars in the application name */ + if (*newval) + pg_clean_ascii(*newval); + return true; +} + +/* + * Define GUC parameters. + */ +void +_PG_init(void) +{ + DefineCustomStringVariable("postgres_fdw.application_name", + "Sets the application name. This is used when connects to the remote server.", + NULL, + &pgfdw_application_name, + NULL, + PGC_USERSET, + GUC_IS_NAME, + check_pgfdw_application_name, + NULL, + NULL); +} diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 9d443baf02..a4720f9e0a 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -318,6 +318,8 @@ typedef struct */ PG_FUNCTION_INFO_V1(postgres_fdw_handler); +void _PG_init(void); + /* * FDW callback routines */ diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h index ca83306af9..1168ad4b52 100644 --- a/contrib/postgres_fdw/postgres_fdw.h +++ b/contrib/postgres_fdw/postgres_fdw.h @@ -158,6 +158,8 @@ extern int ExtractConnectionOptions(List *defelems, const char **values); extern List *ExtractExtensionList(const char *extensionsString, bool warnOnMissing); +extern void parse_application_name(StringInfo buf, const char *name); +extern char *pgfdw_application_name; /* in deparse.c */ extern void classifyConditions(PlannerInfo *root, diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml index 0075bc3dbb..437aed647a 100644 --- a/doc/src/sgml/postgres-fdw.sgml +++ b/doc/src/sgml/postgres-fdw.sgml @@ -105,6 +105,66 @@ of columns to the remote table is by name, not position. + + Configuration Parameters + + + + postgres_fdw.application_name (string) + + + postgres_fdw.application_name configuration parameter + + + + + + Specifies a value for + configuration parameter. This value is used only when a backend process + starts to establish the remote connection. + + + + Same as , this is a + printf-style string. Accepted escapes are + bit different from , + but padding can be used like as it. + + + + + + + Escape + Effect + + + + + %a + Application name + + + %u + Local user name + + + %d + Local database name + + + %p + Local backend process ID + + + + + + + + + + FDW Options of postgres_fdw