1 /*-------------------------------------------------------------------------
2 *
3 * option.c
4 * FDW option handling for mysql_fdw
5 *
6 * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group
7 * Portions Copyright (c) 2004-2021, EnterpriseDB Corporation.
8 *
9 * IDENTIFICATION
10 * option.c
11 *
12 *-------------------------------------------------------------------------
13 */
14 #include "postgres.h"
15
16 #include "access/reloptions.h"
17 #include "catalog/pg_foreign_server.h"
18 #include "catalog/pg_foreign_table.h"
19 #include "catalog/pg_user_mapping.h"
20 #include "catalog/pg_type.h"
21 #include "commands/defrem.h"
22 #include "miscadmin.h"
23 #include "mysql_fdw.h"
24 #include "utils/lsyscache.h"
25
26 /*
27 * Describes the valid options for objects that use this wrapper.
28 */
29 struct MySQLFdwOption
30 {
31 const char *optname;
32 Oid optcontext; /* Oid of catalog in which option may appear */
33 };
34
35 /*
36 * Valid options for mysql_fdw.
37 */
38 static struct MySQLFdwOption valid_options[] =
39 {
40 /* Connection options */
41 {"host", ForeignServerRelationId},
42 {"port", ForeignServerRelationId},
43 {"init_command", ForeignServerRelationId},
44 {"username", UserMappingRelationId},
45 {"password", UserMappingRelationId},
46 {"dbname", ForeignTableRelationId},
47 {"table_name", ForeignTableRelationId},
48 {"secure_auth", ForeignServerRelationId},
49 {"max_blob_size", ForeignTableRelationId},
50 {"use_remote_estimate", ForeignServerRelationId},
51 {"ssl_key", ForeignServerRelationId},
52 {"ssl_cert", ForeignServerRelationId},
53 {"ssl_ca", ForeignServerRelationId},
54 {"ssl_capath", ForeignServerRelationId},
55 {"ssl_cipher", ForeignServerRelationId},
56
57 /* Sentinel */
58 {NULL, InvalidOid}
59 };
60
61 extern Datum mysql_fdw_validator(PG_FUNCTION_ARGS);
62
63 PG_FUNCTION_INFO_V1(mysql_fdw_validator);
64
65 /*
66 * Validate the generic options given to a FOREIGN DATA WRAPPER, SERVER,
67 * USER MAPPING or FOREIGN TABLE that uses file_fdw.
68 *
69 * Raise an ERROR if the option or its value is considered invalid.
70 */
71 Datum
mysql_fdw_validator(PG_FUNCTION_ARGS)72 mysql_fdw_validator(PG_FUNCTION_ARGS)
73 {
74 List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
75 Oid catalog = PG_GETARG_OID(1);
76 ListCell *cell;
77
78 /*
79 * Check that only options supported by mysql_fdw, and allowed for the
80 * current object type, are given.
81 */
82 foreach(cell, options_list)
83 {
84 DefElem *def = (DefElem *) lfirst(cell);
85
86 if (!mysql_is_valid_option(def->defname, catalog))
87 {
88 struct MySQLFdwOption *opt;
89 StringInfoData buf;
90
91 /*
92 * Unknown option specified, complain about it. Provide a hint
93 * with list of valid options for the object.
94 */
95 initStringInfo(&buf);
96 for (opt = valid_options; opt->optname; opt++)
97 {
98 if (catalog == opt->optcontext)
99 appendStringInfo(&buf, "%s%s", (buf.len > 0) ? ", " : "",
100 opt->optname);
101 }
102
103 ereport(ERROR,
104 (errcode(ERRCODE_FDW_INVALID_OPTION_NAME),
105 errmsg("invalid option \"%s\"", def->defname),
106 errhint("Valid options in this context are: %s",
107 buf.len ? buf.data : "<none>")));
108 }
109 }
110
111 PG_RETURN_VOID();
112 }
113
114 /*
115 * Check if the provided option is one of the valid options.
116 * context is the Oid of the catalog holding the object the option is for.
117 */
118 bool
mysql_is_valid_option(const char * option,Oid context)119 mysql_is_valid_option(const char *option, Oid context)
120 {
121 struct MySQLFdwOption *opt;
122
123 for (opt = valid_options; opt->optname; opt++)
124 {
125 if (context == opt->optcontext && strcmp(opt->optname, option) == 0)
126 return true;
127 }
128
129 return false;
130 }
131
132 /*
133 * Fetch the options for a mysql_fdw foreign table.
134 */
135 mysql_opt *
mysql_get_options(Oid foreignoid,bool is_foreigntable)136 mysql_get_options(Oid foreignoid, bool is_foreigntable)
137 {
138 ForeignTable *f_table;
139 ForeignServer *f_server;
140 UserMapping *f_mapping;
141 List *options;
142 ListCell *lc;
143 mysql_opt *opt;
144
145 opt = (mysql_opt *) palloc0(sizeof(mysql_opt));
146
147 /*
148 * Extract options from FDW objects.
149 */
150 if (is_foreigntable)
151 {
152 f_table = GetForeignTable(foreignoid);
153 f_server = GetForeignServer(f_table->serverid);
154 }
155 else
156 {
157 f_table = NULL;
158 f_server = GetForeignServer(foreignoid);
159 }
160
161 f_mapping = GetUserMapping(GetUserId(), f_server->serverid);
162
163 options = NIL;
164 if (f_table)
165 options = list_concat(options, f_table->options);
166
167 options = list_concat(options, f_server->options);
168 options = list_concat(options, f_mapping->options);
169
170 /* Default secure authentication is true */
171 opt->svr_sa = true;
172
173 opt->use_remote_estimate = false;
174
175 /* Loop through the options */
176 foreach(lc, options)
177 {
178 DefElem *def = (DefElem *) lfirst(lc);
179
180 if (strcmp(def->defname, "host") == 0)
181 opt->svr_address = defGetString(def);
182
183 if (strcmp(def->defname, "port") == 0)
184 opt->svr_port = atoi(defGetString(def));
185
186 if (strcmp(def->defname, "username") == 0)
187 opt->svr_username = defGetString(def);
188
189 if (strcmp(def->defname, "password") == 0)
190 opt->svr_password = defGetString(def);
191
192 if (strcmp(def->defname, "dbname") == 0)
193 opt->svr_database = defGetString(def);
194
195 if (strcmp(def->defname, "table_name") == 0)
196 opt->svr_table = defGetString(def);
197
198 if (strcmp(def->defname, "secure_auth") == 0)
199 opt->svr_sa = defGetBoolean(def);
200
201 if (strcmp(def->defname, "init_command") == 0)
202 opt->svr_init_command = defGetString(def);
203
204 if (strcmp(def->defname, "max_blob_size") == 0)
205 opt->max_blob_size = strtoul(defGetString(def), NULL, 0);
206
207 if (strcmp(def->defname, "use_remote_estimate") == 0)
208 opt->use_remote_estimate = defGetBoolean(def);
209
210 if (strcmp(def->defname, "ssl_key") == 0)
211 opt->ssl_key = defGetString(def);
212
213 if (strcmp(def->defname, "ssl_cert") == 0)
214 opt->ssl_cert = defGetString(def);
215
216 if (strcmp(def->defname, "ssl_ca") == 0)
217 opt->ssl_ca = defGetString(def);
218
219 if (strcmp(def->defname, "ssl_capath") == 0)
220 opt->ssl_capath = defGetString(def);
221
222 if (strcmp(def->defname, "ssl_cipher") == 0)
223 opt->ssl_cipher = defGetString(def);
224 }
225
226 /* Default values, if required */
227 if (!opt->svr_address)
228 opt->svr_address = "127.0.0.1";
229
230 if (!opt->svr_port)
231 opt->svr_port = MYSQL_SERVER_PORT;
232
233 /*
234 * When we don't have a table name or database name provided in the
235 * FOREIGN TABLE options, then use a foreign table name as the target table
236 * name and the namespace of the foreign table as a database name.
237 */
238 if (f_table)
239 {
240 if (!opt->svr_table)
241 opt->svr_table = get_rel_name(foreignoid);
242
243 if (!opt->svr_database)
244 opt->svr_database = get_namespace_name(get_rel_namespace(foreignoid));
245 }
246
247 return opt;
248 }
249