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-2017, 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 #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  */
42 
43 /*
44  * Valid options for libpq.
45  * Allocated and filled in InitPgFdwOptions.
46  */
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  */
64 
65 Datum
67 {
68  List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
69  Oid catalog = PG_GETARG_OID(1);
70  ListCell *cell;
71 
72  /* Build our options lists if we didn't yet. */
74 
75  /*
76  * Check that only options supported by postgres_fdw, and allowed for the
77  * current object type, are given.
78  */
79  foreach(cell, options_list)
80  {
81  DefElem *def = (DefElem *) lfirst(cell);
82 
83  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;
91 
92  initStringInfo(&buf);
93  for (opt = postgres_fdw_options; opt->keyword; opt++)
94  {
95  if (catalog == opt->optcontext)
96  appendStringInfo(&buf, "%s%s", (buf.len > 0) ? ", " : "",
97  opt->keyword);
98  }
99 
100  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  if (strcmp(def->defname, "use_remote_estimate") == 0 ||
111  strcmp(def->defname, "updatable") == 0)
112  {
113  /* these accept only boolean values */
114  (void) defGetBoolean(def);
115  }
116  else if (strcmp(def->defname, "fdw_startup_cost") == 0 ||
117  strcmp(def->defname, "fdw_tuple_cost") == 0)
118  {
119  /* these must have a non-negative numeric value */
120  double val;
121  char *endp;
122 
123  val = strtod(defGetString(def), &endp);
124  if (*endp || val < 0)
125  ereport(ERROR,
126  (errcode(ERRCODE_SYNTAX_ERROR),
127  errmsg("%s requires a non-negative numeric value",
128  def->defname)));
129  }
130  else if (strcmp(def->defname, "extensions") == 0)
131  {
132  /* check list syntax, warn about uninstalled extensions */
133  (void) ExtractExtensionList(defGetString(def), true);
134  }
135  else if (strcmp(def->defname, "fetch_size") == 0)
136  {
137  int fetch_size;
138 
139  fetch_size = strtol(defGetString(def), NULL, 10);
140  if (fetch_size <= 0)
141  ereport(ERROR,
142  (errcode(ERRCODE_SYNTAX_ERROR),
143  errmsg("%s requires a non-negative integer value",
144  def->defname)));
145  }
146  }
147 
148  PG_RETURN_VOID();
149 }
150 
151 /*
152  * Initialize option lists.
153  */
154 static 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  if (postgres_fdw_options)
185  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  libpq_options = PQconndefaults();
195  if (!libpq_options) /* assume reason for failure is OOM */
196  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  num_libpq_opts = 0;
203  for (lopt = libpq_options; lopt->keyword; lopt++)
204  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  postgres_fdw_options = (PgFdwOption *)
216  malloc(sizeof(PgFdwOption) * num_libpq_opts +
217  sizeof(non_libpq_options));
218  if (postgres_fdw_options == NULL)
219  ereport(ERROR,
220  (errcode(ERRCODE_FDW_OUT_OF_MEMORY),
221  errmsg("out of memory")));
222 
223  popt = postgres_fdw_options;
224  for (lopt = libpq_options; lopt->keyword; lopt++)
225  {
226  /* Hide debug options, as well as settings we override internally. */
227  if (strchr(lopt->dispchar, 'D') ||
228  strcmp(lopt->keyword, "fallback_application_name") == 0 ||
229  strcmp(lopt->keyword, "client_encoding") == 0)
230  continue;
231 
232  /* We don't have to copy keyword string, as described above. */
233  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  if (strcmp(lopt->keyword, "user") == 0 || strchr(lopt->dispchar, '*'))
241  else
243  popt->is_libpq_opt = true;
244 
245  popt++;
246  }
247 
248  /* Append FDW-specific options and dummy terminator. */
249  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 is_valid_option(const char *keyword, Oid context)
258 {
259  PgFdwOption *opt;
260 
261  Assert(postgres_fdw_options); /* must be initialized already */
262 
263  for (opt = postgres_fdw_options; opt->keyword; opt++)
264  {
265  if (context == opt->optcontext && strcmp(opt->keyword, keyword) == 0)
266  return true;
267  }
268 
269  return false;
270 }
271 
272 /*
273  * Check whether the given option is one of the valid libpq options.
274  */
275 static bool
276 is_libpq_option(const char *keyword)
277 {
278  PgFdwOption *opt;
279 
280  Assert(postgres_fdw_options); /* must be initialized already */
281 
282  for (opt = postgres_fdw_options; opt->keyword; opt++)
283  {
284  if (opt->is_libpq_opt && strcmp(opt->keyword, keyword) == 0)
285  return true;
286  }
287 
288  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 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. */
305 
306  i = 0;
307  foreach(lc, defelems)
308  {
309  DefElem *d = (DefElem *) lfirst(lc);
310 
311  if (is_libpq_option(d->defname))
312  {
313  keywords[i] = d->defname;
314  values[i] = defGetString(d);
315  i++;
316  }
317  }
318  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 ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
329 {
330  List *extensionOids = NIL;
331  List *extlist;
332  ListCell *lc;
333 
334  /* SplitIdentifierString scribbles on its input, so pstrdup first */
335  if (!SplitIdentifierString(pstrdup(extensionsString), ',', &extlist))
336  {
337  /* syntax error in name list */
338  ereport(ERROR,
339  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
340  errmsg("parameter \"%s\" must be a list of extension names",
341  "extensions")));
342  }
343 
344  foreach(lc, extlist)
345  {
346  const char *extension_name = (const char *) lfirst(lc);
347  Oid extension_oid = get_extension_oid(extension_name, true);
348 
349  if (OidIsValid(extension_oid))
350  {
351  extensionOids = lappend_oid(extensionOids, extension_oid);
352  }
353  else if (warnOnMissing)
354  {
356  (errcode(ERRCODE_UNDEFINED_OBJECT),
357  errmsg("extension \"%s\" is not installed",
358  extension_name)));
359  }
360  }
361 
362  list_free(extlist);
363  return extensionOids;
364 }
#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:233
char * pstrdup(const char *in)
Definition: mcxt.c:1077
const char * keyword
Definition: option.c:32
static bool is_valid_option(const char *keyword, Oid context)
Definition: option.c:257
bool is_libpq_opt
Definition: option.c:34
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:538
static PgFdwOption * postgres_fdw_options
Definition: option.c:41
#define malloc(a)
Definition: header.h:50
struct PgFdwOption PgFdwOption
bool defGetBoolean(DefElem *def)
Definition: define.c:111
int ExtractConnectionOptions(List *defelems, const char **keywords, const char **values)
Definition: option.c:297
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:328
static char * buf
Definition: pg_test_fsync.c:66
#define PG_GETARG_OID(n)
Definition: fmgr.h:240
bool SplitIdentifierString(char *rawstring, char separator, List **namelist)
Definition: varlena.c:3260
int errdetail(const char *fmt,...)
Definition: elog.c:873
Oid optcontext
Definition: option.c:33
#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:372
List * untransformRelOptions(Datum options)
Definition: reloptions.c:907
Datum postgres_fdw_validator(PG_FUNCTION_ARGS)
Definition: option.c:66
#define InvalidOid
Definition: postgres_ext.h:36
#define PG_RETURN_VOID()
Definition: fmgr.h:309
#define ForeignServerRelationId
#define NULL
Definition: c.h:229
#define Assert(condition)
Definition: c.h:676
#define lfirst(lc)
Definition: pg_list.h:106
PQconninfoOption * PQconndefaults(void)
Definition: fe-connect.c:1224
static Datum values[MAXATTR]
Definition: bootstrap.c:163
Oid get_extension_oid(const char *extname, bool missing_ok)
Definition: extension.c:135
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:276
#define PG_FUNCTION_ARGS
Definition: fmgr.h:158
char * defname
Definition: parsenodes.h:719
static PQconninfoOption * libpq_options
Definition: option.c:47
Definition: pg_list.h:45
long val
Definition: informix.c:689
static void InitPgFdwOptions(void)
Definition: option.c:155