Header And Logo

PostgreSQL
| The world's most advanced open source database.

option.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * option.c
00004  *        FDW option handling for postgres_fdw
00005  *
00006  * Portions Copyright (c) 2012-2013, PostgreSQL Global Development Group
00007  *
00008  * IDENTIFICATION
00009  *        contrib/postgres_fdw/option.c
00010  *
00011  *-------------------------------------------------------------------------
00012  */
00013 #include "postgres.h"
00014 
00015 #include "postgres_fdw.h"
00016 
00017 #include "access/reloptions.h"
00018 #include "catalog/pg_foreign_server.h"
00019 #include "catalog/pg_foreign_table.h"
00020 #include "catalog/pg_user_mapping.h"
00021 #include "commands/defrem.h"
00022 
00023 
00024 /*
00025  * Describes the valid options for objects that this wrapper uses.
00026  */
00027 typedef struct PgFdwOption
00028 {
00029     const char *keyword;
00030     Oid         optcontext;     /* OID of catalog in which option may appear */
00031     bool        is_libpq_opt;   /* true if it's used in libpq */
00032 } PgFdwOption;
00033 
00034 /*
00035  * Valid options for postgres_fdw.
00036  * Allocated and filled in InitPgFdwOptions.
00037  */
00038 static PgFdwOption *postgres_fdw_options;
00039 
00040 /*
00041  * Valid options for libpq.
00042  * Allocated and filled in InitPgFdwOptions.
00043  */
00044 static PQconninfoOption *libpq_options;
00045 
00046 /*
00047  * Helper functions
00048  */
00049 static void InitPgFdwOptions(void);
00050 static bool is_valid_option(const char *keyword, Oid context);
00051 static bool is_libpq_option(const char *keyword);
00052 
00053 
00054 /*
00055  * Validate the generic options given to a FOREIGN DATA WRAPPER, SERVER,
00056  * USER MAPPING or FOREIGN TABLE that uses postgres_fdw.
00057  *
00058  * Raise an ERROR if the option or its value is considered invalid.
00059  */
00060 extern Datum postgres_fdw_validator(PG_FUNCTION_ARGS);
00061 
00062 PG_FUNCTION_INFO_V1(postgres_fdw_validator);
00063 
00064 Datum
00065 postgres_fdw_validator(PG_FUNCTION_ARGS)
00066 {
00067     List       *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
00068     Oid         catalog = PG_GETARG_OID(1);
00069     ListCell   *cell;
00070 
00071     /* Build our options lists if we didn't yet. */
00072     InitPgFdwOptions();
00073 
00074     /*
00075      * Check that only options supported by postgres_fdw, and allowed for the
00076      * current object type, are given.
00077      */
00078     foreach(cell, options_list)
00079     {
00080         DefElem    *def = (DefElem *) lfirst(cell);
00081 
00082         if (!is_valid_option(def->defname, catalog))
00083         {
00084             /*
00085              * Unknown option specified, complain about it. Provide a hint
00086              * with list of valid options for the object.
00087              */
00088             PgFdwOption *opt;
00089             StringInfoData buf;
00090 
00091             initStringInfo(&buf);
00092             for (opt = postgres_fdw_options; opt->keyword; opt++)
00093             {
00094                 if (catalog == opt->optcontext)
00095                     appendStringInfo(&buf, "%s%s", (buf.len > 0) ? ", " : "",
00096                                      opt->keyword);
00097             }
00098 
00099             ereport(ERROR,
00100                     (errcode(ERRCODE_FDW_INVALID_OPTION_NAME),
00101                      errmsg("invalid option \"%s\"", def->defname),
00102                      errhint("Valid options in this context are: %s",
00103                              buf.data)));
00104         }
00105 
00106         /*
00107          * Validate option value, when we can do so without any context.
00108          */
00109         if (strcmp(def->defname, "use_remote_estimate") == 0)
00110         {
00111             /* use_remote_estimate accepts only boolean values */
00112             (void) defGetBoolean(def);
00113         }
00114         else if (strcmp(def->defname, "fdw_startup_cost") == 0 ||
00115                  strcmp(def->defname, "fdw_tuple_cost") == 0)
00116         {
00117             /* these must have a non-negative numeric value */
00118             double      val;
00119             char       *endp;
00120 
00121             val = strtod(defGetString(def), &endp);
00122             if (*endp || val < 0)
00123                 ereport(ERROR,
00124                         (errcode(ERRCODE_SYNTAX_ERROR),
00125                          errmsg("%s requires a non-negative numeric value",
00126                                 def->defname)));
00127         }
00128     }
00129 
00130     PG_RETURN_VOID();
00131 }
00132 
00133 /*
00134  * Initialize option lists.
00135  */
00136 static void
00137 InitPgFdwOptions(void)
00138 {
00139     int         num_libpq_opts;
00140     PQconninfoOption *lopt;
00141     PgFdwOption *popt;
00142 
00143     /* non-libpq FDW-specific FDW options */
00144     static const PgFdwOption non_libpq_options[] = {
00145         {"schema_name", ForeignTableRelationId, false},
00146         {"table_name", ForeignTableRelationId, false},
00147         {"column_name", AttributeRelationId, false},
00148         /* use_remote_estimate is available on both server and table */
00149         {"use_remote_estimate", ForeignServerRelationId, false},
00150         {"use_remote_estimate", ForeignTableRelationId, false},
00151         /* cost factors */
00152         {"fdw_startup_cost", ForeignServerRelationId, false},
00153         {"fdw_tuple_cost", ForeignServerRelationId, false},
00154         {NULL, InvalidOid, false}
00155     };
00156 
00157     /* Prevent redundant initialization. */
00158     if (postgres_fdw_options)
00159         return;
00160 
00161     /*
00162      * Get list of valid libpq options.
00163      *
00164      * To avoid unnecessary work, we get the list once and use it throughout
00165      * the lifetime of this backend process.  We don't need to care about
00166      * memory context issues, because PQconndefaults allocates with malloc.
00167      */
00168     libpq_options = PQconndefaults();
00169     if (!libpq_options)         /* assume reason for failure is OOM */
00170         ereport(ERROR,
00171                 (errcode(ERRCODE_FDW_OUT_OF_MEMORY),
00172                  errmsg("out of memory"),
00173              errdetail("could not get libpq's default connection options")));
00174 
00175     /* Count how many libpq options are available. */
00176     num_libpq_opts = 0;
00177     for (lopt = libpq_options; lopt->keyword; lopt++)
00178         num_libpq_opts++;
00179 
00180     /*
00181      * Construct an array which consists of all valid options for
00182      * postgres_fdw, by appending FDW-specific options to libpq options.
00183      *
00184      * We use plain malloc here to allocate postgres_fdw_options because it
00185      * lives as long as the backend process does.  Besides, keeping
00186      * libpq_options in memory allows us to avoid copying every keyword
00187      * string.
00188      */
00189     postgres_fdw_options = (PgFdwOption *)
00190         malloc(sizeof(PgFdwOption) * num_libpq_opts +
00191                sizeof(non_libpq_options));
00192     if (postgres_fdw_options == NULL)
00193         ereport(ERROR,
00194                 (errcode(ERRCODE_FDW_OUT_OF_MEMORY),
00195                  errmsg("out of memory")));
00196 
00197     popt = postgres_fdw_options;
00198     for (lopt = libpq_options; lopt->keyword; lopt++)
00199     {
00200         /* Hide debug options, as well as settings we override internally. */
00201         if (strchr(lopt->dispchar, 'D') ||
00202             strcmp(lopt->keyword, "fallback_application_name") == 0 ||
00203             strcmp(lopt->keyword, "client_encoding") == 0)
00204             continue;
00205 
00206         /* We don't have to copy keyword string, as described above. */
00207         popt->keyword = lopt->keyword;
00208 
00209         /*
00210          * "user" and any secret options are allowed only on user mappings.
00211          * Everything else is a server option.
00212          */
00213         if (strcmp(lopt->keyword, "user") == 0 || strchr(lopt->dispchar, '*'))
00214             popt->optcontext = UserMappingRelationId;
00215         else
00216             popt->optcontext = ForeignServerRelationId;
00217         popt->is_libpq_opt = true;
00218 
00219         popt++;
00220     }
00221 
00222     /* Append FDW-specific options and dummy terminator. */
00223     memcpy(popt, non_libpq_options, sizeof(non_libpq_options));
00224 }
00225 
00226 /*
00227  * Check whether the given option is one of the valid postgres_fdw options.
00228  * context is the Oid of the catalog holding the object the option is for.
00229  */
00230 static bool
00231 is_valid_option(const char *keyword, Oid context)
00232 {
00233     PgFdwOption *opt;
00234 
00235     Assert(postgres_fdw_options);       /* must be initialized already */
00236 
00237     for (opt = postgres_fdw_options; opt->keyword; opt++)
00238     {
00239         if (context == opt->optcontext && strcmp(opt->keyword, keyword) == 0)
00240             return true;
00241     }
00242 
00243     return false;
00244 }
00245 
00246 /*
00247  * Check whether the given option is one of the valid libpq options.
00248  */
00249 static bool
00250 is_libpq_option(const char *keyword)
00251 {
00252     PgFdwOption *opt;
00253 
00254     Assert(postgres_fdw_options);       /* must be initialized already */
00255 
00256     for (opt = postgres_fdw_options; opt->keyword; opt++)
00257     {
00258         if (opt->is_libpq_opt && strcmp(opt->keyword, keyword) == 0)
00259             return true;
00260     }
00261 
00262     return false;
00263 }
00264 
00265 /*
00266  * Generate key-value arrays which include only libpq options from the
00267  * given list (which can contain any kind of options).  Caller must have
00268  * allocated large-enough arrays.  Returns number of options found.
00269  */
00270 int
00271 ExtractConnectionOptions(List *defelems, const char **keywords,
00272                          const char **values)
00273 {
00274     ListCell   *lc;
00275     int         i;
00276 
00277     /* Build our options lists if we didn't yet. */
00278     InitPgFdwOptions();
00279 
00280     i = 0;
00281     foreach(lc, defelems)
00282     {
00283         DefElem    *d = (DefElem *) lfirst(lc);
00284 
00285         if (is_libpq_option(d->defname))
00286         {
00287             keywords[i] = d->defname;
00288             values[i] = defGetString(d);
00289             i++;
00290         }
00291     }
00292     return i;
00293 }