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