LCOV - code coverage report
Current view: top level - contrib/postgres_fdw - option.c (source / functions) Hit Total Coverage
Test: PostgreSQL 11devel Lines: 78 88 88.6 %
Date: 2018-02-20 09:20:13 Functions: 7 7 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 60 86 69.8 %

           Branch data     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                 :          8 : PG_FUNCTION_INFO_V1(postgres_fdw_validator);
      64                 :            : 
      65                 :            : Datum
      66                 :        244 : postgres_fdw_validator(PG_FUNCTION_ARGS)
      67                 :            : {
      68                 :        244 :     List       *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
      69                 :        244 :     Oid         catalog = PG_GETARG_OID(1);
      70                 :            :     ListCell   *cell;
      71                 :            : 
      72                 :            :     /* Build our options lists if we didn't yet. */
      73                 :        244 :     InitPgFdwOptions();
      74                 :            : 
      75                 :            :     /*
      76                 :            :      * Check that only options supported by postgres_fdw, and allowed for the
      77                 :            :      * current object type, are given.
      78                 :            :      */
      79         [ +  + ]:        756 :     foreach(cell, options_list)
      80                 :            :     {
      81                 :        514 :         DefElem    *def = (DefElem *) lfirst(cell);
      82                 :            : 
      83         [ -  + ]:        514 :         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 [ +  + ][ +  + ]:        514 :         if (strcmp(def->defname, "use_remote_estimate") == 0 ||
     111                 :        490 :             strcmp(def->defname, "updatable") == 0)
     112                 :            :         {
     113                 :            :             /* these accept only boolean values */
     114                 :         32 :             (void) defGetBoolean(def);
     115                 :            :         }
     116 [ +  + ][ +  + ]:        482 :         else if (strcmp(def->defname, "fdw_startup_cost") == 0 ||
     117                 :        474 :                  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         [ +  + ]:        466 :         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         [ +  + ]:        446 :         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                 :        242 :     PG_RETURN_VOID();
     149                 :            : }
     150                 :            : 
     151                 :            : /*
     152                 :            :  * Initialize option lists.
     153                 :            :  */
     154                 :            : static void
     155                 :        316 : 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         [ +  + ]:        316 :     if (postgres_fdw_options)
     185                 :        314 :         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         [ +  + ]:         60 :     for (lopt = libpq_options; lopt->keyword; lopt++)
     204                 :         58 :         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         [ +  + ]:         60 :     for (lopt = libpq_options; lopt->keyword; lopt++)
     225                 :            :     {
     226                 :            :         /* Hide debug options, as well as settings we override internally. */
     227 [ +  + ][ +  + ]:         58 :         if (strchr(lopt->dispchar, 'D') ||
     228         [ +  + ]:         46 :             strcmp(lopt->keyword, "fallback_application_name") == 0 ||
     229                 :         46 :             strcmp(lopt->keyword, "client_encoding") == 0)
     230                 :         14 :             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                 :        514 : is_valid_option(const char *keyword, Oid context)
     258                 :            : {
     259                 :            :     PgFdwOption *opt;
     260                 :            : 
     261                 :            :     Assert(postgres_fdw_options);   /* must be initialized already */
     262                 :            : 
     263         [ +  - ]:      10046 :     for (opt = postgres_fdw_options; opt->keyword; opt++)
     264                 :            :     {
     265 [ +  + ][ +  + ]:      10046 :         if (context == opt->optcontext && strcmp(opt->keyword, keyword) == 0)
     266                 :        514 :             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                 :       1188 : ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
     329                 :            : {
     330                 :       1188 :     List       *extensionOids = NIL;
     331                 :            :     List       *extlist;
     332                 :            :     ListCell   *lc;
     333                 :            : 
     334                 :            :     /* SplitIdentifierString scribbles on its input, so pstrdup first */
     335         [ +  + ]:       1188 :     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         [ +  + ]:       2374 :     foreach(lc, extlist)
     345                 :            :     {
     346                 :       1188 :         const char *extension_name = (const char *) lfirst(lc);
     347                 :       1188 :         Oid         extension_oid = get_extension_oid(extension_name, true);
     348                 :            : 
     349         [ +  + ]:       1188 :         if (OidIsValid(extension_oid))
     350                 :            :         {
     351                 :       1184 :             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                 :       1186 :     list_free(extlist);
     363                 :       1186 :     return extensionOids;
     364                 :            : }

Generated by: LCOV version 1.13