Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * option.c
4 : : * FDW and GUC option handling for postgres_fdw
5 : : *
6 : : * Portions Copyright (c) 2012-2026, PostgreSQL Global Development Group
7 : : *
8 : : * IDENTIFICATION
9 : : * contrib/postgres_fdw/option.c
10 : : *
11 : : *-------------------------------------------------------------------------
12 : : */
13 : : #include "postgres.h"
14 : :
15 : : #include "access/reloptions.h"
16 : : #include "catalog/pg_foreign_server.h"
17 : : #include "catalog/pg_foreign_table.h"
18 : : #include "catalog/pg_user_mapping.h"
19 : : #include "commands/defrem.h"
20 : : #include "commands/extension.h"
21 : : #include "libpq/libpq-be.h"
22 : : #include "postgres_fdw.h"
23 : : #include "utils/guc.h"
24 : : #include "utils/memutils.h"
25 : : #include "utils/varlena.h"
26 : :
27 : : /*
28 : : * Describes the valid options for objects that this wrapper uses.
29 : : */
30 : : typedef struct PgFdwOption
31 : : {
32 : : const char *keyword;
33 : : Oid optcontext; /* OID of catalog in which option may appear */
34 : : bool is_libpq_opt; /* true if it's used in libpq */
35 : : } PgFdwOption;
36 : :
37 : : /*
38 : : * Valid options for postgres_fdw.
39 : : * Allocated and filled in InitPgFdwOptions.
40 : : */
41 : : static PgFdwOption *postgres_fdw_options;
42 : :
43 : : /*
44 : : * GUC parameters
45 : : */
46 : : char *pgfdw_application_name = NULL;
47 : :
48 : : /*
49 : : * Helper functions
50 : : */
51 : : static void InitPgFdwOptions(void);
52 : : static bool is_valid_option(const char *keyword, Oid context);
53 : : static bool is_libpq_option(const char *keyword);
54 : :
55 : : #include "miscadmin.h"
56 : :
57 : : /*
58 : : * Validate the generic options given to a FOREIGN DATA WRAPPER, SERVER,
59 : : * USER MAPPING or FOREIGN TABLE that uses postgres_fdw.
60 : : *
61 : : * Raise an ERROR if the option or its value is considered invalid.
62 : : */
4821 tgl@sss.pgh.pa.us 63 :CBC 26 : PG_FUNCTION_INFO_V1(postgres_fdw_validator);
64 : :
65 : : Datum
66 : 329 : postgres_fdw_validator(PG_FUNCTION_ARGS)
67 : : {
68 : 329 : List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
69 : 329 : Oid catalog = PG_GETARG_OID(1);
70 : : ListCell *cell;
71 : :
72 : : /* Build our options lists if we didn't yet. */
73 : 329 : InitPgFdwOptions();
74 : :
75 : : /*
76 : : * Check that only options supported by postgres_fdw, and allowed for the
77 : : * current object type, are given.
78 : : */
79 [ + + + + : 1064 : foreach(cell, options_list)
+ + ]
80 : : {
81 : 751 : DefElem *def = (DefElem *) lfirst(cell);
82 : :
83 [ + + ]: 751 : if (!is_valid_option(def->defname, catalog))
84 : : {
85 : : /*
86 : : * Unknown option specified, complain about it. Provide a hint
87 : : * with a valid option that looks similar, if there is one.
88 : : */
89 : : PgFdwOption *opt;
90 : : const char *closest_match;
91 : : ClosestMatchState match_state;
1327 peter@eisentraut.org 92 : 7 : bool has_valid_options = false;
93 : :
94 : 7 : initClosestMatch(&match_state, def->defname, 4);
4821 tgl@sss.pgh.pa.us 95 [ + + ]: 511 : for (opt = postgres_fdw_options; opt->keyword; opt++)
96 : : {
97 [ + + ]: 504 : if (catalog == opt->optcontext)
98 : : {
1327 peter@eisentraut.org 99 : 183 : has_valid_options = true;
100 : 183 : updateClosestMatch(&match_state, opt->keyword);
101 : : }
102 : : }
103 : :
104 : 7 : closest_match = getClosestMatch(&match_state);
4821 tgl@sss.pgh.pa.us 105 [ + - + + : 7 : ereport(ERROR,
+ + ]
106 : : (errcode(ERRCODE_FDW_INVALID_OPTION_NAME),
107 : : errmsg("invalid option \"%s\"", def->defname),
108 : : has_valid_options ? closest_match ?
109 : : errhint("Perhaps you meant the option \"%s\".",
110 : : closest_match) : 0 :
111 : : errhint("There are no valid options in this context.")));
112 : : }
113 : :
114 : : /*
115 : : * Validate option value, when we can do so without any context.
116 : : */
4710 117 [ + + ]: 744 : if (strcmp(def->defname, "use_remote_estimate") == 0 ||
1861 efujita@postgresql.o 118 [ + + ]: 705 : strcmp(def->defname, "updatable") == 0 ||
1853 fujii@postgresql.org 119 [ + + ]: 699 : strcmp(def->defname, "truncatable") == 0 ||
1859 120 [ + + ]: 694 : strcmp(def->defname, "async_capable") == 0 ||
1531 efujita@postgresql.o 121 [ + + ]: 690 : strcmp(def->defname, "parallel_commit") == 0 ||
1125 122 [ + + ]: 686 : strcmp(def->defname, "parallel_abort") == 0 ||
27 efujita@postgresql.o 123 [ + + ]:GNC 682 : strcmp(def->defname, "keep_connections") == 0 ||
124 [ + + ]: 664 : strcmp(def->defname, "restore_stats") == 0)
125 : : {
126 : : /* these accept only boolean values */
4821 tgl@sss.pgh.pa.us 127 :CBC 82 : (void) defGetBoolean(def);
128 : : }
129 [ + + ]: 662 : else if (strcmp(def->defname, "fdw_startup_cost") == 0 ||
130 [ + + ]: 653 : strcmp(def->defname, "fdw_tuple_cost") == 0)
131 : 15 : {
132 : : /*
133 : : * These must have a floating point value greater than or equal to
134 : : * zero.
135 : : */
136 : : char *value;
137 : : double real_val;
138 : : bool is_parsed;
139 : :
1763 fujii@postgresql.org 140 : 17 : value = defGetString(def);
141 : 17 : is_parsed = parse_real(value, &real_val, 0, NULL);
142 : :
143 [ + + ]: 17 : if (!is_parsed)
144 [ + - ]: 2 : ereport(ERROR,
145 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
146 : : errmsg("invalid value for floating point option \"%s\": %s",
147 : : def->defname, value)));
148 : :
149 [ - + ]: 15 : if (real_val < 0)
4821 tgl@sss.pgh.pa.us 150 [ # # ]:UBC 0 : ereport(ERROR,
151 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
152 : : errmsg("\"%s\" must be a floating point value greater than or equal to zero",
153 : : def->defname)));
154 : : }
3836 tgl@sss.pgh.pa.us 155 [ + + ]:CBC 645 : else if (strcmp(def->defname, "extensions") == 0)
156 : : {
157 : : /* check list syntax, warn about uninstalled extensions */
158 : 33 : (void) ExtractExtensionList(defGetString(def), true);
159 : : }
1763 fujii@postgresql.org 160 [ + + ]: 612 : else if (strcmp(def->defname, "fetch_size") == 0 ||
161 [ + + ]: 607 : strcmp(def->defname, "batch_size") == 0)
3744 rhaas@postgresql.org 162 : 24 : {
163 : : char *value;
164 : : int int_val;
165 : : bool is_parsed;
166 : :
1763 fujii@postgresql.org 167 : 26 : value = defGetString(def);
168 : 26 : is_parsed = parse_int(value, &int_val, 0, NULL);
169 : :
170 [ + + ]: 26 : if (!is_parsed)
3744 rhaas@postgresql.org 171 [ + - ]: 2 : ereport(ERROR,
172 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
173 : : errmsg("invalid value for integer option \"%s\": %s",
174 : : def->defname, value)));
175 : :
1763 fujii@postgresql.org 176 [ - + ]: 24 : if (int_val <= 0)
1931 tomas.vondra@postgre 177 [ # # ]:UBC 0 : ereport(ERROR,
178 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
179 : : errmsg("\"%s\" must be an integer value greater than zero",
180 : : def->defname)));
181 : : }
2328 andrew@dunslane.net 182 [ + + ]:CBC 586 : else if (strcmp(def->defname, "password_required") == 0)
183 : : {
2182 tgl@sss.pgh.pa.us 184 : 6 : bool pw_required = defGetBoolean(def);
185 : :
186 : : /*
187 : : * Only the superuser may set this option on a user mapping, or
188 : : * alter a user mapping on which this option is set. We allow a
189 : : * user to clear this option if it's set - in fact, we don't have
190 : : * a choice since we can't see the old mapping when validating an
191 : : * alter.
192 : : */
2328 andrew@dunslane.net 193 [ + + + + ]: 6 : if (!superuser() && !pw_required)
194 [ + - ]: 1 : ereport(ERROR,
195 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
196 : : errmsg("password_required=false is superuser-only"),
197 : : errhint("User mappings with the password_required option set to false may only be created or modified by the superuser.")));
198 : : }
2304 199 [ + + ]: 580 : else if (strcmp(def->defname, "sslcert") == 0 ||
200 [ + + ]: 570 : strcmp(def->defname, "sslkey") == 0)
201 : : {
202 : : /* similarly for sslcert / sslkey on user mapping */
203 [ + + + + ]: 20 : if (catalog == UserMappingRelationId && !superuser())
204 [ + - ]: 2 : ereport(ERROR,
205 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
206 : : errmsg("sslcert and sslkey are superuser-only"),
207 : : errhint("User mappings with the sslcert or sslkey options set may only be created or modified by the superuser.")));
208 : : }
1222 tomas.vondra@postgre 209 [ + + ]: 560 : else if (strcmp(def->defname, "analyze_sampling") == 0)
210 : : {
211 : : char *value;
212 : :
213 : 7 : value = defGetString(def);
214 : :
215 : : /* we recognize off/auto/random/system/bernoulli */
216 [ + + ]: 7 : if (strcmp(value, "off") != 0 &&
217 [ + + ]: 5 : strcmp(value, "auto") != 0 &&
218 [ + + ]: 4 : strcmp(value, "random") != 0 &&
219 [ + + ]: 3 : strcmp(value, "system") != 0 &&
220 [ + + ]: 2 : strcmp(value, "bernoulli") != 0)
221 [ + - ]: 1 : ereport(ERROR,
222 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
223 : : errmsg("invalid value for string option \"%s\": %s",
224 : : def->defname, value)));
225 : : }
226 : : }
227 : :
4821 tgl@sss.pgh.pa.us 228 : 313 : PG_RETURN_VOID();
229 : : }
230 : :
231 : : /*
232 : : * Initialize option lists.
233 : : */
234 : : static void
235 : 525 : InitPgFdwOptions(void)
236 : : {
237 : : int num_libpq_opts;
238 : : PQconninfoOption *libpq_options;
239 : : PQconninfoOption *lopt;
240 : : PgFdwOption *popt;
241 : :
242 : : /* non-libpq FDW-specific FDW options */
243 : : static const PgFdwOption non_libpq_options[] = {
244 : : {"schema_name", ForeignTableRelationId, false},
245 : : {"table_name", ForeignTableRelationId, false},
246 : : {"column_name", AttributeRelationId, false},
247 : : /* use_remote_estimate is available on both server and table */
248 : : {"use_remote_estimate", ForeignServerRelationId, false},
249 : : {"use_remote_estimate", ForeignTableRelationId, false},
250 : : /* cost factors */
251 : : {"fdw_startup_cost", ForeignServerRelationId, false},
252 : : {"fdw_tuple_cost", ForeignServerRelationId, false},
253 : : /* shippable extensions */
254 : : {"extensions", ForeignServerRelationId, false},
255 : : /* updatable is available on both server and table */
256 : : {"updatable", ForeignServerRelationId, false},
257 : : {"updatable", ForeignTableRelationId, false},
258 : : /* truncatable is available on both server and table */
259 : : {"truncatable", ForeignServerRelationId, false},
260 : : {"truncatable", ForeignTableRelationId, false},
261 : : /* fetch_size is available on both server and table */
262 : : {"fetch_size", ForeignServerRelationId, false},
263 : : {"fetch_size", ForeignTableRelationId, false},
264 : : /* batch_size is available on both server and table */
265 : : {"batch_size", ForeignServerRelationId, false},
266 : : {"batch_size", ForeignTableRelationId, false},
267 : : /* async_capable is available on both server and table */
268 : : {"async_capable", ForeignServerRelationId, false},
269 : : {"async_capable", ForeignTableRelationId, false},
270 : : {"parallel_commit", ForeignServerRelationId, false},
271 : : {"parallel_abort", ForeignServerRelationId, false},
272 : : {"keep_connections", ForeignServerRelationId, false},
273 : : {"password_required", UserMappingRelationId, false},
274 : :
275 : : /* sampling is available on both server and table */
276 : : {"analyze_sampling", ForeignServerRelationId, false},
277 : : {"analyze_sampling", ForeignTableRelationId, false},
278 : : /* restore_stats is available on both server and table */
279 : : {"restore_stats", ForeignServerRelationId, false},
280 : : {"restore_stats", ForeignTableRelationId, false},
281 : :
282 : : {"use_scram_passthrough", ForeignServerRelationId, false},
283 : : {"use_scram_passthrough", UserMappingRelationId, false},
284 : :
285 : : /*
286 : : * sslcert and sslkey are in fact libpq options, but we repeat them
287 : : * here to allow them to appear in both foreign server context (when
288 : : * we generate libpq options) and user mapping context (from here).
289 : : */
290 : : {"sslcert", UserMappingRelationId, true},
291 : : {"sslkey", UserMappingRelationId, true},
292 : :
293 : : /*
294 : : * gssdelegation is also a libpq option but should be allowed in a
295 : : * user mapping context too
296 : : */
297 : : {"gssdelegation", UserMappingRelationId, true},
298 : :
299 : : {NULL, InvalidOid, false}
300 : : };
301 : :
302 : : /* Prevent redundant initialization. */
303 [ + + ]: 525 : if (postgres_fdw_options)
304 : 485 : return;
305 : :
306 : : /*
307 : : * Get list of valid libpq options.
308 : : *
309 : : * To avoid unnecessary work, we get the list once and use it throughout
310 : : * the lifetime of this backend process. Hence, we'll allocate it in
311 : : * TopMemoryContext.
312 : : */
313 : 40 : libpq_options = PQconndefaults();
314 [ - + ]: 40 : if (!libpq_options) /* assume reason for failure is OOM */
4821 tgl@sss.pgh.pa.us 315 [ # # ]:UBC 0 : ereport(ERROR,
316 : : (errcode(ERRCODE_FDW_OUT_OF_MEMORY),
317 : : errmsg("out of memory"),
318 : : errdetail("Could not get libpq's default connection options.")));
319 : :
320 : : /* Count how many libpq options are available. */
4821 tgl@sss.pgh.pa.us 321 :CBC 40 : num_libpq_opts = 0;
322 [ + + ]: 2120 : for (lopt = libpq_options; lopt->keyword; lopt++)
323 : 2080 : num_libpq_opts++;
324 : :
325 : : /*
326 : : * Construct an array which consists of all valid options for
327 : : * postgres_fdw, by appending FDW-specific options to libpq options.
328 : : */
329 : 40 : postgres_fdw_options = (PgFdwOption *)
284 tgl@sss.pgh.pa.us 330 :GNC 40 : MemoryContextAlloc(TopMemoryContext,
331 : 40 : sizeof(PgFdwOption) * num_libpq_opts +
332 : : sizeof(non_libpq_options));
333 : :
4821 tgl@sss.pgh.pa.us 334 :CBC 40 : popt = postgres_fdw_options;
335 [ + + ]: 2120 : for (lopt = libpq_options; lopt->keyword; lopt++)
336 : : {
337 : : /* Hide debug options, as well as settings we override internally. */
338 [ + + ]: 2080 : if (strchr(lopt->dispchar, 'D') ||
339 [ + + ]: 1920 : strcmp(lopt->keyword, "fallback_application_name") == 0 ||
340 [ + + ]: 1880 : strcmp(lopt->keyword, "client_encoding") == 0)
341 : 240 : continue;
342 : :
343 : : /*
344 : : * Disallow OAuth options for now, since the builtin flow communicates
345 : : * on stderr by default and can't cache tokens yet.
346 : : */
371 jchampion@postgresql 347 [ + + ]: 1840 : if (strncmp(lopt->keyword, "oauth_", strlen("oauth_")) == 0)
348 : 200 : continue;
349 : :
284 tgl@sss.pgh.pa.us 350 :GNC 3280 : popt->keyword = MemoryContextStrdup(TopMemoryContext,
351 : 1640 : lopt->keyword);
352 : :
353 : : /*
354 : : * "user" and any secret options are allowed only on user mappings.
355 : : * Everything else is a server option.
356 : : */
4821 tgl@sss.pgh.pa.us 357 [ + + + + ]:CBC 1640 : if (strcmp(lopt->keyword, "user") == 0 || strchr(lopt->dispchar, '*'))
358 : 120 : popt->optcontext = UserMappingRelationId;
359 : : else
360 : 1520 : popt->optcontext = ForeignServerRelationId;
361 : 1640 : popt->is_libpq_opt = true;
362 : :
363 : 1640 : popt++;
364 : : }
365 : :
366 : : /* Done with libpq's output structure. */
284 tgl@sss.pgh.pa.us 367 :GNC 40 : PQconninfoFree(libpq_options);
368 : :
369 : : /* Append FDW-specific options and dummy terminator. */
4821 tgl@sss.pgh.pa.us 370 :CBC 40 : memcpy(popt, non_libpq_options, sizeof(non_libpq_options));
371 : : }
372 : :
373 : : /*
374 : : * Check whether the given option is one of the valid postgres_fdw options.
375 : : * context is the Oid of the catalog holding the object the option is for.
376 : : */
377 : : static bool
378 : 751 : is_valid_option(const char *keyword, Oid context)
379 : : {
380 : : PgFdwOption *opt;
381 : :
3240 382 [ - + ]: 751 : Assert(postgres_fdw_options); /* must be initialized already */
383 : :
4821 384 [ + + ]: 24693 : for (opt = postgres_fdw_options; opt->keyword; opt++)
385 : : {
386 [ + + + + ]: 24686 : if (context == opt->optcontext && strcmp(opt->keyword, keyword) == 0)
387 : 744 : return true;
388 : : }
389 : :
390 : 7 : return false;
391 : : }
392 : :
393 : : /*
394 : : * Check whether the given option is one of the valid libpq options.
395 : : */
396 : : static bool
397 : 411 : is_libpq_option(const char *keyword)
398 : : {
399 : : PgFdwOption *opt;
400 : :
3240 401 [ - + ]: 411 : Assert(postgres_fdw_options); /* must be initialized already */
402 : :
4821 403 [ + + ]: 11639 : for (opt = postgres_fdw_options; opt->keyword; opt++)
404 : : {
405 [ + + + + ]: 11524 : if (opt->is_libpq_opt && strcmp(opt->keyword, keyword) == 0)
406 : 296 : return true;
407 : : }
408 : :
409 : 115 : return false;
410 : : }
411 : :
412 : : /*
413 : : * Generate key-value arrays which include only libpq options from the
414 : : * given list (which can contain any kind of options). Caller must have
415 : : * allocated large-enough arrays. Returns number of options found.
416 : : */
417 : : int
418 : 196 : ExtractConnectionOptions(List *defelems, const char **keywords,
419 : : const char **values)
420 : : {
421 : : ListCell *lc;
422 : : int i;
423 : :
424 : : /* Build our options lists if we didn't yet. */
425 : 196 : InitPgFdwOptions();
426 : :
427 : 196 : i = 0;
428 [ + + + + : 607 : foreach(lc, defelems)
+ + ]
429 : : {
430 : 411 : DefElem *d = (DefElem *) lfirst(lc);
431 : :
432 [ + + ]: 411 : if (is_libpq_option(d->defname))
433 : : {
434 : 296 : keywords[i] = d->defname;
435 : 296 : values[i] = defGetString(d);
436 : 296 : i++;
437 : : }
438 : : }
439 : 196 : return i;
440 : : }
441 : :
442 : : /*
443 : : * Parse a comma-separated string and return a List of the OIDs of the
444 : : * extensions named in the string. If any names in the list cannot be
445 : : * found, report a warning if warnOnMissing is true, else just silently
446 : : * ignore them.
447 : : */
448 : : List *
3836 449 : 963 : ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
450 : : {
451 : 963 : List *extensionOids = NIL;
452 : : List *extlist;
453 : : ListCell *lc;
454 : :
455 : : /* SplitIdentifierString scribbles on its input, so pstrdup first */
456 [ + + ]: 963 : if (!SplitIdentifierString(pstrdup(extensionsString), ',', &extlist))
457 : : {
458 : : /* syntax error in name list */
459 [ + - ]: 1 : ereport(ERROR,
460 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
461 : : errmsg("parameter \"%s\" must be a list of extension names",
462 : : "extensions")));
463 : : }
464 : :
465 [ + - + + : 1925 : foreach(lc, extlist)
+ + ]
466 : : {
467 : 963 : const char *extension_name = (const char *) lfirst(lc);
468 : 963 : Oid extension_oid = get_extension_oid(extension_name, true);
469 : :
470 [ + + ]: 963 : if (OidIsValid(extension_oid))
471 : : {
472 : 961 : extensionOids = lappend_oid(extensionOids, extension_oid);
473 : : }
474 [ + - ]: 2 : else if (warnOnMissing)
475 : : {
476 [ + - ]: 2 : ereport(WARNING,
477 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
478 : : errmsg("extension \"%s\" is not installed",
479 : : extension_name)));
480 : : }
481 : : }
482 : :
483 : 962 : list_free(extlist);
484 : 962 : return extensionOids;
485 : : }
486 : :
487 : : /*
488 : : * Replace escape sequences beginning with % character in the given
489 : : * application_name with status information, and return it.
490 : : *
491 : : * This function always returns a palloc'd string, so the caller is
492 : : * responsible for pfreeing it.
493 : : */
494 : : char *
1593 fujii@postgresql.org 495 : 26 : process_pgfdw_appname(const char *appname)
496 : : {
497 : : const char *p;
498 : : StringInfoData buf;
499 : :
500 : 26 : initStringInfo(&buf);
501 : :
502 [ + + ]: 359 : for (p = appname; *p != '\0'; p++)
503 : : {
504 [ + + ]: 333 : if (*p != '%')
505 : : {
506 : : /* literal char, just copy */
507 : 324 : appendStringInfoChar(&buf, *p);
508 : 324 : continue;
509 : : }
510 : :
511 : : /* must be a '%', so skip to the next char */
512 : 9 : p++;
513 [ - + ]: 9 : if (*p == '\0')
1593 fujii@postgresql.org 514 :UBC 0 : break; /* format error - ignore it */
1593 fujii@postgresql.org 515 [ + + ]:CBC 9 : else if (*p == '%')
516 : : {
517 : : /* string contains %% */
518 : 1 : appendStringInfoChar(&buf, '%');
519 : 1 : continue;
520 : : }
521 : :
522 : : /* process the option */
523 [ + + + + : 8 : switch (*p)
+ + - ]
524 : : {
525 : 1 : case 'a':
526 : 1 : appendStringInfoString(&buf, application_name);
527 : 1 : break;
1537 528 : 2 : case 'c':
272 peter@eisentraut.org 529 : 2 : appendStringInfo(&buf, "%" PRIx64 ".%x", MyStartTime, MyProcPid);
1537 fujii@postgresql.org 530 : 2 : break;
531 : 2 : case 'C':
532 : 2 : appendStringInfoString(&buf, cluster_name);
533 : 2 : break;
1593 534 : 1 : case 'd':
1169 michael@paquier.xyz 535 [ + - ]: 1 : if (MyProcPort)
536 : : {
537 : 1 : const char *dbname = MyProcPort->database_name;
538 : :
539 [ + - ]: 1 : if (dbname)
540 : 1 : appendStringInfoString(&buf, dbname);
541 : : else
1169 michael@paquier.xyz 542 :UBC 0 : appendStringInfoString(&buf, "[unknown]");
543 : : }
1593 fujii@postgresql.org 544 :CBC 1 : break;
545 : 1 : case 'p':
546 : 1 : appendStringInfo(&buf, "%d", MyProcPid);
547 : 1 : break;
548 : 1 : case 'u':
1169 michael@paquier.xyz 549 [ + - ]: 1 : if (MyProcPort)
550 : : {
551 : 1 : const char *username = MyProcPort->user_name;
552 : :
553 [ + - ]: 1 : if (username)
554 : 1 : appendStringInfoString(&buf, username);
555 : : else
1169 michael@paquier.xyz 556 :UBC 0 : appendStringInfoString(&buf, "[unknown]");
557 : : }
1593 fujii@postgresql.org 558 :CBC 1 : break;
1593 fujii@postgresql.org 559 :UBC 0 : default:
560 : : /* format error - ignore it */
561 : 0 : break;
562 : : }
563 : : }
564 : :
1593 fujii@postgresql.org 565 :CBC 26 : return buf.data;
566 : : }
567 : :
568 : : /*
569 : : * Module load callback
570 : : */
571 : : void
1701 572 : 40 : _PG_init(void)
573 : : {
574 : : /*
575 : : * Unlike application_name GUC, don't set GUC_IS_NAME flag nor check_hook
576 : : * to allow postgres_fdw.application_name to be any string more than
577 : : * NAMEDATALEN characters and to include non-ASCII characters. Instead,
578 : : * remote server truncates application_name of remote connection to less
579 : : * than NAMEDATALEN and replaces any non-ASCII characters in it with a '?'
580 : : * character.
581 : : */
582 : 40 : DefineCustomStringVariable("postgres_fdw.application_name",
583 : : "Sets the application name to be used on the remote server.",
584 : : NULL,
585 : : &pgfdw_application_name,
586 : : NULL,
587 : : PGC_USERSET,
588 : : 0,
589 : : NULL,
590 : : NULL,
591 : : NULL);
592 : :
1534 tgl@sss.pgh.pa.us 593 : 40 : MarkGUCPrefixReserved("postgres_fdw");
1701 fujii@postgresql.org 594 : 40 : }
|