LCOV - code coverage report
Current view: top level - contrib/postgres_fdw - option.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 95.3 % 171 163
Test Date: 2026-07-15 11:15:40 Functions: 100.0 % 9 9
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 85.7 % 175 150

             Branch data     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                 :          24 : PG_FUNCTION_INFO_V1(postgres_fdw_validator);
      64                 :             : 
      65                 :             : Datum
      66                 :         336 : postgres_fdw_validator(PG_FUNCTION_ARGS)
      67                 :             : {
      68                 :         336 :     List       *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
      69                 :         336 :     Oid         catalog = PG_GETARG_OID(1);
      70                 :             :     ListCell   *cell;
      71                 :             : 
      72                 :             :     /* Build our options lists if we didn't yet. */
      73                 :         336 :     InitPgFdwOptions();
      74                 :             : 
      75                 :             :     /*
      76                 :             :      * Check that only options supported by postgres_fdw, and allowed for the
      77                 :             :      * current object type, are given.
      78                 :             :      */
      79   [ +  +  +  +  :        1078 :     foreach(cell, options_list)
                   +  + ]
      80                 :             :     {
      81                 :         758 :         DefElem    *def = (DefElem *) lfirst(cell);
      82                 :             : 
      83         [ +  + ]:         758 :         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         [ +  + ]:         751 :         if (strcmp(def->defname, "use_remote_estimate") == 0 ||
     118         [ +  + ]:         712 :             strcmp(def->defname, "updatable") == 0 ||
     119         [ +  + ]:         706 :             strcmp(def->defname, "truncatable") == 0 ||
     120         [ +  + ]:         701 :             strcmp(def->defname, "async_capable") == 0 ||
     121         [ +  + ]:         697 :             strcmp(def->defname, "parallel_commit") == 0 ||
     122         [ +  + ]:         693 :             strcmp(def->defname, "parallel_abort") == 0 ||
     123         [ +  + ]:         689 :             strcmp(def->defname, "keep_connections") == 0 ||
     124         [ +  + ]:         671 :             strcmp(def->defname, "restore_stats") == 0 ||
     125         [ +  + ]:         668 :             strcmp(def->defname, "use_scram_passthrough") == 0)
     126                 :             :         {
     127                 :             :             /* these accept only boolean values */
     128                 :          87 :             (void) defGetBoolean(def);
     129                 :             :         }
     130         [ +  + ]:         664 :         else if (strcmp(def->defname, "fdw_startup_cost") == 0 ||
     131         [ +  + ]:         655 :                  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         [ +  + ]:         647 :         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         [ +  + ]:         614 :         else if (strcmp(def->defname, "fetch_size") == 0 ||
     162         [ +  + ]:         609 :                  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         [ +  + ]:         588 :         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         [ +  + ]:         582 :         else if (strcmp(def->defname, "sslcert") == 0 ||
     201         [ +  + ]:         572 :                  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         [ +  + ]:         562 :         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                 :         320 :     PG_RETURN_VOID();
     230                 :             : }
     231                 :             : 
     232                 :             : /*
     233                 :             :  * Initialize option lists.
     234                 :             :  */
     235                 :             : static void
     236                 :         530 : 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         [ +  + ]:         530 :     if (postgres_fdw_options)
     305                 :         494 :         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                 :          36 :     libpq_options = PQconndefaults();
     315         [ -  + ]:          36 :     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                 :          36 :     num_libpq_opts = 0;
     323         [ +  + ]:        1908 :     for (lopt = libpq_options; lopt->keyword; lopt++)
     324                 :        1872 :         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                 :          36 :     postgres_fdw_options = (PgFdwOption *)
     331                 :          36 :         MemoryContextAlloc(TopMemoryContext,
     332                 :          36 :                            sizeof(PgFdwOption) * num_libpq_opts +
     333                 :             :                            sizeof(non_libpq_options));
     334                 :             : 
     335                 :          36 :     popt = postgres_fdw_options;
     336         [ +  + ]:        1908 :     for (lopt = libpq_options; lopt->keyword; lopt++)
     337                 :             :     {
     338                 :             :         /* Hide debug options, as well as settings we override internally. */
     339         [ +  + ]:        1872 :         if (strchr(lopt->dispchar, 'D') ||
     340         [ +  + ]:        1728 :             strcmp(lopt->keyword, "fallback_application_name") == 0 ||
     341         [ +  + ]:        1692 :             strcmp(lopt->keyword, "client_encoding") == 0)
     342                 :         216 :             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         [ +  + ]:        1656 :         if (strncmp(lopt->keyword, "oauth_", strlen("oauth_")) == 0)
     349                 :         180 :             continue;
     350                 :             : 
     351                 :        2952 :         popt->keyword = MemoryContextStrdup(TopMemoryContext,
     352                 :        1476 :                                             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   [ +  +  +  + ]:        1476 :         if (strcmp(lopt->keyword, "user") == 0 || strchr(lopt->dispchar, '*'))
     359                 :         108 :             popt->optcontext = UserMappingRelationId;
     360                 :             :         else
     361                 :        1368 :             popt->optcontext = ForeignServerRelationId;
     362                 :        1476 :         popt->is_libpq_opt = true;
     363                 :             : 
     364                 :        1476 :         popt++;
     365                 :             :     }
     366                 :             : 
     367                 :             :     /* Done with libpq's output structure. */
     368                 :          36 :     PQconninfoFree(libpq_options);
     369                 :             : 
     370                 :             :     /* Append FDW-specific options and dummy terminator. */
     371                 :          36 :     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                 :         758 : is_valid_option(const char *keyword, Oid context)
     380                 :             : {
     381                 :             :     PgFdwOption *opt;
     382                 :             : 
     383                 :             :     Assert(postgres_fdw_options);   /* must be initialized already */
     384                 :             : 
     385         [ +  + ]:       25107 :     for (opt = postgres_fdw_options; opt->keyword; opt++)
     386                 :             :     {
     387   [ +  +  +  + ]:       25100 :         if (context == opt->optcontext && strcmp(opt->keyword, keyword) == 0)
     388                 :         751 :             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                 :         387 : is_libpq_option(const char *keyword)
     399                 :             : {
     400                 :             :     PgFdwOption *opt;
     401                 :             : 
     402                 :             :     Assert(postgres_fdw_options);   /* must be initialized already */
     403                 :             : 
     404         [ +  + ]:       11537 :     for (opt = postgres_fdw_options; opt->keyword; opt++)
     405                 :             :     {
     406   [ +  +  +  + ]:       11417 :         if (opt->is_libpq_opt && strcmp(opt->keyword, keyword) == 0)
     407                 :         267 :             return true;
     408                 :             :     }
     409                 :             : 
     410                 :         120 :     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                 :         194 : 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                 :         194 :     InitPgFdwOptions();
     427                 :             : 
     428                 :         194 :     i = 0;
     429   [ +  +  +  +  :         581 :     foreach(lc, defelems)
                   +  + ]
     430                 :             :     {
     431                 :         387 :         DefElem    *d = (DefElem *) lfirst(lc);
     432                 :             : 
     433         [ +  + ]:         387 :         if (is_libpq_option(d->defname))
     434                 :             :         {
     435                 :         267 :             keywords[i] = d->defname;
     436                 :         267 :             values[i] = defGetString(d);
     437                 :         267 :             i++;
     438                 :             :         }
     439                 :             :     }
     440                 :         194 :     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                 :         975 : ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
     451                 :             : {
     452                 :         975 :     List       *extensionOids = NIL;
     453                 :             :     List       *extlist;
     454                 :             :     ListCell   *lc;
     455                 :             : 
     456                 :             :     /* SplitIdentifierString scribbles on its input, so pstrdup first */
     457         [ +  + ]:         975 :     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   [ +  -  +  +  :        1949 :     foreach(lc, extlist)
                   +  + ]
     467                 :             :     {
     468                 :         975 :         const char *extension_name = (const char *) lfirst(lc);
     469                 :         975 :         Oid         extension_oid = get_extension_oid(extension_name, true);
     470                 :             : 
     471         [ +  + ]:         975 :         if (OidIsValid(extension_oid))
     472                 :             :         {
     473                 :         973 :             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                 :         974 :     list_free(extlist);
     485                 :         974 :     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                 :          27 : process_pgfdw_appname(const char *appname)
     497                 :             : {
     498                 :             :     const char *p;
     499                 :             :     StringInfoData buf;
     500                 :             : 
     501                 :          27 :     initStringInfo(&buf);
     502                 :             : 
     503         [ +  + ]:         383 :     for (p = appname; *p != '\0'; p++)
     504                 :             :     {
     505         [ +  + ]:         356 :         if (*p != '%')
     506                 :             :         {
     507                 :             :             /* literal char, just copy */
     508                 :         347 :             appendStringInfoChar(&buf, *p);
     509                 :         347 :             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                 :          27 :     return buf.data;
     567                 :             : }
     568                 :             : 
     569                 :             : /*
     570                 :             :  * Module load callback
     571                 :             :  */
     572                 :             : void
     573                 :          38 : _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                 :          38 :     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                 :          38 :     MarkGUCPrefixReserved("postgres_fdw");
     595                 :          38 : }
        

Generated by: LCOV version 2.0-1