LCOV - code coverage report
Current view: top level - contrib/postgres_fdw - option.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 95.3 % 171 163
Test Date: 2026-05-30 11:16:13 Functions: 100.0 % 9 9
Legend: Lines:     hit not hit

            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              :  */
      63           22 : PG_FUNCTION_INFO_V1(postgres_fdw_validator);
      64              : 
      65              : Datum
      66          328 : postgres_fdw_validator(PG_FUNCTION_ARGS)
      67              : {
      68          328 :     List       *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
      69          328 :     Oid         catalog = PG_GETARG_OID(1);
      70              :     ListCell   *cell;
      71              : 
      72              :     /* Build our options lists if we didn't yet. */
      73          328 :     InitPgFdwOptions();
      74              : 
      75              :     /*
      76              :      * Check that only options supported by postgres_fdw, and allowed for the
      77              :      * current object type, are given.
      78              :      */
      79         1061 :     foreach(cell, options_list)
      80              :     {
      81          749 :         DefElem    *def = (DefElem *) lfirst(cell);
      82              : 
      83          749 :         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;
      92            7 :             bool        has_valid_options = false;
      93              : 
      94            7 :             initClosestMatch(&match_state, def->defname, 4);
      95          511 :             for (opt = postgres_fdw_options; opt->keyword; opt++)
      96              :             {
      97          504 :                 if (catalog == opt->optcontext)
      98              :                 {
      99          183 :                     has_valid_options = true;
     100          183 :                     updateClosestMatch(&match_state, opt->keyword);
     101              :                 }
     102              :             }
     103              : 
     104            7 :             closest_match = getClosestMatch(&match_state);
     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              :          */
     117          742 :         if (strcmp(def->defname, "use_remote_estimate") == 0 ||
     118          703 :             strcmp(def->defname, "updatable") == 0 ||
     119          697 :             strcmp(def->defname, "truncatable") == 0 ||
     120          692 :             strcmp(def->defname, "async_capable") == 0 ||
     121          688 :             strcmp(def->defname, "parallel_commit") == 0 ||
     122          684 :             strcmp(def->defname, "parallel_abort") == 0 ||
     123          680 :             strcmp(def->defname, "keep_connections") == 0 ||
     124          662 :             strcmp(def->defname, "restore_stats") == 0 ||
     125          659 :             strcmp(def->defname, "use_scram_passthrough") == 0)
     126              :         {
     127              :             /* these accept only boolean values */
     128           87 :             (void) defGetBoolean(def);
     129              :         }
     130          655 :         else if (strcmp(def->defname, "fdw_startup_cost") == 0 ||
     131          646 :                  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              : 
     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)
     151            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              :         }
     156          638 :         else if (strcmp(def->defname, "extensions") == 0)
     157              :         {
     158              :             /* check list syntax, warn about uninstalled extensions */
     159           33 :             (void) ExtractExtensionList(defGetString(def), true);
     160              :         }
     161          605 :         else if (strcmp(def->defname, "fetch_size") == 0 ||
     162          600 :                  strcmp(def->defname, "batch_size") == 0)
     163           24 :         {
     164              :             char       *value;
     165              :             int         int_val;
     166              :             bool        is_parsed;
     167              : 
     168           26 :             value = defGetString(def);
     169           26 :             is_parsed = parse_int(value, &int_val, 0, NULL);
     170              : 
     171           26 :             if (!is_parsed)
     172            2 :                 ereport(ERROR,
     173              :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     174              :                          errmsg("invalid value for integer option \"%s\": %s",
     175              :                                 def->defname, value)));
     176              : 
     177           24 :             if (int_val <= 0)
     178            0 :                 ereport(ERROR,
     179              :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     180              :                          errmsg("\"%s\" must be an integer value greater than zero",
     181              :                                 def->defname)));
     182              :         }
     183          579 :         else if (strcmp(def->defname, "password_required") == 0)
     184              :         {
     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              :              */
     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              :         }
     200          573 :         else if (strcmp(def->defname, "sslcert") == 0 ||
     201          563 :                  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              :         }
     210          553 :         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              : 
     229          312 :     PG_RETURN_VOID();
     230              : }
     231              : 
     232              : /*
     233              :  * Initialize option lists.
     234              :  */
     235              : static void
     236          520 : 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          520 :     if (postgres_fdw_options)
     305          487 :         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           33 :     libpq_options = PQconndefaults();
     315           33 :     if (!libpq_options)         /* assume reason for failure is OOM */
     316            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. */
     322           33 :     num_libpq_opts = 0;
     323         1749 :     for (lopt = libpq_options; lopt->keyword; lopt++)
     324         1716 :         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           33 :     postgres_fdw_options = (PgFdwOption *)
     331           33 :         MemoryContextAlloc(TopMemoryContext,
     332           33 :                            sizeof(PgFdwOption) * num_libpq_opts +
     333              :                            sizeof(non_libpq_options));
     334              : 
     335           33 :     popt = postgres_fdw_options;
     336         1749 :     for (lopt = libpq_options; lopt->keyword; lopt++)
     337              :     {
     338              :         /* Hide debug options, as well as settings we override internally. */
     339         1716 :         if (strchr(lopt->dispchar, 'D') ||
     340         1584 :             strcmp(lopt->keyword, "fallback_application_name") == 0 ||
     341         1551 :             strcmp(lopt->keyword, "client_encoding") == 0)
     342          198 :             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              :          */
     348         1518 :         if (strncmp(lopt->keyword, "oauth_", strlen("oauth_")) == 0)
     349          165 :             continue;
     350              : 
     351         2706 :         popt->keyword = MemoryContextStrdup(TopMemoryContext,
     352         1353 :                                             lopt->keyword);
     353              : 
     354              :         /*
     355              :          * "user" and any secret options are allowed only on user mappings.
     356              :          * Everything else is a server option.
     357              :          */
     358         1353 :         if (strcmp(lopt->keyword, "user") == 0 || strchr(lopt->dispchar, '*'))
     359           99 :             popt->optcontext = UserMappingRelationId;
     360              :         else
     361         1254 :             popt->optcontext = ForeignServerRelationId;
     362         1353 :         popt->is_libpq_opt = true;
     363              : 
     364         1353 :         popt++;
     365              :     }
     366              : 
     367              :     /* Done with libpq's output structure. */
     368           33 :     PQconninfoFree(libpq_options);
     369              : 
     370              :     /* Append FDW-specific options and dummy terminator. */
     371           33 :     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          749 : is_valid_option(const char *keyword, Oid context)
     380              : {
     381              :     PgFdwOption *opt;
     382              : 
     383              :     Assert(postgres_fdw_options);   /* must be initialized already */
     384              : 
     385        24822 :     for (opt = postgres_fdw_options; opt->keyword; opt++)
     386              :     {
     387        24815 :         if (context == opt->optcontext && strcmp(opt->keyword, keyword) == 0)
     388          742 :             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          406 : is_libpq_option(const char *keyword)
     399              : {
     400              :     PgFdwOption *opt;
     401              : 
     402              :     Assert(postgres_fdw_options);   /* must be initialized already */
     403              : 
     404        12141 :     for (opt = postgres_fdw_options; opt->keyword; opt++)
     405              :     {
     406        12018 :         if (opt->is_libpq_opt && strcmp(opt->keyword, keyword) == 0)
     407          283 :             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          192 : 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          192 :     InitPgFdwOptions();
     427              : 
     428          192 :     i = 0;
     429          598 :     foreach(lc, defelems)
     430              :     {
     431          406 :         DefElem    *d = (DefElem *) lfirst(lc);
     432              : 
     433          406 :         if (is_libpq_option(d->defname))
     434              :         {
     435          283 :             keywords[i] = d->defname;
     436          283 :             values[i] = defGetString(d);
     437          283 :             i++;
     438              :         }
     439              :     }
     440          192 :     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 *
     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 *
     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')
     515            0 :             break;              /* format error - ignore it */
     516            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;
     529            2 :             case 'c':
     530            2 :                 appendStringInfo(&buf, "%" PRIx64 ".%x", MyStartTime, MyProcPid);
     531            2 :                 break;
     532            2 :             case 'C':
     533            2 :                 appendStringInfoString(&buf, cluster_name);
     534            2 :                 break;
     535            1 :             case 'd':
     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
     543            0 :                         appendStringInfoString(&buf, "[unknown]");
     544              :                 }
     545            1 :                 break;
     546            1 :             case 'p':
     547            1 :                 appendStringInfo(&buf, "%d", MyProcPid);
     548            1 :                 break;
     549            1 :             case 'u':
     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
     557            0 :                         appendStringInfoString(&buf, "[unknown]");
     558              :                 }
     559            1 :                 break;
     560            0 :             default:
     561              :                 /* format error - ignore it */
     562            0 :                 break;
     563              :         }
     564              :     }
     565              : 
     566           28 :     return buf.data;
     567              : }
     568              : 
     569              : /*
     570              :  * Module load callback
     571              :  */
     572              : void
     573           33 : _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           33 :     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              : 
     594           33 :     MarkGUCPrefixReserved("postgres_fdw");
     595           33 : }
        

Generated by: LCOV version 2.0-1