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

Generated by: LCOV version 2.3.2-1