PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
option.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * option.c
4  * FDW option handling for postgres_fdw
5  *
6  * Portions Copyright (c) 2012-2016, 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"
21 #include "commands/defrem.h"
22 #include "commands/extension.h"
23 #include "utils/builtins.h"
24 
25 
26 /*
27  * Describes the valid options for objects that this wrapper uses.
28  */
29 typedef struct PgFdwOption
30 {
31  const char *keyword;
32  Oid optcontext; /* OID of catalog in which option may appear */
33  bool is_libpq_opt; /* true if it's used in libpq */
34 } PgFdwOption;
35 
36 /*
37  * Valid options for postgres_fdw.
38  * Allocated and filled in InitPgFdwOptions.
39  */
41 
42 /*
43  * Valid options for libpq.
44  * Allocated and filled in InitPgFdwOptions.
45  */
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 
56 /*
57  * Validate the generic options given to a FOREIGN DATA WRAPPER, SERVER,
58  * USER MAPPING or FOREIGN TABLE that uses postgres_fdw.
59  *
60  * Raise an ERROR if the option or its value is considered invalid.
61  */
63 
64 Datum
66 {
67  List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
68  Oid catalog = PG_GETARG_OID(1);
69  ListCell *cell;
70 
71  /* Build our options lists if we didn't yet. */
73 
74  /*
75  * Check that only options supported by postgres_fdw, and allowed for the
76  * current object type, are given.
77  */
78  foreach(cell, options_list)
79  {
80  DefElem *def = (DefElem *) lfirst(cell);
81 
82  if (!is_valid_option(def->defname, catalog))
83  {
84  /*
85  * Unknown option specified, complain about it. Provide a hint
86  * with list of valid options for the object.
87  */
88  PgFdwOption *opt;
90 
91  initStringInfo(&buf);
92  for (opt = postgres_fdw_options; opt->keyword; opt++)
93  {
94  if (catalog == opt->optcontext)
95  appendStringInfo(&buf, "%s%s", (buf.len > 0) ? ", " : "",
96  opt->keyword);
97  }
98 
99  ereport(ERROR,
100  (errcode(ERRCODE_FDW_INVALID_OPTION_NAME),
101  errmsg("invalid option \"%s\"", def->defname),
102  errhint("Valid options in this context are: %s",
103  buf.data)));
104  }
105 
106  /*
107  * Validate option value, when we can do so without any context.
108  */
109  if (strcmp(def->defname, "use_remote_estimate") == 0 ||
110  strcmp(def->defname, "updatable") == 0)
111  {
112  /* these accept only boolean values */
113  (void) defGetBoolean(def);
114  }
115  else if (strcmp(def->defname, "fdw_startup_cost") == 0 ||
116  strcmp(def->defname, "fdw_tuple_cost") == 0)
117  {
118  /* these must have a non-negative numeric value */
119  double val;
120  char *endp;
121 
122  val = strtod(defGetString(def), &endp);
123  if (*endp || val < 0)
124  ereport(ERROR,
125  (errcode(ERRCODE_SYNTAX_ERROR),
126  errmsg("%s requires a non-negative numeric value",
127  def->defname)));
128  }
129  else if (strcmp(def->defname, "extensions") == 0)
130  {
131  /* check list syntax, warn about uninstalled extensions */
132  (void) ExtractExtensionList(defGetString(def), true);
133  }
134  else if (strcmp(def->defname, "fetch_size") == 0)
135  {
136  int fetch_size;
137 
138  fetch_size = strtol(defGetString(def), NULL,10);
139  if (fetch_size <= 0)
140  ereport(ERROR,
141  (errcode(ERRCODE_SYNTAX_ERROR),
142  errmsg("%s requires a non-negative integer value",
143  def->defname)));
144  }
145  }
146 
147  PG_RETURN_VOID();
148 }
149 
150 /*
151  * Initialize option lists.
152  */
153 static void
155 {
156  int num_libpq_opts;
157  PQconninfoOption *lopt;
158  PgFdwOption *popt;
159 
160  /* non-libpq FDW-specific FDW options */
161  static const PgFdwOption non_libpq_options[] = {
162  {"schema_name", ForeignTableRelationId, false},
163  {"table_name", ForeignTableRelationId, false},
164  {"column_name", AttributeRelationId, false},
165  /* use_remote_estimate is available on both server and table */
166  {"use_remote_estimate", ForeignServerRelationId, false},
167  {"use_remote_estimate", ForeignTableRelationId, false},
168  /* cost factors */
169  {"fdw_startup_cost", ForeignServerRelationId, false},
170  {"fdw_tuple_cost", ForeignServerRelationId, false},
171  /* shippable extensions */
172  {"extensions", ForeignServerRelationId, false},
173  /* updatable is available on both server and table */
174  {"updatable", ForeignServerRelationId, false},
175  {"updatable", ForeignTableRelationId, false},
176  /* fetch_size is available on both server and table */
177  {"fetch_size", ForeignServerRelationId, false},
178  {"fetch_size", ForeignTableRelationId, false},
179  {NULL, InvalidOid, false}
180  };
181 
182  /* Prevent redundant initialization. */
183  if (postgres_fdw_options)
184  return;
185 
186  /*
187  * Get list of valid libpq options.
188  *
189  * To avoid unnecessary work, we get the list once and use it throughout
190  * the lifetime of this backend process. We don't need to care about
191  * memory context issues, because PQconndefaults allocates with malloc.
192  */
193  libpq_options = PQconndefaults();
194  if (!libpq_options) /* assume reason for failure is OOM */
195  ereport(ERROR,
196  (errcode(ERRCODE_FDW_OUT_OF_MEMORY),
197  errmsg("out of memory"),
198  errdetail("could not get libpq's default connection options")));
199 
200  /* Count how many libpq options are available. */
201  num_libpq_opts = 0;
202  for (lopt = libpq_options; lopt->keyword; lopt++)
203  num_libpq_opts++;
204 
205  /*
206  * Construct an array which consists of all valid options for
207  * postgres_fdw, by appending FDW-specific options to libpq options.
208  *
209  * We use plain malloc here to allocate postgres_fdw_options because it
210  * lives as long as the backend process does. Besides, keeping
211  * libpq_options in memory allows us to avoid copying every keyword
212  * string.
213  */
214  postgres_fdw_options = (PgFdwOption *)
215  malloc(sizeof(PgFdwOption) * num_libpq_opts +
216  sizeof(non_libpq_options));
217  if (postgres_fdw_options == NULL)
218  ereport(ERROR,
219  (errcode(ERRCODE_FDW_OUT_OF_MEMORY),
220  errmsg("out of memory")));
221 
222  popt = postgres_fdw_options;
223  for (lopt = libpq_options; lopt->keyword; lopt++)
224  {
225  /* Hide debug options, as well as settings we override internally. */
226  if (strchr(lopt->dispchar, 'D') ||
227  strcmp(lopt->keyword, "fallback_application_name") == 0 ||
228  strcmp(lopt->keyword, "client_encoding") == 0)
229  continue;
230 
231  /* We don't have to copy keyword string, as described above. */
232  popt->keyword = lopt->keyword;
233 
234  /*
235  * "user" and any secret options are allowed only on user mappings.
236  * Everything else is a server option.
237  */
238  if (strcmp(lopt->keyword, "user") == 0 || strchr(lopt->dispchar, '*'))
240  else
242  popt->is_libpq_opt = true;
243 
244  popt++;
245  }
246 
247  /* Append FDW-specific options and dummy terminator. */
248  memcpy(popt, non_libpq_options, sizeof(non_libpq_options));
249 }
250 
251 /*
252  * Check whether the given option is one of the valid postgres_fdw options.
253  * context is the Oid of the catalog holding the object the option is for.
254  */
255 static bool
256 is_valid_option(const char *keyword, Oid context)
257 {
258  PgFdwOption *opt;
259 
260  Assert(postgres_fdw_options); /* must be initialized already */
261 
262  for (opt = postgres_fdw_options; opt->keyword; opt++)
263  {
264  if (context == opt->optcontext && strcmp(opt->keyword, keyword) == 0)
265  return true;
266  }
267 
268  return false;
269 }
270 
271 /*
272  * Check whether the given option is one of the valid libpq options.
273  */
274 static bool
275 is_libpq_option(const char *keyword)
276 {
277  PgFdwOption *opt;
278 
279  Assert(postgres_fdw_options); /* must be initialized already */
280 
281  for (opt = postgres_fdw_options; opt->keyword; opt++)
282  {
283  if (opt->is_libpq_opt && strcmp(opt->keyword, keyword) == 0)
284  return true;
285  }
286 
287  return false;
288 }
289 
290 /*
291  * Generate key-value arrays which include only libpq options from the
292  * given list (which can contain any kind of options). Caller must have
293  * allocated large-enough arrays. Returns number of options found.
294  */
295 int
296 ExtractConnectionOptions(List *defelems, const char **keywords,
297  const char **values)
298 {
299  ListCell *lc;
300  int i;
301 
302  /* Build our options lists if we didn't yet. */
304 
305  i = 0;
306  foreach(lc, defelems)
307  {
308  DefElem *d = (DefElem *) lfirst(lc);
309 
310  if (is_libpq_option(d->defname))
311  {
312  keywords[i] = d->defname;
313  values[i] = defGetString(d);
314  i++;
315  }
316  }
317  return i;
318 }
319 
320 /*
321  * Parse a comma-separated string and return a List of the OIDs of the
322  * extensions named in the string. If any names in the list cannot be
323  * found, report a warning if warnOnMissing is true, else just silently
324  * ignore them.
325  */
326 List *
327 ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
328 {
329  List *extensionOids = NIL;
330  List *extlist;
331  ListCell *lc;
332 
333  /* SplitIdentifierString scribbles on its input, so pstrdup first */
334  if (!SplitIdentifierString(pstrdup(extensionsString), ',', &extlist))
335  {
336  /* syntax error in name list */
337  ereport(ERROR,
338  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
339  errmsg("parameter \"%s\" must be a list of extension names",
340  "extensions")));
341  }
342 
343  foreach(lc, extlist)
344  {
345  const char *extension_name = (const char *) lfirst(lc);
346  Oid extension_oid = get_extension_oid(extension_name, true);
347 
348  if (OidIsValid(extension_oid))
349  {
350  extensionOids = lappend_oid(extensionOids, extension_oid);
351  }
352  else if (warnOnMissing)
353  {
355  (errcode(ERRCODE_UNDEFINED_OBJECT),
356  errmsg("extension \"%s\" is not installed",
357  extension_name)));
358  }
359  }
360 
361  list_free(extlist);
362  return extensionOids;
363 }
#define NIL
Definition: pg_list.h:69
PG_FUNCTION_INFO_V1(postgres_fdw_validator)
int errhint(const char *fmt,...)
Definition: elog.c:987
#define PG_GETARG_DATUM(n)
Definition: fmgr.h:224
char * pstrdup(const char *in)
Definition: mcxt.c:1168
const char * keyword
Definition: option.c:31
static bool is_valid_option(const char *keyword, Oid context)
Definition: option.c:256
bool is_libpq_opt
Definition: option.c:33
uint64 fetch_size
Definition: logging.c:21
#define AttributeRelationId
Definition: pg_attribute.h:33
int errcode(int sqlerrcode)
Definition: elog.c:575
unsigned int Oid
Definition: postgres_ext.h:31
List * lappend_oid(List *list, Oid datum)
Definition: list.c:164
#define OidIsValid(objectId)
Definition: c.h:530
static PgFdwOption * postgres_fdw_options
Definition: option.c:40
#define malloc(a)
Definition: header.h:45
struct PgFdwOption PgFdwOption
bool defGetBoolean(DefElem *def)
Definition: define.c:111
int ExtractConnectionOptions(List *defelems, const char **keywords, const char **values)
Definition: option.c:296
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:78
#define ERROR
Definition: elog.h:43
char * defGetString(DefElem *def)
Definition: define.c:49
List * ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
Definition: option.c:327
static char * buf
Definition: pg_test_fsync.c:65
#define PG_GETARG_OID(n)
Definition: fmgr.h:231
bool SplitIdentifierString(char *rawstring, char separator, List **namelist)
Definition: varlena.c:3128
int errdetail(const char *fmt,...)
Definition: elog.c:873
Oid optcontext
Definition: option.c:32
#define ereport(elevel, rest)
Definition: elog.h:122
void initStringInfo(StringInfo str)
Definition: stringinfo.c:46
#define WARNING
Definition: elog.h:40
uintptr_t Datum
Definition: postgres.h:374
List * untransformRelOptions(Datum options)
Definition: reloptions.c:856
Datum postgres_fdw_validator(PG_FUNCTION_ARGS)
Definition: option.c:65
#define InvalidOid
Definition: postgres_ext.h:36
#define PG_RETURN_VOID()
Definition: fmgr.h:293
#define ForeignServerRelationId
#define NULL
Definition: c.h:226
#define Assert(condition)
Definition: c.h:667
#define lfirst(lc)
Definition: pg_list.h:106
PQconninfoOption * PQconndefaults(void)
Definition: fe-connect.c:925
static Datum values[MAXATTR]
Definition: bootstrap.c:160
Oid get_extension_oid(const char *extname, bool missing_ok)
Definition: extension.c:121
int errmsg(const char *fmt,...)
Definition: elog.c:797
#define UserMappingRelationId
void list_free(List *list)
Definition: list.c:1133
#define ForeignTableRelationId
int i
static bool is_libpq_option(const char *keyword)
Definition: option.c:275
#define PG_FUNCTION_ARGS
Definition: fmgr.h:150
char * defname
Definition: parsenodes.h:665
static PQconninfoOption * libpq_options
Definition: option.c:46
Definition: pg_list.h:45
long val
Definition: informix.c:689
static void InitPgFdwOptions(void)
Definition: option.c:154