LCOV - code coverage report
Current view: top level - contrib/postgres_fdw - option.c (source / functions) Hit Total Coverage
Test: PostgreSQL 12devel Lines: 78 88 88.6 %
Date: 2018-08-16 03:20:48 Functions: 7 7 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * option.c
       4             :  *        FDW option handling for postgres_fdw
       5             :  *
       6             :  * Portions Copyright (c) 2012-2018, PostgreSQL Global Development Group
       7             :  *
       8             :  * IDENTIFICATION
       9             :  *        contrib/postgres_fdw/option.c
      10             :  *
      11             :  *-------------------------------------------------------------------------
      12             :  */
      13             : #include "postgres.h"
      14             : 
      15             : #include "postgres_fdw.h"
      16             : 
      17             : #include "access/reloptions.h"
      18             : #include "catalog/pg_foreign_server.h"
      19             : #include "catalog/pg_foreign_table.h"
      20             : #include "catalog/pg_user_mapping.h"
      21             : #include "commands/defrem.h"
      22             : #include "commands/extension.h"
      23             : #include "utils/builtins.h"
      24             : #include "utils/varlena.h"
      25             : 
      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             :  * Valid options for libpq.
      45             :  * Allocated and filled in InitPgFdwOptions.
      46             :  */
      47             : static PQconninfoOption *libpq_options;
      48             : 
      49             : /*
      50             :  * Helper functions
      51             :  */
      52             : static void InitPgFdwOptions(void);
      53             : static bool is_valid_option(const char *keyword, Oid context);
      54             : static bool is_libpq_option(const char *keyword);
      55             : 
      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           4 : PG_FUNCTION_INFO_V1(postgres_fdw_validator);
      64             : 
      65             : Datum
      66         264 : postgres_fdw_validator(PG_FUNCTION_ARGS)
      67             : {
      68         264 :     List       *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
      69         264 :     Oid         catalog = PG_GETARG_OID(1);
      70             :     ListCell   *cell;
      71             : 
      72             :     /* Build our options lists if we didn't yet. */
      73         264 :     InitPgFdwOptions();
      74             : 
      75             :     /*
      76             :      * Check that only options supported by postgres_fdw, and allowed for the
      77             :      * current object type, are given.
      78             :      */
      79         788 :     foreach(cell, options_list)
      80             :     {
      81         526 :         DefElem    *def = (DefElem *) lfirst(cell);
      82             : 
      83         526 :         if (!is_valid_option(def->defname, catalog))
      84             :         {
      85             :             /*
      86             :              * Unknown option specified, complain about it. Provide a hint
      87             :              * with list of valid options for the object.
      88             :              */
      89             :             PgFdwOption *opt;
      90             :             StringInfoData buf;
      91             : 
      92           0 :             initStringInfo(&buf);
      93           0 :             for (opt = postgres_fdw_options; opt->keyword; opt++)
      94             :             {
      95           0 :                 if (catalog == opt->optcontext)
      96           0 :                     appendStringInfo(&buf, "%s%s", (buf.len > 0) ? ", " : "",
      97             :                                      opt->keyword);
      98             :             }
      99             : 
     100           0 :             ereport(ERROR,
     101             :                     (errcode(ERRCODE_FDW_INVALID_OPTION_NAME),
     102             :                      errmsg("invalid option \"%s\"", def->defname),
     103             :                      errhint("Valid options in this context are: %s",
     104             :                              buf.data)));
     105             :         }
     106             : 
     107             :         /*
     108             :          * Validate option value, when we can do so without any context.
     109             :          */
     110        1032 :         if (strcmp(def->defname, "use_remote_estimate") == 0 ||
     111         506 :             strcmp(def->defname, "updatable") == 0)
     112             :         {
     113             :             /* these accept only boolean values */
     114          28 :             (void) defGetBoolean(def);
     115             :         }
     116         988 :         else if (strcmp(def->defname, "fdw_startup_cost") == 0 ||
     117         490 :                  strcmp(def->defname, "fdw_tuple_cost") == 0)
     118          16 :         {
     119             :             /* these must have a non-negative numeric value */
     120             :             double      val;
     121             :             char       *endp;
     122             : 
     123          16 :             val = strtod(defGetString(def), &endp);
     124          16 :             if (*endp || val < 0)
     125           0 :                 ereport(ERROR,
     126             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     127             :                          errmsg("%s requires a non-negative numeric value",
     128             :                                 def->defname)));
     129             :         }
     130         482 :         else if (strcmp(def->defname, "extensions") == 0)
     131             :         {
     132             :             /* check list syntax, warn about uninstalled extensions */
     133          20 :             (void) ExtractExtensionList(defGetString(def), true);
     134             :         }
     135         462 :         else if (strcmp(def->defname, "fetch_size") == 0)
     136             :         {
     137             :             int         fetch_size;
     138             : 
     139           8 :             fetch_size = strtol(defGetString(def), NULL, 10);
     140           8 :             if (fetch_size <= 0)
     141           0 :                 ereport(ERROR,
     142             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     143             :                          errmsg("%s requires a non-negative integer value",
     144             :                                 def->defname)));
     145             :         }
     146             :     }
     147             : 
     148         262 :     PG_RETURN_VOID();
     149             : }
     150             : 
     151             : /*
     152             :  * Initialize option lists.
     153             :  */
     154             : static void
     155         336 : InitPgFdwOptions(void)
     156             : {
     157             :     int         num_libpq_opts;
     158             :     PQconninfoOption *lopt;
     159             :     PgFdwOption *popt;
     160             : 
     161             :     /* non-libpq FDW-specific FDW options */
     162             :     static const PgFdwOption non_libpq_options[] = {
     163             :         {"schema_name", ForeignTableRelationId, false},
     164             :         {"table_name", ForeignTableRelationId, false},
     165             :         {"column_name", AttributeRelationId, false},
     166             :         /* use_remote_estimate is available on both server and table */
     167             :         {"use_remote_estimate", ForeignServerRelationId, false},
     168             :         {"use_remote_estimate", ForeignTableRelationId, false},
     169             :         /* cost factors */
     170             :         {"fdw_startup_cost", ForeignServerRelationId, false},
     171             :         {"fdw_tuple_cost", ForeignServerRelationId, false},
     172             :         /* shippable extensions */
     173             :         {"extensions", ForeignServerRelationId, false},
     174             :         /* updatable is available on both server and table */
     175             :         {"updatable", ForeignServerRelationId, false},
     176             :         {"updatable", ForeignTableRelationId, false},
     177             :         /* fetch_size is available on both server and table */
     178             :         {"fetch_size", ForeignServerRelationId, false},
     179             :         {"fetch_size", ForeignTableRelationId, false},
     180             :         {NULL, InvalidOid, false}
     181             :     };
     182             : 
     183             :     /* Prevent redundant initialization. */
     184         336 :     if (postgres_fdw_options)
     185         334 :         return;
     186             : 
     187             :     /*
     188             :      * Get list of valid libpq options.
     189             :      *
     190             :      * To avoid unnecessary work, we get the list once and use it throughout
     191             :      * the lifetime of this backend process.  We don't need to care about
     192             :      * memory context issues, because PQconndefaults allocates with malloc.
     193             :      */
     194           2 :     libpq_options = PQconndefaults();
     195           2 :     if (!libpq_options)         /* assume reason for failure is OOM */
     196           0 :         ereport(ERROR,
     197             :                 (errcode(ERRCODE_FDW_OUT_OF_MEMORY),
     198             :                  errmsg("out of memory"),
     199             :                  errdetail("Could not get libpq's default connection options.")));
     200             : 
     201             :     /* Count how many libpq options are available. */
     202           2 :     num_libpq_opts = 0;
     203          58 :     for (lopt = libpq_options; lopt->keyword; lopt++)
     204          56 :         num_libpq_opts++;
     205             : 
     206             :     /*
     207             :      * Construct an array which consists of all valid options for
     208             :      * postgres_fdw, by appending FDW-specific options to libpq options.
     209             :      *
     210             :      * We use plain malloc here to allocate postgres_fdw_options because it
     211             :      * lives as long as the backend process does.  Besides, keeping
     212             :      * libpq_options in memory allows us to avoid copying every keyword
     213             :      * string.
     214             :      */
     215           2 :     postgres_fdw_options = (PgFdwOption *)
     216           2 :         malloc(sizeof(PgFdwOption) * num_libpq_opts +
     217             :                sizeof(non_libpq_options));
     218           2 :     if (postgres_fdw_options == NULL)
     219           0 :         ereport(ERROR,
     220             :                 (errcode(ERRCODE_FDW_OUT_OF_MEMORY),
     221             :                  errmsg("out of memory")));
     222             : 
     223           2 :     popt = postgres_fdw_options;
     224          58 :     for (lopt = libpq_options; lopt->keyword; lopt++)
     225             :     {
     226             :         /* Hide debug options, as well as settings we override internally. */
     227         104 :         if (strchr(lopt->dispchar, 'D') ||
     228          94 :             strcmp(lopt->keyword, "fallback_application_name") == 0 ||
     229          46 :             strcmp(lopt->keyword, "client_encoding") == 0)
     230          12 :             continue;
     231             : 
     232             :         /* We don't have to copy keyword string, as described above. */
     233          44 :         popt->keyword = lopt->keyword;
     234             : 
     235             :         /*
     236             :          * "user" and any secret options are allowed only on user mappings.
     237             :          * Everything else is a server option.
     238             :          */
     239          44 :         if (strcmp(lopt->keyword, "user") == 0 || strchr(lopt->dispchar, '*'))
     240           4 :             popt->optcontext = UserMappingRelationId;
     241             :         else
     242          40 :             popt->optcontext = ForeignServerRelationId;
     243          44 :         popt->is_libpq_opt = true;
     244             : 
     245          44 :         popt++;
     246             :     }
     247             : 
     248             :     /* Append FDW-specific options and dummy terminator. */
     249           2 :     memcpy(popt, non_libpq_options, sizeof(non_libpq_options));
     250             : }
     251             : 
     252             : /*
     253             :  * Check whether the given option is one of the valid postgres_fdw options.
     254             :  * context is the Oid of the catalog holding the object the option is for.
     255             :  */
     256             : static bool
     257         526 : is_valid_option(const char *keyword, Oid context)
     258             : {
     259             :     PgFdwOption *opt;
     260             : 
     261             :     Assert(postgres_fdw_options);   /* must be initialized already */
     262             : 
     263       10326 :     for (opt = postgres_fdw_options; opt->keyword; opt++)
     264             :     {
     265       10326 :         if (context == opt->optcontext && strcmp(opt->keyword, keyword) == 0)
     266         526 :             return true;
     267             :     }
     268             : 
     269           0 :     return false;
     270             : }
     271             : 
     272             : /*
     273             :  * Check whether the given option is one of the valid libpq options.
     274             :  */
     275             : static bool
     276          92 : is_libpq_option(const char *keyword)
     277             : {
     278             :     PgFdwOption *opt;
     279             : 
     280             :     Assert(postgres_fdw_options);   /* must be initialized already */
     281             : 
     282        1174 :     for (opt = postgres_fdw_options; opt->keyword; opt++)
     283             :     {
     284        1156 :         if (opt->is_libpq_opt && strcmp(opt->keyword, keyword) == 0)
     285          74 :             return true;
     286             :     }
     287             : 
     288          18 :     return false;
     289             : }
     290             : 
     291             : /*
     292             :  * Generate key-value arrays which include only libpq options from the
     293             :  * given list (which can contain any kind of options).  Caller must have
     294             :  * allocated large-enough arrays.  Returns number of options found.
     295             :  */
     296             : int
     297          72 : ExtractConnectionOptions(List *defelems, const char **keywords,
     298             :                          const char **values)
     299             : {
     300             :     ListCell   *lc;
     301             :     int         i;
     302             : 
     303             :     /* Build our options lists if we didn't yet. */
     304          72 :     InitPgFdwOptions();
     305             : 
     306          72 :     i = 0;
     307         164 :     foreach(lc, defelems)
     308             :     {
     309          92 :         DefElem    *d = (DefElem *) lfirst(lc);
     310             : 
     311          92 :         if (is_libpq_option(d->defname))
     312             :         {
     313          74 :             keywords[i] = d->defname;
     314          74 :             values[i] = defGetString(d);
     315          74 :             i++;
     316             :         }
     317             :     }
     318          72 :     return i;
     319             : }
     320             : 
     321             : /*
     322             :  * Parse a comma-separated string and return a List of the OIDs of the
     323             :  * extensions named in the string.  If any names in the list cannot be
     324             :  * found, report a warning if warnOnMissing is true, else just silently
     325             :  * ignore them.
     326             :  */
     327             : List *
     328        1362 : ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
     329             : {
     330        1362 :     List       *extensionOids = NIL;
     331             :     List       *extlist;
     332             :     ListCell   *lc;
     333             : 
     334             :     /* SplitIdentifierString scribbles on its input, so pstrdup first */
     335        1362 :     if (!SplitIdentifierString(pstrdup(extensionsString), ',', &extlist))
     336             :     {
     337             :         /* syntax error in name list */
     338           2 :         ereport(ERROR,
     339             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     340             :                  errmsg("parameter \"%s\" must be a list of extension names",
     341             :                         "extensions")));
     342             :     }
     343             : 
     344        2722 :     foreach(lc, extlist)
     345             :     {
     346        1362 :         const char *extension_name = (const char *) lfirst(lc);
     347        1362 :         Oid         extension_oid = get_extension_oid(extension_name, true);
     348             : 
     349        1362 :         if (OidIsValid(extension_oid))
     350             :         {
     351        1358 :             extensionOids = lappend_oid(extensionOids, extension_oid);
     352             :         }
     353           4 :         else if (warnOnMissing)
     354             :         {
     355           4 :             ereport(WARNING,
     356             :                     (errcode(ERRCODE_UNDEFINED_OBJECT),
     357             :                      errmsg("extension \"%s\" is not installed",
     358             :                             extension_name)));
     359             :         }
     360             :     }
     361             : 
     362        1360 :     list_free(extlist);
     363        1360 :     return extensionOids;
     364             : }

Generated by: LCOV version 1.13