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