Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * foreigncmds.c
4 : : * foreign-data wrapper/server creation/manipulation commands
5 : : *
6 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : : *
8 : : *
9 : : * IDENTIFICATION
10 : : * src/backend/commands/foreigncmds.c
11 : : *
12 : : *-------------------------------------------------------------------------
13 : : */
14 : : #include "postgres.h"
15 : :
16 : : #include "access/htup_details.h"
17 : : #include "access/reloptions.h"
18 : : #include "access/table.h"
19 : : #include "access/xact.h"
20 : : #include "catalog/catalog.h"
21 : : #include "catalog/dependency.h"
22 : : #include "catalog/indexing.h"
23 : : #include "catalog/objectaccess.h"
24 : : #include "catalog/pg_foreign_data_wrapper.h"
25 : : #include "catalog/pg_foreign_server.h"
26 : : #include "catalog/pg_foreign_table.h"
27 : : #include "catalog/pg_proc.h"
28 : : #include "catalog/pg_type.h"
29 : : #include "catalog/pg_user_mapping.h"
30 : : #include "commands/defrem.h"
31 : : #include "foreign/fdwapi.h"
32 : : #include "foreign/foreign.h"
33 : : #include "miscadmin.h"
34 : : #include "parser/parse_func.h"
35 : : #include "tcop/utility.h"
36 : : #include "utils/acl.h"
37 : : #include "utils/builtins.h"
38 : : #include "utils/lsyscache.h"
39 : : #include "utils/rel.h"
40 : : #include "utils/syscache.h"
41 : :
42 : :
43 : : typedef struct
44 : : {
45 : : char *tablename;
46 : : char *cmd;
47 : : } import_error_callback_arg;
48 : :
49 : : /* Internal functions */
50 : : static void import_error_callback(void *arg);
51 : :
52 : :
53 : : /*
54 : : * Convert a DefElem list to the text array format that is used in
55 : : * pg_foreign_data_wrapper, pg_foreign_server, pg_user_mapping, and
56 : : * pg_foreign_table.
57 : : *
58 : : * Returns the array in the form of a Datum, or PointerGetDatum(NULL)
59 : : * if the list is empty.
60 : : *
61 : : * Note: The array is usually stored to database without further
62 : : * processing, hence any validation should be done before this
63 : : * conversion.
64 : : */
65 : : static Datum
6346 peter_e@gmx.net 66 :CBC 945 : optionListToArray(List *options)
67 : : {
6172 bruce@momjian.us 68 : 945 : ArrayBuildState *astate = NULL;
69 : : ListCell *cell;
70 : :
6345 tgl@sss.pgh.pa.us 71 [ + + + + : 2291 : foreach(cell, options)
+ + ]
72 : : {
6346 peter_e@gmx.net 73 : 1347 : DefElem *def = lfirst(cell);
74 : : const char *name;
75 : : const char *value;
76 : : Size len;
77 : : text *t;
78 : :
337 tgl@sss.pgh.pa.us 79 : 1347 : name = def->defname;
6346 peter_e@gmx.net 80 : 1347 : value = defGetString(def);
81 : :
82 : : /* Insist that name not contain "=", else "a=b=c" is ambiguous */
337 tgl@sss.pgh.pa.us 83 [ + + ]: 1347 : if (strchr(name, '=') != NULL)
84 [ + - ]: 1 : ereport(ERROR,
85 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
86 : : errmsg("invalid option name \"%s\": must not contain \"=\"",
87 : : name)));
88 : :
89 : 1346 : len = VARHDRSZ + strlen(name) + 1 + strlen(value);
90 : : /* +1 leaves room for sprintf's trailing null */
6346 peter_e@gmx.net 91 : 1346 : t = palloc(len + 1);
92 : 1346 : SET_VARSIZE(t, len);
337 tgl@sss.pgh.pa.us 93 : 1346 : sprintf(VARDATA(t), "%s=%s", name, value);
94 : :
6346 peter_e@gmx.net 95 : 1346 : astate = accumArrayResult(astate, PointerGetDatum(t),
96 : : false, TEXTOID,
97 : : CurrentMemoryContext);
98 : : }
99 : :
100 [ + + ]: 944 : if (astate)
101 : 597 : return makeArrayResult(astate, CurrentMemoryContext);
102 : :
103 : 347 : return PointerGetDatum(NULL);
104 : : }
105 : :
106 : :
107 : : /*
108 : : * Transform a list of DefElem into text array format. This is substantially
109 : : * the same thing as optionListToArray(), except we recognize SET/ADD/DROP
110 : : * actions for modifying an existing list of options, which is passed in
111 : : * Datum form as oldOptions. Also, if fdwvalidator isn't InvalidOid
112 : : * it specifies a validator function to call on the result.
113 : : *
114 : : * Returns the array in the form of a Datum, or PointerGetDatum(NULL)
115 : : * if the list is empty.
116 : : *
117 : : * This is used by CREATE/ALTER of FOREIGN DATA WRAPPER/SERVER/USER MAPPING/
118 : : * FOREIGN TABLE.
119 : : */
120 : : Datum
5977 heikki.linnakangas@i 121 : 961 : transformGenericOptions(Oid catalogId,
122 : : Datum oldOptions,
123 : : List *options,
124 : : Oid fdwvalidator)
125 : : {
6172 bruce@momjian.us 126 : 961 : List *resultOptions = untransformRelOptions(oldOptions);
127 : : ListCell *optcell;
128 : : Datum result;
129 : :
6240 tgl@sss.pgh.pa.us 130 [ + + + + : 1909 : foreach(optcell, options)
+ + ]
131 : : {
6172 bruce@momjian.us 132 : 964 : DefElem *od = lfirst(optcell);
133 : : ListCell *cell;
134 : :
135 : : /*
136 : : * Find the element in resultOptions. We need this for validation in
137 : : * all cases.
138 : : */
139 [ + + + + : 2121 : foreach(cell, resultOptions)
+ + ]
140 : : {
141 : 1289 : DefElem *def = lfirst(cell);
142 : :
6240 tgl@sss.pgh.pa.us 143 [ + + ]: 1289 : if (strcmp(def->defname, od->defname) == 0)
6346 peter_e@gmx.net 144 : 132 : break;
145 : : }
146 : :
147 : : /*
148 : : * It is possible to perform multiple SET/DROP actions on the same
149 : : * option. The standard permits this, as long as the options to be
150 : : * added are unique. Note that an unspecified action is taken to be
151 : : * ADD.
152 : : */
6240 tgl@sss.pgh.pa.us 153 [ + + + - ]: 964 : switch (od->defaction)
154 : : {
155 : 68 : case DEFELEM_DROP:
6346 peter_e@gmx.net 156 [ + + ]: 68 : if (!cell)
157 [ + - ]: 4 : ereport(ERROR,
158 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
159 : : errmsg("option \"%s\" not found",
160 : : od->defname)));
2486 tgl@sss.pgh.pa.us 161 : 64 : resultOptions = list_delete_cell(resultOptions, cell);
6346 peter_e@gmx.net 162 : 64 : break;
163 : :
6240 tgl@sss.pgh.pa.us 164 : 64 : case DEFELEM_SET:
6346 peter_e@gmx.net 165 [ + + ]: 64 : if (!cell)
166 [ + - ]: 4 : ereport(ERROR,
167 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
168 : : errmsg("option \"%s\" not found",
169 : : od->defname)));
6240 tgl@sss.pgh.pa.us 170 : 60 : lfirst(cell) = od;
6346 peter_e@gmx.net 171 : 60 : break;
172 : :
6240 tgl@sss.pgh.pa.us 173 : 832 : case DEFELEM_ADD:
174 : : case DEFELEM_UNSPEC:
6346 peter_e@gmx.net 175 [ + + ]: 832 : if (cell)
176 [ + - ]: 8 : ereport(ERROR,
177 : : (errcode(ERRCODE_DUPLICATE_OBJECT),
178 : : errmsg("option \"%s\" provided more than once",
179 : : od->defname)));
6240 tgl@sss.pgh.pa.us 180 : 824 : resultOptions = lappend(resultOptions, od);
6346 peter_e@gmx.net 181 : 824 : break;
182 : :
6346 peter_e@gmx.net 183 :UBC 0 : default:
184 [ # # ]: 0 : elog(ERROR, "unrecognized action %d on option \"%s\"",
185 : : (int) od->defaction, od->defname);
186 : : break;
187 : : }
188 : : }
189 : :
6279 peter_e@gmx.net 190 :CBC 945 : result = optionListToArray(resultOptions);
191 : :
5418 tgl@sss.pgh.pa.us 192 [ + + ]: 944 : if (OidIsValid(fdwvalidator))
193 : : {
5077 bruce@momjian.us 194 : 509 : Datum valarg = result;
195 : :
196 : : /*
197 : : * Pass a null options list as an empty array, so that validators
198 : : * don't have to be declared non-strict to handle the case.
199 : : */
5418 tgl@sss.pgh.pa.us 200 [ + + ]: 509 : if (DatumGetPointer(valarg) == NULL)
201 : 80 : valarg = PointerGetDatum(construct_empty_array(TEXTOID));
202 : 509 : OidFunctionCall2(fdwvalidator, valarg, ObjectIdGetDatum(catalogId));
203 : : }
204 : :
6279 peter_e@gmx.net 205 : 861 : return result;
206 : : }
207 : :
208 : :
209 : : /*
210 : : * Internal workhorse for changing a data wrapper's owner.
211 : : *
212 : : * Allow this only for superusers; also the new owner must be a
213 : : * superuser.
214 : : */
215 : : static void
5187 alvherre@alvh.no-ip. 216 : 13 : AlterForeignDataWrapperOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
217 : : {
218 : : Form_pg_foreign_data_wrapper form;
219 : : Datum repl_val[Natts_pg_foreign_data_wrapper];
220 : : bool repl_null[Natts_pg_foreign_data_wrapper];
221 : : bool repl_repl[Natts_pg_foreign_data_wrapper];
222 : : Acl *newAcl;
223 : : Datum aclDatum;
224 : : bool isNull;
225 : :
226 : 13 : form = (Form_pg_foreign_data_wrapper) GETSTRUCT(tup);
227 : :
228 : : /* Must be a superuser to change a FDW owner */
6346 peter_e@gmx.net 229 [ + + ]: 13 : if (!superuser())
230 [ + - ]: 4 : ereport(ERROR,
231 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
232 : : errmsg("permission denied to change owner of foreign-data wrapper \"%s\"",
233 : : NameStr(form->fdwname)),
234 : : errhint("Must be superuser to change owner of a foreign-data wrapper.")));
235 : :
236 : : /* New owner must also be a superuser */
237 [ + + ]: 9 : if (!superuser_arg(newOwnerId))
238 [ + - ]: 4 : ereport(ERROR,
239 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
240 : : errmsg("permission denied to change owner of foreign-data wrapper \"%s\"",
241 : : NameStr(form->fdwname)),
242 : : errhint("The owner of a foreign-data wrapper must be a superuser.")));
243 : :
244 [ + + ]: 5 : if (form->fdwowner != newOwnerId)
245 : : {
4121 bruce@momjian.us 246 : 4 : memset(repl_null, false, sizeof(repl_null));
247 : 4 : memset(repl_repl, false, sizeof(repl_repl));
248 : :
249 : 4 : repl_repl[Anum_pg_foreign_data_wrapper_fdwowner - 1] = true;
250 : 4 : repl_val[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(newOwnerId);
251 : :
252 : 4 : aclDatum = heap_getattr(tup,
253 : : Anum_pg_foreign_data_wrapper_fdwacl,
254 : : RelationGetDescr(rel),
255 : : &isNull);
256 : : /* Null ACLs do not require changes */
257 [ - + ]: 4 : if (!isNull)
258 : : {
4121 bruce@momjian.us 259 :UBC 0 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
260 : : form->fdwowner, newOwnerId);
261 : 0 : repl_repl[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
262 : 0 : repl_val[Anum_pg_foreign_data_wrapper_fdwacl - 1] = PointerGetDatum(newAcl);
263 : : }
264 : :
4121 bruce@momjian.us 265 :CBC 4 : tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null,
266 : : repl_repl);
267 : :
3381 alvherre@alvh.no-ip. 268 : 4 : CatalogTupleUpdate(rel, &tup->t_self, tup);
269 : :
270 : : /* Update owner dependency reference */
6346 peter_e@gmx.net 271 : 4 : changeDependencyOnOwner(ForeignDataWrapperRelationId,
272 : : form->oid,
273 : : newOwnerId);
274 : : }
275 : :
4797 rhaas@postgresql.org 276 [ - + ]: 5 : InvokeObjectPostAlterHook(ForeignDataWrapperRelationId,
277 : : form->oid, 0);
5187 alvherre@alvh.no-ip. 278 : 5 : }
279 : :
280 : : /*
281 : : * Change foreign-data wrapper owner -- by name
282 : : *
283 : : * Note restrictions in the "_internal" function, above.
284 : : */
285 : : ObjectAddress
286 : 13 : AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId)
287 : : {
288 : : Oid fdwId;
289 : : HeapTuple tup;
290 : : Relation rel;
291 : : ObjectAddress address;
292 : : Form_pg_foreign_data_wrapper form;
293 : :
294 : :
2661 andres@anarazel.de 295 : 13 : rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
296 : :
5187 alvherre@alvh.no-ip. 297 : 13 : tup = SearchSysCacheCopy1(FOREIGNDATAWRAPPERNAME, CStringGetDatum(name));
298 : :
299 [ - + ]: 13 : if (!HeapTupleIsValid(tup))
5187 alvherre@alvh.no-ip. 300 [ # # ]:UBC 0 : ereport(ERROR,
301 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
302 : : errmsg("foreign-data wrapper \"%s\" does not exist", name)));
303 : :
2723 andres@anarazel.de 304 :CBC 13 : form = (Form_pg_foreign_data_wrapper) GETSTRUCT(tup);
305 : 13 : fdwId = form->oid;
306 : :
5187 alvherre@alvh.no-ip. 307 : 13 : AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId);
308 : :
4081 309 : 5 : ObjectAddressSet(address, ForeignDataWrapperRelationId, fdwId);
310 : :
6346 peter_e@gmx.net 311 : 5 : heap_freetuple(tup);
312 : :
2661 andres@anarazel.de 313 : 5 : table_close(rel, RowExclusiveLock);
314 : :
4081 alvherre@alvh.no-ip. 315 : 5 : return address;
316 : : }
317 : :
318 : : /*
319 : : * Change foreign-data wrapper owner -- by OID
320 : : *
321 : : * Note restrictions in the "_internal" function, above.
322 : : */
323 : : void
5187 alvherre@alvh.no-ip. 324 :UBC 0 : AlterForeignDataWrapperOwner_oid(Oid fwdId, Oid newOwnerId)
325 : : {
326 : : HeapTuple tup;
327 : : Relation rel;
328 : :
2661 andres@anarazel.de 329 : 0 : rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
330 : :
5187 alvherre@alvh.no-ip. 331 : 0 : tup = SearchSysCacheCopy1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fwdId));
332 : :
6346 peter_e@gmx.net 333 [ # # ]: 0 : if (!HeapTupleIsValid(tup))
6172 bruce@momjian.us 334 [ # # ]: 0 : ereport(ERROR,
335 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
336 : : errmsg("foreign-data wrapper with OID %u does not exist", fwdId)));
337 : :
5187 alvherre@alvh.no-ip. 338 : 0 : AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId);
339 : :
340 : 0 : heap_freetuple(tup);
341 : :
2661 andres@anarazel.de 342 : 0 : table_close(rel, RowExclusiveLock);
5187 alvherre@alvh.no-ip. 343 : 0 : }
344 : :
345 : : /*
346 : : * Internal workhorse for changing a foreign server's owner
347 : : */
348 : : static void
5187 alvherre@alvh.no-ip. 349 :CBC 53 : AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
350 : : {
351 : : Form_pg_foreign_server form;
352 : : Datum repl_val[Natts_pg_foreign_server];
353 : : bool repl_null[Natts_pg_foreign_server];
354 : : bool repl_repl[Natts_pg_foreign_server];
355 : : Acl *newAcl;
356 : : Datum aclDatum;
357 : : bool isNull;
358 : :
6346 peter_e@gmx.net 359 : 53 : form = (Form_pg_foreign_server) GETSTRUCT(tup);
360 : :
361 [ + + ]: 53 : if (form->srvowner != newOwnerId)
362 : : {
363 : : /* Superusers can always do it */
364 [ + + ]: 48 : if (!superuser())
365 : : {
366 : : Oid srvId;
367 : : AclResult aclresult;
368 : :
2723 andres@anarazel.de 369 : 20 : srvId = form->oid;
370 : :
371 : : /* Must be owner */
1269 peter@eisentraut.org 372 [ + + ]: 20 : if (!object_ownercheck(ForeignServerRelationId, srvId, GetUserId()))
3076 peter_e@gmx.net 373 : 8 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FOREIGN_SERVER,
5187 alvherre@alvh.no-ip. 374 : 8 : NameStr(form->srvname));
375 : :
376 : : /* Must be able to become new owner */
1264 rhaas@postgresql.org 377 : 12 : check_can_set_role(GetUserId(), newOwnerId);
378 : :
379 : : /* New owner must have USAGE privilege on foreign-data wrapper */
1269 peter@eisentraut.org 380 : 8 : aclresult = object_aclcheck(ForeignDataWrapperRelationId, form->srvfdw, newOwnerId, ACL_USAGE);
6346 peter_e@gmx.net 381 [ + + ]: 8 : if (aclresult != ACLCHECK_OK)
382 : : {
383 : 4 : ForeignDataWrapper *fdw = GetForeignDataWrapper(form->srvfdw);
384 : :
3076 385 : 4 : aclcheck_error(aclresult, OBJECT_FDW, fdw->fdwname);
386 : : }
387 : : }
388 : :
4121 bruce@momjian.us 389 : 32 : memset(repl_null, false, sizeof(repl_null));
390 : 32 : memset(repl_repl, false, sizeof(repl_repl));
391 : :
392 : 32 : repl_repl[Anum_pg_foreign_server_srvowner - 1] = true;
393 : 32 : repl_val[Anum_pg_foreign_server_srvowner - 1] = ObjectIdGetDatum(newOwnerId);
394 : :
395 : 32 : aclDatum = heap_getattr(tup,
396 : : Anum_pg_foreign_server_srvacl,
397 : : RelationGetDescr(rel),
398 : : &isNull);
399 : : /* Null ACLs do not require changes */
400 [ + + ]: 32 : if (!isNull)
401 : : {
402 : 12 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
403 : : form->srvowner, newOwnerId);
404 : 12 : repl_repl[Anum_pg_foreign_server_srvacl - 1] = true;
405 : 12 : repl_val[Anum_pg_foreign_server_srvacl - 1] = PointerGetDatum(newAcl);
406 : : }
407 : :
408 : 32 : tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null,
409 : : repl_repl);
410 : :
3381 alvherre@alvh.no-ip. 411 : 32 : CatalogTupleUpdate(rel, &tup->t_self, tup);
412 : :
413 : : /* Update owner dependency reference */
2723 andres@anarazel.de 414 : 32 : changeDependencyOnOwner(ForeignServerRelationId, form->oid,
415 : : newOwnerId);
416 : : }
417 : :
4797 rhaas@postgresql.org 418 [ - + ]: 37 : InvokeObjectPostAlterHook(ForeignServerRelationId,
419 : : form->oid, 0);
5187 alvherre@alvh.no-ip. 420 : 37 : }
421 : :
422 : : /*
423 : : * Change foreign server owner -- by name
424 : : */
425 : : ObjectAddress
426 : 45 : AlterForeignServerOwner(const char *name, Oid newOwnerId)
427 : : {
428 : : Oid servOid;
429 : : HeapTuple tup;
430 : : Relation rel;
431 : : ObjectAddress address;
432 : : Form_pg_foreign_server form;
433 : :
2661 andres@anarazel.de 434 : 45 : rel = table_open(ForeignServerRelationId, RowExclusiveLock);
435 : :
5187 alvherre@alvh.no-ip. 436 : 45 : tup = SearchSysCacheCopy1(FOREIGNSERVERNAME, CStringGetDatum(name));
437 : :
438 [ - + ]: 45 : if (!HeapTupleIsValid(tup))
5187 alvherre@alvh.no-ip. 439 [ # # ]:UBC 0 : ereport(ERROR,
440 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
441 : : errmsg("server \"%s\" does not exist", name)));
442 : :
2723 andres@anarazel.de 443 :CBC 45 : form = (Form_pg_foreign_server) GETSTRUCT(tup);
444 : 45 : servOid = form->oid;
445 : :
5187 alvherre@alvh.no-ip. 446 : 45 : AlterForeignServerOwner_internal(rel, tup, newOwnerId);
447 : :
4081 448 : 29 : ObjectAddressSet(address, ForeignServerRelationId, servOid);
449 : :
6346 peter_e@gmx.net 450 : 29 : heap_freetuple(tup);
451 : :
2661 andres@anarazel.de 452 : 29 : table_close(rel, RowExclusiveLock);
453 : :
4081 alvherre@alvh.no-ip. 454 : 29 : return address;
455 : : }
456 : :
457 : : /*
458 : : * Change foreign server owner -- by OID
459 : : */
460 : : void
5187 461 : 8 : AlterForeignServerOwner_oid(Oid srvId, Oid newOwnerId)
462 : : {
463 : : HeapTuple tup;
464 : : Relation rel;
465 : :
2661 andres@anarazel.de 466 : 8 : rel = table_open(ForeignServerRelationId, RowExclusiveLock);
467 : :
5187 alvherre@alvh.no-ip. 468 : 8 : tup = SearchSysCacheCopy1(FOREIGNSERVEROID, ObjectIdGetDatum(srvId));
469 : :
470 [ - + ]: 8 : if (!HeapTupleIsValid(tup))
5187 alvherre@alvh.no-ip. 471 [ # # ]:UBC 0 : ereport(ERROR,
472 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
473 : : errmsg("foreign server with OID %u does not exist", srvId)));
474 : :
5187 alvherre@alvh.no-ip. 475 :CBC 8 : AlterForeignServerOwner_internal(rel, tup, newOwnerId);
476 : :
477 : 8 : heap_freetuple(tup);
478 : :
2661 andres@anarazel.de 479 : 8 : table_close(rel, RowExclusiveLock);
5187 alvherre@alvh.no-ip. 480 : 8 : }
481 : :
482 : : /*
483 : : * Convert a handler function name passed from the parser to an Oid.
484 : : */
485 : : static Oid
5554 tgl@sss.pgh.pa.us 486 : 30 : lookup_fdw_handler_func(DefElem *handler)
487 : : {
488 : : Oid handlerOid;
489 : :
490 [ + - - + ]: 30 : if (handler == NULL || handler->arg == NULL)
5554 tgl@sss.pgh.pa.us 491 :UBC 0 : return InvalidOid;
492 : :
493 : : /* handlers have no arguments */
2366 alvherre@alvh.no-ip. 494 :CBC 30 : handlerOid = LookupFuncName((List *) handler->arg, 0, NULL, false);
495 : :
496 : : /* check that handler has correct return type */
5554 tgl@sss.pgh.pa.us 497 [ + + ]: 30 : if (get_func_rettype(handlerOid) != FDW_HANDLEROID)
498 [ + - ]: 8 : ereport(ERROR,
499 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
500 : : errmsg("function %s must return type %s",
501 : : NameListToString((List *) handler->arg), "fdw_handler")));
502 : :
503 : 22 : return handlerOid;
504 : : }
505 : :
506 : : /*
507 : : * Convert a validator function name passed from the parser to an Oid.
508 : : */
509 : : static Oid
510 : 41 : lookup_fdw_validator_func(DefElem *validator)
511 : : {
512 : : Oid funcargtypes[2];
513 : :
514 [ + - + + ]: 41 : if (validator == NULL || validator->arg == NULL)
515 : 4 : return InvalidOid;
516 : :
517 : : /* validators take text[], oid */
6279 peter_e@gmx.net 518 : 37 : funcargtypes[0] = TEXTARRAYOID;
519 : 37 : funcargtypes[1] = OIDOID;
520 : :
5554 tgl@sss.pgh.pa.us 521 : 37 : return LookupFuncName((List *) validator->arg, 2, funcargtypes, false);
522 : : /* validator's return value is ignored, so we don't check the type */
523 : : }
524 : :
525 : : /*
526 : : * Convert a connection string function name passed from the parser to an Oid.
527 : : */
528 : : static Oid
60 jdavis@postgresql.or 529 :GNC 17 : lookup_fdw_connection_func(DefElem *connection)
530 : : {
531 : : Oid connectionOid;
532 : : Oid funcargtypes[3];
533 : :
534 [ + - + + ]: 17 : if (connection == NULL || connection->arg == NULL)
535 : 4 : return InvalidOid;
536 : :
537 : : /* connection string functions take user oid, server oid */
538 : 13 : funcargtypes[0] = OIDOID;
539 : 13 : funcargtypes[1] = OIDOID;
540 : 13 : funcargtypes[2] = INTERNALOID;
541 : :
542 : 13 : connectionOid = LookupFuncName((List *) connection->arg, 3, funcargtypes, false);
543 : :
544 : : /* check that connection string function has correct return type */
545 [ - + ]: 13 : if (get_func_rettype(connectionOid) != TEXTOID)
60 jdavis@postgresql.or 546 [ # # ]:UNC 0 : ereport(ERROR,
547 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
548 : : errmsg("function %s must return type %s",
549 : : NameListToString((List *) connection->arg), "text")));
550 : :
60 jdavis@postgresql.or 551 :GNC 13 : return connectionOid;
552 : : }
553 : :
554 : : /*
555 : : * Process function options of CREATE/ALTER FDW
556 : : */
557 : : static void
1755 dean.a.rasheed@gmail 558 :CBC 203 : parse_func_options(ParseState *pstate, List *func_options,
559 : : bool *handler_given, Oid *fdwhandler,
560 : : bool *validator_given, Oid *fdwvalidator,
561 : : bool *connection_given, Oid *fdwconnection)
562 : : {
563 : : ListCell *cell;
564 : :
5554 tgl@sss.pgh.pa.us 565 : 203 : *handler_given = false;
566 : 203 : *validator_given = false;
60 jdavis@postgresql.or 567 :GNC 203 : *connection_given = false;
568 : : /* return InvalidOid if not given */
5554 tgl@sss.pgh.pa.us 569 :CBC 203 : *fdwhandler = InvalidOid;
570 : 203 : *fdwvalidator = InvalidOid;
60 jdavis@postgresql.or 571 :GNC 203 : *fdwconnection = InvalidOid;
572 : :
5554 tgl@sss.pgh.pa.us 573 [ + + + + :CBC 275 : foreach(cell, func_options)
+ + ]
574 : : {
575 : 96 : DefElem *def = (DefElem *) lfirst(cell);
576 : :
577 [ + + ]: 96 : if (strcmp(def->defname, "handler") == 0)
578 : : {
579 [ + + ]: 38 : if (*handler_given)
1755 dean.a.rasheed@gmail 580 : 8 : errorConflictingDefElem(def, pstate);
5554 tgl@sss.pgh.pa.us 581 : 30 : *handler_given = true;
582 : 30 : *fdwhandler = lookup_fdw_handler_func(def);
583 : : }
584 [ + + ]: 58 : else if (strcmp(def->defname, "validator") == 0)
585 : : {
586 [ - + ]: 41 : if (*validator_given)
1755 dean.a.rasheed@gmail 587 :UBC 0 : errorConflictingDefElem(def, pstate);
5554 tgl@sss.pgh.pa.us 588 :CBC 41 : *validator_given = true;
589 : 41 : *fdwvalidator = lookup_fdw_validator_func(def);
590 : : }
60 jdavis@postgresql.or 591 [ + - ]:GNC 17 : else if (strcmp(def->defname, "connection") == 0)
592 : : {
593 [ - + ]: 17 : if (*connection_given)
60 jdavis@postgresql.or 594 :UNC 0 : errorConflictingDefElem(def, pstate);
60 jdavis@postgresql.or 595 :GNC 17 : *connection_given = true;
596 : 17 : *fdwconnection = lookup_fdw_connection_func(def);
597 : : }
598 : : else
5554 tgl@sss.pgh.pa.us 599 [ # # ]:UBC 0 : elog(ERROR, "option \"%s\" not recognized",
600 : : def->defname);
601 : : }
5554 tgl@sss.pgh.pa.us 602 :CBC 179 : }
603 : :
604 : : /*
605 : : * Create a foreign-data wrapper
606 : : */
607 : : ObjectAddress
1755 dean.a.rasheed@gmail 608 : 135 : CreateForeignDataWrapper(ParseState *pstate, CreateFdwStmt *stmt)
609 : : {
610 : : Relation rel;
611 : : Datum values[Natts_pg_foreign_data_wrapper];
612 : : bool nulls[Natts_pg_foreign_data_wrapper];
613 : : HeapTuple tuple;
614 : : Oid fdwId;
615 : : bool handler_given;
616 : : bool validator_given;
617 : : bool connection_given;
618 : : Oid fdwhandler;
619 : : Oid fdwvalidator;
620 : : Oid fdwconnection;
621 : : Datum fdwoptions;
622 : : Oid ownerId;
623 : : ObjectAddress myself;
624 : : ObjectAddress referenced;
625 : :
2661 andres@anarazel.de 626 : 135 : rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
627 : :
628 : : /* Must be superuser */
6346 peter_e@gmx.net 629 [ + + ]: 135 : if (!superuser())
630 [ + - ]: 13 : ereport(ERROR,
631 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
632 : : errmsg("permission denied to create foreign-data wrapper \"%s\"",
633 : : stmt->fdwname),
634 : : errhint("Must be superuser to create a foreign-data wrapper.")));
635 : :
636 : : /* For now the owner cannot be specified on create. Use effective user ID. */
637 : 122 : ownerId = GetUserId();
638 : :
639 : : /*
640 : : * Check that there is no other foreign-data wrapper by this name.
641 : : */
642 [ + + ]: 122 : if (GetForeignDataWrapperByName(stmt->fdwname, true) != NULL)
643 [ + - ]: 4 : ereport(ERROR,
644 : : (errcode(ERRCODE_DUPLICATE_OBJECT),
645 : : errmsg("foreign-data wrapper \"%s\" already exists",
646 : : stmt->fdwname)));
647 : :
648 : : /*
649 : : * Insert tuple into pg_foreign_data_wrapper.
650 : : */
6345 tgl@sss.pgh.pa.us 651 : 118 : memset(values, 0, sizeof(values));
652 : 118 : memset(nulls, false, sizeof(nulls));
653 : :
2723 andres@anarazel.de 654 : 118 : fdwId = GetNewOidWithIndex(rel, ForeignDataWrapperOidIndexId,
655 : : Anum_pg_foreign_data_wrapper_oid);
656 : 118 : values[Anum_pg_foreign_data_wrapper_oid - 1] = ObjectIdGetDatum(fdwId);
6346 peter_e@gmx.net 657 : 118 : values[Anum_pg_foreign_data_wrapper_fdwname - 1] =
658 : 118 : DirectFunctionCall1(namein, CStringGetDatum(stmt->fdwname));
659 : 118 : values[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(ownerId);
660 : :
661 : : /* Lookup handler and validator functions, if given */
1755 dean.a.rasheed@gmail 662 : 118 : parse_func_options(pstate, stmt->func_options,
663 : : &handler_given, &fdwhandler,
664 : : &validator_given, &fdwvalidator,
665 : : &connection_given, &fdwconnection);
666 : :
5554 tgl@sss.pgh.pa.us 667 : 106 : values[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fdwhandler);
668 : 106 : values[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fdwvalidator);
60 jdavis@postgresql.or 669 :GNC 106 : values[Anum_pg_foreign_data_wrapper_fdwconnection - 1] = ObjectIdGetDatum(fdwconnection);
670 : :
6279 peter_e@gmx.net 671 :CBC 106 : nulls[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
672 : :
5977 heikki.linnakangas@i 673 : 106 : fdwoptions = transformGenericOptions(ForeignDataWrapperRelationId,
674 : : PointerGetDatum(NULL),
675 : : stmt->options,
676 : : fdwvalidator);
677 : :
223 peter@eisentraut.org 678 [ + + ]:GNC 102 : if (DatumGetPointer(fdwoptions) != NULL)
6346 peter_e@gmx.net 679 :CBC 12 : values[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = fdwoptions;
680 : : else
681 : 90 : nulls[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
682 : :
683 : 102 : tuple = heap_form_tuple(rel->rd_att, values, nulls);
684 : :
2723 andres@anarazel.de 685 : 102 : CatalogTupleInsert(rel, tuple);
686 : :
6346 peter_e@gmx.net 687 : 102 : heap_freetuple(tuple);
688 : :
689 : : /* record dependencies */
5565 tgl@sss.pgh.pa.us 690 : 102 : myself.classId = ForeignDataWrapperRelationId;
691 : 102 : myself.objectId = fdwId;
692 : 102 : myself.objectSubId = 0;
693 : :
5554 694 [ + + ]: 102 : if (OidIsValid(fdwhandler))
695 : : {
696 : 10 : referenced.classId = ProcedureRelationId;
697 : 10 : referenced.objectId = fdwhandler;
698 : 10 : referenced.objectSubId = 0;
699 : 10 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
700 : : }
701 : :
702 [ + + ]: 102 : if (OidIsValid(fdwvalidator))
703 : : {
6279 peter_e@gmx.net 704 : 21 : referenced.classId = ProcedureRelationId;
705 : 21 : referenced.objectId = fdwvalidator;
706 : 21 : referenced.objectSubId = 0;
707 : 21 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
708 : : }
709 : :
46 jdavis@postgresql.or 710 [ - + ]:GNC 102 : if (OidIsValid(fdwconnection))
711 : : {
46 jdavis@postgresql.or 712 :UNC 0 : referenced.classId = ProcedureRelationId;
713 : 0 : referenced.objectId = fdwconnection;
714 : 0 : referenced.objectSubId = 0;
715 : 0 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
716 : : }
717 : :
6346 peter_e@gmx.net 718 :CBC 102 : recordDependencyOnOwner(ForeignDataWrapperRelationId, fdwId, ownerId);
719 : :
720 : : /* dependency on extension */
5400 tgl@sss.pgh.pa.us 721 : 102 : recordDependencyOnCurrentExtension(&myself, false);
722 : :
723 : : /* Post creation hook for new foreign data wrapper */
4808 rhaas@postgresql.org 724 [ - + ]: 102 : InvokeObjectPostCreateHook(ForeignDataWrapperRelationId, fdwId, 0);
725 : :
2661 andres@anarazel.de 726 : 102 : table_close(rel, RowExclusiveLock);
727 : :
4081 alvherre@alvh.no-ip. 728 : 102 : return myself;
729 : : }
730 : :
731 : :
732 : : /*
733 : : * Alter foreign-data wrapper
734 : : */
735 : : ObjectAddress
1755 dean.a.rasheed@gmail 736 : 101 : AlterForeignDataWrapper(ParseState *pstate, AlterFdwStmt *stmt)
737 : : {
738 : : Relation rel;
739 : : HeapTuple tp;
740 : : Form_pg_foreign_data_wrapper fdwForm;
741 : : Datum repl_val[Natts_pg_foreign_data_wrapper];
742 : : bool repl_null[Natts_pg_foreign_data_wrapper];
743 : : bool repl_repl[Natts_pg_foreign_data_wrapper];
744 : : Oid fdwId;
745 : : bool isnull;
746 : : Datum datum;
747 : : bool handler_given;
748 : : bool validator_given;
749 : : bool connection_given;
750 : : Oid fdwhandler;
751 : : Oid fdwvalidator;
752 : : Oid fdwconnection;
753 : : ObjectAddress myself;
754 : :
2661 andres@anarazel.de 755 : 101 : rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
756 : :
757 : : /* Must be superuser */
6346 peter_e@gmx.net 758 [ + + ]: 101 : if (!superuser())
759 [ + - ]: 16 : ereport(ERROR,
760 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
761 : : errmsg("permission denied to alter foreign-data wrapper \"%s\"",
762 : : stmt->fdwname),
763 : : errhint("Must be superuser to alter a foreign-data wrapper.")));
764 : :
5924 rhaas@postgresql.org 765 : 85 : tp = SearchSysCacheCopy1(FOREIGNDATAWRAPPERNAME,
766 : : CStringGetDatum(stmt->fdwname));
767 : :
6346 peter_e@gmx.net 768 [ - + ]: 85 : if (!HeapTupleIsValid(tp))
6346 peter_e@gmx.net 769 [ # # ]:UBC 0 : ereport(ERROR,
770 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
771 : : errmsg("foreign-data wrapper \"%s\" does not exist", stmt->fdwname)));
772 : :
5554 tgl@sss.pgh.pa.us 773 :CBC 85 : fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
2723 andres@anarazel.de 774 : 85 : fdwId = fdwForm->oid;
775 : :
6346 peter_e@gmx.net 776 : 85 : memset(repl_val, 0, sizeof(repl_val));
777 : 85 : memset(repl_null, false, sizeof(repl_null));
778 : 85 : memset(repl_repl, false, sizeof(repl_repl));
779 : :
1755 dean.a.rasheed@gmail 780 : 85 : parse_func_options(pstate, stmt->func_options,
781 : : &handler_given, &fdwhandler,
782 : : &validator_given, &fdwvalidator,
783 : : &connection_given, &fdwconnection);
784 : :
5554 tgl@sss.pgh.pa.us 785 [ + + ]: 73 : if (handler_given)
786 : : {
787 : 4 : repl_val[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fdwhandler);
788 : 4 : repl_repl[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = true;
789 : :
790 : : /*
791 : : * It could be that the behavior of accessing foreign table changes
792 : : * with the new handler. Warn about this.
793 : : */
794 [ + - ]: 4 : ereport(WARNING,
795 : : (errmsg("changing the foreign-data wrapper handler can change behavior of existing foreign tables")));
796 : : }
797 : : else
798 : : {
799 : : /* handler unchanged */
47 jdavis@postgresql.or 800 : 69 : fdwhandler = fdwForm->fdwhandler;
801 : : }
802 : :
5554 tgl@sss.pgh.pa.us 803 [ + + ]: 73 : if (validator_given)
804 : : {
6279 peter_e@gmx.net 805 : 12 : repl_val[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fdwvalidator);
806 : 12 : repl_repl[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = true;
807 : :
808 : : /*
809 : : * It could be that existing options for the FDW or dependent SERVER,
810 : : * USER MAPPING or FOREIGN TABLE objects are no longer valid according
811 : : * to the new validator. Warn about this.
812 : : */
5554 tgl@sss.pgh.pa.us 813 [ + + ]: 12 : if (OidIsValid(fdwvalidator))
6279 peter_e@gmx.net 814 [ + - ]: 8 : ereport(WARNING,
815 : : (errmsg("changing the foreign-data wrapper validator can cause "
816 : : "the options for dependent objects to become invalid")));
817 : : }
818 : : else
819 : : {
820 : : /*
821 : : * Validator is not changed, but we need it for validating options.
822 : : */
5554 tgl@sss.pgh.pa.us 823 : 61 : fdwvalidator = fdwForm->fdwvalidator;
824 : : }
825 : :
60 jdavis@postgresql.or 826 [ + + ]:GNC 73 : if (connection_given)
827 : : {
828 : 17 : repl_val[Anum_pg_foreign_data_wrapper_fdwconnection - 1] = ObjectIdGetDatum(fdwconnection);
829 : 17 : repl_repl[Anum_pg_foreign_data_wrapper_fdwconnection - 1] = true;
830 : :
831 : : /*
832 : : * If the connection function is changed, behavior of dependent
833 : : * subscriptions can change. If NO CONNECTION, dependent
834 : : * subscriptions will fail.
835 : : */
46 836 [ + + ]: 17 : if (OidIsValid(fdwForm->fdwconnection))
837 : : {
838 [ - + ]: 4 : if (OidIsValid(fdwconnection))
46 jdavis@postgresql.or 839 [ # # ]:UNC 0 : ereport(WARNING,
840 : : (errmsg("changing the foreign-data wrapper connection function can cause "
841 : : "the options for dependent objects to become invalid")));
842 : : else
46 jdavis@postgresql.or 843 [ + - ]:GNC 4 : ereport(WARNING,
844 : : (errmsg("removing the foreign-data wrapper connection function will cause "
845 : : "dependent subscriptions to fail")));
846 : : }
847 : : }
848 : : else
849 : : {
850 : : /* connection function unchanged */
851 : 56 : fdwconnection = fdwForm->fdwconnection;
852 : : }
853 : :
854 : : /*
855 : : * If options specified, validate and update.
856 : : */
6346 peter_e@gmx.net 857 [ + + ]:CBC 73 : if (stmt->options)
858 : : {
859 : : /* Extract the current options */
860 : 40 : datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
861 : : tp,
862 : : Anum_pg_foreign_data_wrapper_fdwoptions,
863 : : &isnull);
6345 tgl@sss.pgh.pa.us 864 [ + + ]: 40 : if (isnull)
865 : 12 : datum = PointerGetDatum(NULL);
866 : :
867 : : /* Transform the options */
5977 heikki.linnakangas@i 868 : 40 : datum = transformGenericOptions(ForeignDataWrapperRelationId,
869 : : datum,
870 : : stmt->options,
871 : : fdwvalidator);
872 : :
223 peter@eisentraut.org 873 [ + - ]:GNC 20 : if (DatumGetPointer(datum) != NULL)
6345 tgl@sss.pgh.pa.us 874 :CBC 20 : repl_val[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = datum;
875 : : else
6346 peter_e@gmx.net 876 :UBC 0 : repl_null[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
877 : :
6346 peter_e@gmx.net 878 :CBC 20 : repl_repl[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
879 : : }
880 : :
881 : : /* Everything looks good - update the tuple */
882 : 53 : tp = heap_modify_tuple(tp, RelationGetDescr(rel),
883 : : repl_val, repl_null, repl_repl);
884 : :
3381 alvherre@alvh.no-ip. 885 : 53 : CatalogTupleUpdate(rel, &tp->t_self, tp);
886 : :
6346 peter_e@gmx.net 887 : 53 : heap_freetuple(tp);
888 : :
4081 alvherre@alvh.no-ip. 889 : 53 : ObjectAddressSet(myself, ForeignDataWrapperRelationId, fdwId);
890 : :
891 : : /* Update function dependencies if we changed them */
46 jdavis@postgresql.or 892 [ + + + + :GNC 53 : if (handler_given || validator_given || connection_given)
+ + ]
893 : : {
894 : : ObjectAddress referenced;
895 : :
896 : : /*
897 : : * Flush all existing dependency records of this FDW on functions; we
898 : : * assume there can be none other than the ones we are fixing.
899 : : */
5554 tgl@sss.pgh.pa.us 900 :CBC 33 : deleteDependencyRecordsForClass(ForeignDataWrapperRelationId,
901 : : fdwId,
902 : : ProcedureRelationId,
903 : : DEPENDENCY_NORMAL);
904 : :
905 : : /* And build new ones. */
906 : :
907 [ + + ]: 33 : if (OidIsValid(fdwhandler))
908 : : {
909 : 17 : referenced.classId = ProcedureRelationId;
910 : 17 : referenced.objectId = fdwhandler;
911 : 17 : referenced.objectSubId = 0;
912 : 17 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
913 : : }
914 : :
915 [ + + ]: 33 : if (OidIsValid(fdwvalidator))
916 : : {
917 : 13 : referenced.classId = ProcedureRelationId;
918 : 13 : referenced.objectId = fdwvalidator;
919 : 13 : referenced.objectSubId = 0;
920 : 13 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
921 : : }
922 : :
46 jdavis@postgresql.or 923 [ + + ]:GNC 33 : if (OidIsValid(fdwconnection))
924 : : {
925 : 17 : referenced.classId = ProcedureRelationId;
926 : 17 : referenced.objectId = fdwconnection;
927 : 17 : referenced.objectSubId = 0;
928 : 17 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
929 : : }
930 : : }
931 : :
4797 rhaas@postgresql.org 932 [ - + ]:CBC 53 : InvokeObjectPostAlterHook(ForeignDataWrapperRelationId, fdwId, 0);
933 : :
2661 andres@anarazel.de 934 : 53 : table_close(rel, RowExclusiveLock);
935 : :
4081 alvherre@alvh.no-ip. 936 : 53 : return myself;
937 : : }
938 : :
939 : :
940 : : /*
941 : : * Create a foreign server
942 : : */
943 : : ObjectAddress
6346 peter_e@gmx.net 944 : 190 : CreateForeignServer(CreateForeignServerStmt *stmt)
945 : : {
946 : : Relation rel;
947 : : Datum srvoptions;
948 : : Datum values[Natts_pg_foreign_server];
949 : : bool nulls[Natts_pg_foreign_server];
950 : : HeapTuple tuple;
951 : : Oid srvId;
952 : : Oid ownerId;
953 : : AclResult aclresult;
954 : : ObjectAddress myself;
955 : : ObjectAddress referenced;
956 : : ForeignDataWrapper *fdw;
957 : :
2661 andres@anarazel.de 958 : 190 : rel = table_open(ForeignServerRelationId, RowExclusiveLock);
959 : :
960 : : /* For now the owner cannot be specified on create. Use effective user ID. */
6346 peter_e@gmx.net 961 : 190 : ownerId = GetUserId();
962 : :
963 : : /*
964 : : * Check that there is no other foreign server by this name. If there is
965 : : * one, do nothing if IF NOT EXISTS was specified.
966 : : */
1366 tgl@sss.pgh.pa.us 967 : 190 : srvId = get_foreign_server_oid(stmt->servername, true);
968 [ + + ]: 190 : if (OidIsValid(srvId))
969 : : {
3333 andrew@dunslane.net 970 [ + + ]: 10 : if (stmt->if_not_exists)
971 : : {
972 : : /*
973 : : * If we are in an extension script, insist that the pre-existing
974 : : * object be a member of the extension, to avoid security risks.
975 : : */
1366 tgl@sss.pgh.pa.us 976 : 6 : ObjectAddressSet(myself, ForeignServerRelationId, srvId);
977 : 6 : checkMembershipInCurrentExtension(&myself);
978 : :
979 : : /* OK to skip */
3333 andrew@dunslane.net 980 [ + + ]: 5 : ereport(NOTICE,
981 : : (errcode(ERRCODE_DUPLICATE_OBJECT),
982 : : errmsg("server \"%s\" already exists, skipping",
983 : : stmt->servername)));
2661 andres@anarazel.de 984 : 5 : table_close(rel, RowExclusiveLock);
3333 andrew@dunslane.net 985 : 5 : return InvalidObjectAddress;
986 : : }
987 : : else
988 [ + - ]: 4 : ereport(ERROR,
989 : : (errcode(ERRCODE_DUPLICATE_OBJECT),
990 : : errmsg("server \"%s\" already exists",
991 : : stmt->servername)));
992 : : }
993 : :
994 : : /*
995 : : * Check that the FDW exists and that we have USAGE on it. Also get the
996 : : * actual FDW for option validation etc.
997 : : */
6346 peter_e@gmx.net 998 : 180 : fdw = GetForeignDataWrapperByName(stmt->fdwname, false);
999 : :
1269 peter@eisentraut.org 1000 : 176 : aclresult = object_aclcheck(ForeignDataWrapperRelationId, fdw->fdwid, ownerId, ACL_USAGE);
6346 peter_e@gmx.net 1001 [ + + ]: 176 : if (aclresult != ACLCHECK_OK)
3076 1002 : 17 : aclcheck_error(aclresult, OBJECT_FDW, fdw->fdwname);
1003 : :
1004 : : /*
1005 : : * Insert tuple into pg_foreign_server.
1006 : : */
6345 tgl@sss.pgh.pa.us 1007 : 159 : memset(values, 0, sizeof(values));
1008 : 159 : memset(nulls, false, sizeof(nulls));
1009 : :
2723 andres@anarazel.de 1010 : 159 : srvId = GetNewOidWithIndex(rel, ForeignServerOidIndexId,
1011 : : Anum_pg_foreign_server_oid);
1012 : 159 : values[Anum_pg_foreign_server_oid - 1] = ObjectIdGetDatum(srvId);
6346 peter_e@gmx.net 1013 : 159 : values[Anum_pg_foreign_server_srvname - 1] =
1014 : 159 : DirectFunctionCall1(namein, CStringGetDatum(stmt->servername));
1015 : 159 : values[Anum_pg_foreign_server_srvowner - 1] = ObjectIdGetDatum(ownerId);
1016 : 159 : values[Anum_pg_foreign_server_srvfdw - 1] = ObjectIdGetDatum(fdw->fdwid);
1017 : :
1018 : : /* Add server type if supplied */
1019 [ + + ]: 159 : if (stmt->servertype)
1020 : 12 : values[Anum_pg_foreign_server_srvtype - 1] =
1021 : 12 : CStringGetTextDatum(stmt->servertype);
1022 : : else
1023 : 147 : nulls[Anum_pg_foreign_server_srvtype - 1] = true;
1024 : :
1025 : : /* Add server version if supplied */
1026 [ + + ]: 159 : if (stmt->version)
1027 : 12 : values[Anum_pg_foreign_server_srvversion - 1] =
1028 : 12 : CStringGetTextDatum(stmt->version);
1029 : : else
1030 : 147 : nulls[Anum_pg_foreign_server_srvversion - 1] = true;
1031 : :
1032 : : /* Start with a blank acl */
1033 : 159 : nulls[Anum_pg_foreign_server_srvacl - 1] = true;
1034 : :
1035 : : /* Add server options */
5977 heikki.linnakangas@i 1036 : 159 : srvoptions = transformGenericOptions(ForeignServerRelationId,
1037 : : PointerGetDatum(NULL),
1038 : : stmt->options,
1039 : : fdw->fdwvalidator);
1040 : :
223 peter@eisentraut.org 1041 [ + + ]:GNC 153 : if (DatumGetPointer(srvoptions) != NULL)
6346 peter_e@gmx.net 1042 :CBC 40 : values[Anum_pg_foreign_server_srvoptions - 1] = srvoptions;
1043 : : else
1044 : 113 : nulls[Anum_pg_foreign_server_srvoptions - 1] = true;
1045 : :
1046 : 153 : tuple = heap_form_tuple(rel->rd_att, values, nulls);
1047 : :
2723 andres@anarazel.de 1048 : 153 : CatalogTupleInsert(rel, tuple);
1049 : :
6346 peter_e@gmx.net 1050 : 153 : heap_freetuple(tuple);
1051 : :
1052 : : /* record dependencies */
1053 : 153 : myself.classId = ForeignServerRelationId;
1054 : 153 : myself.objectId = srvId;
1055 : 153 : myself.objectSubId = 0;
1056 : :
1057 : 153 : referenced.classId = ForeignDataWrapperRelationId;
1058 : 153 : referenced.objectId = fdw->fdwid;
1059 : 153 : referenced.objectSubId = 0;
1060 : 153 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1061 : :
1062 : 153 : recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId);
1063 : :
1064 : : /* dependency on extension */
5400 tgl@sss.pgh.pa.us 1065 : 153 : recordDependencyOnCurrentExtension(&myself, false);
1066 : :
1067 : : /* Post creation hook for new foreign server */
4808 rhaas@postgresql.org 1068 [ - + ]: 153 : InvokeObjectPostCreateHook(ForeignServerRelationId, srvId, 0);
1069 : :
2661 andres@anarazel.de 1070 : 153 : table_close(rel, RowExclusiveLock);
1071 : :
4081 alvherre@alvh.no-ip. 1072 : 153 : return myself;
1073 : : }
1074 : :
1075 : :
1076 : : /*
1077 : : * Alter foreign server
1078 : : */
1079 : : ObjectAddress
6346 peter_e@gmx.net 1080 : 129 : AlterForeignServer(AlterForeignServerStmt *stmt)
1081 : : {
1082 : : Relation rel;
1083 : : HeapTuple tp;
1084 : : Datum repl_val[Natts_pg_foreign_server];
1085 : : bool repl_null[Natts_pg_foreign_server];
1086 : : bool repl_repl[Natts_pg_foreign_server];
1087 : : Oid srvId;
1088 : : Form_pg_foreign_server srvForm;
1089 : : ObjectAddress address;
1090 : :
2661 andres@anarazel.de 1091 : 129 : rel = table_open(ForeignServerRelationId, RowExclusiveLock);
1092 : :
5924 rhaas@postgresql.org 1093 : 129 : tp = SearchSysCacheCopy1(FOREIGNSERVERNAME,
1094 : : CStringGetDatum(stmt->servername));
1095 : :
6346 peter_e@gmx.net 1096 [ + + ]: 129 : if (!HeapTupleIsValid(tp))
1097 [ + - ]: 4 : ereport(ERROR,
1098 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
1099 : : errmsg("server \"%s\" does not exist", stmt->servername)));
1100 : :
1101 : 125 : srvForm = (Form_pg_foreign_server) GETSTRUCT(tp);
2723 andres@anarazel.de 1102 : 125 : srvId = srvForm->oid;
1103 : :
1104 : : /*
1105 : : * Only owner or a superuser can ALTER a SERVER.
1106 : : */
1269 peter@eisentraut.org 1107 [ + + ]: 125 : if (!object_ownercheck(ForeignServerRelationId, srvId, GetUserId()))
3076 peter_e@gmx.net 1108 : 16 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FOREIGN_SERVER,
6346 1109 : 16 : stmt->servername);
1110 : :
1111 : 109 : memset(repl_val, 0, sizeof(repl_val));
1112 : 109 : memset(repl_null, false, sizeof(repl_null));
1113 : 109 : memset(repl_repl, false, sizeof(repl_repl));
1114 : :
1115 [ + + ]: 109 : if (stmt->has_version)
1116 : : {
1117 : : /*
1118 : : * Change the server VERSION string.
1119 : : */
1120 [ + - ]: 16 : if (stmt->version)
1121 : 16 : repl_val[Anum_pg_foreign_server_srvversion - 1] =
1122 : 16 : CStringGetTextDatum(stmt->version);
1123 : : else
6346 peter_e@gmx.net 1124 :UBC 0 : repl_null[Anum_pg_foreign_server_srvversion - 1] = true;
1125 : :
6346 peter_e@gmx.net 1126 :CBC 16 : repl_repl[Anum_pg_foreign_server_srvversion - 1] = true;
1127 : : }
1128 : :
1129 [ + + ]: 109 : if (stmt->options)
1130 : : {
1131 : 97 : ForeignDataWrapper *fdw = GetForeignDataWrapper(srvForm->srvfdw);
1132 : : Datum datum;
1133 : : bool isnull;
1134 : :
1135 : : /* Extract the current srvoptions */
1136 : 97 : datum = SysCacheGetAttr(FOREIGNSERVEROID,
1137 : : tp,
1138 : : Anum_pg_foreign_server_srvoptions,
1139 : : &isnull);
6345 tgl@sss.pgh.pa.us 1140 [ + + ]: 97 : if (isnull)
1141 : 11 : datum = PointerGetDatum(NULL);
1142 : :
1143 : : /* Prepare the options array */
5977 heikki.linnakangas@i 1144 : 97 : datum = transformGenericOptions(ForeignServerRelationId,
1145 : : datum,
1146 : : stmt->options,
1147 : : fdw->fdwvalidator);
1148 : :
223 peter@eisentraut.org 1149 [ + + ]:GNC 84 : if (DatumGetPointer(datum) != NULL)
6346 peter_e@gmx.net 1150 :CBC 80 : repl_val[Anum_pg_foreign_server_srvoptions - 1] = datum;
1151 : : else
1152 : 4 : repl_null[Anum_pg_foreign_server_srvoptions - 1] = true;
1153 : :
1154 : 84 : repl_repl[Anum_pg_foreign_server_srvoptions - 1] = true;
1155 : : }
1156 : :
1157 : : /* Everything looks good - update the tuple */
1158 : 96 : tp = heap_modify_tuple(tp, RelationGetDescr(rel),
1159 : : repl_val, repl_null, repl_repl);
1160 : :
3381 alvherre@alvh.no-ip. 1161 : 96 : CatalogTupleUpdate(rel, &tp->t_self, tp);
1162 : :
4797 rhaas@postgresql.org 1163 [ - + ]: 96 : InvokeObjectPostAlterHook(ForeignServerRelationId, srvId, 0);
1164 : :
4081 alvherre@alvh.no-ip. 1165 : 96 : ObjectAddressSet(address, ForeignServerRelationId, srvId);
1166 : :
6346 peter_e@gmx.net 1167 : 96 : heap_freetuple(tp);
1168 : :
2661 andres@anarazel.de 1169 : 96 : table_close(rel, RowExclusiveLock);
1170 : :
4081 alvherre@alvh.no-ip. 1171 : 96 : return address;
1172 : : }
1173 : :
1174 : :
1175 : : /*
1176 : : * Common routine to check permission for user-mapping-related DDL
1177 : : * commands. We allow server owners to operate on any mapping, and
1178 : : * users to operate on their own mapping.
1179 : : */
1180 : : static void
6314 peter_e@gmx.net 1181 : 265 : user_mapping_ddl_aclcheck(Oid umuserid, Oid serverid, const char *servername)
1182 : : {
1183 : 265 : Oid curuserid = GetUserId();
1184 : :
1269 peter@eisentraut.org 1185 [ + + ]: 265 : if (!object_ownercheck(ForeignServerRelationId, serverid, curuserid))
1186 : : {
6314 peter_e@gmx.net 1187 [ + + ]: 57 : if (umuserid == curuserid)
1188 : : {
1189 : : AclResult aclresult;
1190 : :
1269 peter@eisentraut.org 1191 : 21 : aclresult = object_aclcheck(ForeignServerRelationId, serverid, curuserid, ACL_USAGE);
6314 peter_e@gmx.net 1192 [ + + ]: 21 : if (aclresult != ACLCHECK_OK)
3076 1193 : 5 : aclcheck_error(aclresult, OBJECT_FOREIGN_SERVER, servername);
1194 : : }
1195 : : else
1196 : 36 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FOREIGN_SERVER,
1197 : : servername);
1198 : : }
6314 1199 : 224 : }
1200 : :
1201 : :
1202 : : /*
1203 : : * Create user mapping
1204 : : */
1205 : : ObjectAddress
6346 1206 : 164 : CreateUserMapping(CreateUserMappingStmt *stmt)
1207 : : {
1208 : : Relation rel;
1209 : : Datum useoptions;
1210 : : Datum values[Natts_pg_user_mapping];
1211 : : bool nulls[Natts_pg_user_mapping];
1212 : : HeapTuple tuple;
1213 : : Oid useId;
1214 : : Oid umId;
1215 : : ObjectAddress myself;
1216 : : ObjectAddress referenced;
1217 : : ForeignServer *srv;
1218 : : ForeignDataWrapper *fdw;
4075 alvherre@alvh.no-ip. 1219 : 164 : RoleSpec *role = (RoleSpec *) stmt->user;
1220 : :
2661 andres@anarazel.de 1221 : 164 : rel = table_open(UserMappingRelationId, RowExclusiveLock);
1222 : :
4075 alvherre@alvh.no-ip. 1223 [ + + ]: 164 : if (role->roletype == ROLESPEC_PUBLIC)
1224 : 46 : useId = ACL_ID_PUBLIC;
1225 : : else
1226 : 118 : useId = get_rolespec_oid(stmt->user, false);
1227 : :
1228 : : /* Check that the server exists. */
6346 peter_e@gmx.net 1229 : 159 : srv = GetForeignServerByName(stmt->servername, false);
1230 : :
6314 1231 : 155 : user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
1232 : :
1233 : : /*
1234 : : * Check that the user mapping is unique within server.
1235 : : */
2723 andres@anarazel.de 1236 : 138 : umId = GetSysCacheOid2(USERMAPPINGUSERSERVER, Anum_pg_user_mapping_oid,
1237 : : ObjectIdGetDatum(useId),
1238 : : ObjectIdGetDatum(srv->serverid));
1239 : :
6346 peter_e@gmx.net 1240 [ + + ]: 138 : if (OidIsValid(umId))
1241 : : {
3333 andrew@dunslane.net 1242 [ + + ]: 12 : if (stmt->if_not_exists)
1243 : : {
1244 : : /*
1245 : : * Since user mappings aren't members of extensions (see comments
1246 : : * below), no need for checkMembershipInCurrentExtension here.
1247 : : */
1248 [ + - + - ]: 4 : ereport(NOTICE,
1249 : : (errcode(ERRCODE_DUPLICATE_OBJECT),
1250 : : errmsg("user mapping for \"%s\" already exists for server \"%s\", skipping",
1251 : : MappingUserName(useId),
1252 : : stmt->servername)));
1253 : :
2661 andres@anarazel.de 1254 : 4 : table_close(rel, RowExclusiveLock);
3333 andrew@dunslane.net 1255 : 4 : return InvalidObjectAddress;
1256 : : }
1257 : : else
1258 [ + - + - ]: 8 : ereport(ERROR,
1259 : : (errcode(ERRCODE_DUPLICATE_OBJECT),
1260 : : errmsg("user mapping for \"%s\" already exists for server \"%s\"",
1261 : : MappingUserName(useId),
1262 : : stmt->servername)));
1263 : : }
1264 : :
6346 peter_e@gmx.net 1265 : 126 : fdw = GetForeignDataWrapper(srv->fdwid);
1266 : :
1267 : : /*
1268 : : * Insert tuple into pg_user_mapping.
1269 : : */
6345 tgl@sss.pgh.pa.us 1270 : 126 : memset(values, 0, sizeof(values));
1271 : 126 : memset(nulls, false, sizeof(nulls));
1272 : :
2723 andres@anarazel.de 1273 : 126 : umId = GetNewOidWithIndex(rel, UserMappingOidIndexId,
1274 : : Anum_pg_user_mapping_oid);
1275 : 126 : values[Anum_pg_user_mapping_oid - 1] = ObjectIdGetDatum(umId);
6346 peter_e@gmx.net 1276 : 126 : values[Anum_pg_user_mapping_umuser - 1] = ObjectIdGetDatum(useId);
1277 : 126 : values[Anum_pg_user_mapping_umserver - 1] = ObjectIdGetDatum(srv->serverid);
1278 : :
1279 : : /* Add user options */
5977 heikki.linnakangas@i 1280 : 126 : useoptions = transformGenericOptions(UserMappingRelationId,
1281 : : PointerGetDatum(NULL),
1282 : : stmt->options,
1283 : : fdw->fdwvalidator);
1284 : :
223 peter@eisentraut.org 1285 [ + + ]:GNC 117 : if (DatumGetPointer(useoptions) != NULL)
6346 peter_e@gmx.net 1286 :CBC 56 : values[Anum_pg_user_mapping_umoptions - 1] = useoptions;
1287 : : else
1288 : 61 : nulls[Anum_pg_user_mapping_umoptions - 1] = true;
1289 : :
1290 : 117 : tuple = heap_form_tuple(rel->rd_att, values, nulls);
1291 : :
2723 andres@anarazel.de 1292 : 117 : CatalogTupleInsert(rel, tuple);
1293 : :
6346 peter_e@gmx.net 1294 : 117 : heap_freetuple(tuple);
1295 : :
1296 : : /* Add dependency on the server */
1297 : 117 : myself.classId = UserMappingRelationId;
1298 : 117 : myself.objectId = umId;
1299 : 117 : myself.objectSubId = 0;
1300 : :
1301 : 117 : referenced.classId = ForeignServerRelationId;
1302 : 117 : referenced.objectId = srv->serverid;
1303 : 117 : referenced.objectSubId = 0;
1304 : 117 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1305 : :
1306 [ + + ]: 117 : if (OidIsValid(useId))
1307 : : {
1308 : : /* Record the mapped user dependency */
1309 : 86 : recordDependencyOnOwner(UserMappingRelationId, umId, useId);
1310 : : }
1311 : :
1312 : : /*
1313 : : * Perhaps someday there should be a recordDependencyOnCurrentExtension
1314 : : * call here; but since roles aren't members of extensions, it seems like
1315 : : * user mappings shouldn't be either. Note that the grammar and pg_dump
1316 : : * would need to be extended too if we change this.
1317 : : */
1318 : :
1319 : : /* Post creation hook for new user mapping */
4808 rhaas@postgresql.org 1320 [ - + ]: 117 : InvokeObjectPostCreateHook(UserMappingRelationId, umId, 0);
1321 : :
2661 andres@anarazel.de 1322 : 117 : table_close(rel, RowExclusiveLock);
1323 : :
4081 alvherre@alvh.no-ip. 1324 : 117 : return myself;
1325 : : }
1326 : :
1327 : :
1328 : : /*
1329 : : * Alter user mapping
1330 : : */
1331 : : ObjectAddress
6346 peter_e@gmx.net 1332 : 70 : AlterUserMapping(AlterUserMappingStmt *stmt)
1333 : : {
1334 : : Relation rel;
1335 : : HeapTuple tp;
1336 : : Datum repl_val[Natts_pg_user_mapping];
1337 : : bool repl_null[Natts_pg_user_mapping];
1338 : : bool repl_repl[Natts_pg_user_mapping];
1339 : : Oid useId;
1340 : : Oid umId;
1341 : : ForeignServer *srv;
1342 : : ObjectAddress address;
4075 alvherre@alvh.no-ip. 1343 : 70 : RoleSpec *role = (RoleSpec *) stmt->user;
1344 : :
2661 andres@anarazel.de 1345 : 70 : rel = table_open(UserMappingRelationId, RowExclusiveLock);
1346 : :
4075 alvherre@alvh.no-ip. 1347 [ + + ]: 70 : if (role->roletype == ROLESPEC_PUBLIC)
1348 : 21 : useId = ACL_ID_PUBLIC;
1349 : : else
1350 : 49 : useId = get_rolespec_oid(stmt->user, false);
1351 : :
6346 peter_e@gmx.net 1352 : 65 : srv = GetForeignServerByName(stmt->servername, false);
1353 : :
2723 andres@anarazel.de 1354 : 61 : umId = GetSysCacheOid2(USERMAPPINGUSERSERVER, Anum_pg_user_mapping_oid,
1355 : : ObjectIdGetDatum(useId),
1356 : : ObjectIdGetDatum(srv->serverid));
6346 peter_e@gmx.net 1357 [ + + ]: 61 : if (!OidIsValid(umId))
1358 [ + - - + ]: 4 : ereport(ERROR,
1359 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
1360 : : errmsg("user mapping for \"%s\" does not exist for server \"%s\"",
1361 : : MappingUserName(useId), stmt->servername)));
1362 : :
6314 1363 : 57 : user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
1364 : :
5924 rhaas@postgresql.org 1365 : 45 : tp = SearchSysCacheCopy1(USERMAPPINGOID, ObjectIdGetDatum(umId));
1366 : :
6346 peter_e@gmx.net 1367 [ - + ]: 45 : if (!HeapTupleIsValid(tp))
6346 peter_e@gmx.net 1368 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for user mapping %u", umId);
1369 : :
6346 peter_e@gmx.net 1370 :CBC 45 : memset(repl_val, 0, sizeof(repl_val));
1371 : 45 : memset(repl_null, false, sizeof(repl_null));
1372 : 45 : memset(repl_repl, false, sizeof(repl_repl));
1373 : :
1374 [ + - ]: 45 : if (stmt->options)
1375 : : {
1376 : : ForeignDataWrapper *fdw;
1377 : : Datum datum;
1378 : : bool isnull;
1379 : :
1380 : : /*
1381 : : * Process the options.
1382 : : */
1383 : :
1384 : 45 : fdw = GetForeignDataWrapper(srv->fdwid);
1385 : :
1386 : 45 : datum = SysCacheGetAttr(USERMAPPINGUSERSERVER,
1387 : : tp,
1388 : : Anum_pg_user_mapping_umoptions,
1389 : : &isnull);
6345 tgl@sss.pgh.pa.us 1390 [ + + ]: 45 : if (isnull)
1391 : 12 : datum = PointerGetDatum(NULL);
1392 : :
1393 : : /* Prepare the options array */
5977 heikki.linnakangas@i 1394 : 45 : datum = transformGenericOptions(UserMappingRelationId,
1395 : : datum,
1396 : : stmt->options,
1397 : : fdw->fdwvalidator);
1398 : :
223 peter@eisentraut.org 1399 [ + + ]:GNC 33 : if (DatumGetPointer(datum) != NULL)
6346 peter_e@gmx.net 1400 :CBC 27 : repl_val[Anum_pg_user_mapping_umoptions - 1] = datum;
1401 : : else
1402 : 6 : repl_null[Anum_pg_user_mapping_umoptions - 1] = true;
1403 : :
1404 : 33 : repl_repl[Anum_pg_user_mapping_umoptions - 1] = true;
1405 : : }
1406 : :
1407 : : /* Everything looks good - update the tuple */
1408 : 33 : tp = heap_modify_tuple(tp, RelationGetDescr(rel),
1409 : : repl_val, repl_null, repl_repl);
1410 : :
3381 alvherre@alvh.no-ip. 1411 : 33 : CatalogTupleUpdate(rel, &tp->t_self, tp);
1412 : :
2173 michael@paquier.xyz 1413 [ - + ]: 33 : InvokeObjectPostAlterHook(UserMappingRelationId,
1414 : : umId, 0);
1415 : :
4081 alvherre@alvh.no-ip. 1416 : 33 : ObjectAddressSet(address, UserMappingRelationId, umId);
1417 : :
6346 peter_e@gmx.net 1418 : 33 : heap_freetuple(tp);
1419 : :
2661 andres@anarazel.de 1420 : 33 : table_close(rel, RowExclusiveLock);
1421 : :
4081 alvherre@alvh.no-ip. 1422 : 33 : return address;
1423 : : }
1424 : :
1425 : :
1426 : : /*
1427 : : * Drop user mapping
1428 : : */
1429 : : Oid
6346 peter_e@gmx.net 1430 : 79 : RemoveUserMapping(DropUserMappingStmt *stmt)
1431 : : {
1432 : : ObjectAddress object;
1433 : : Oid useId;
1434 : : Oid umId;
1435 : : ForeignServer *srv;
4075 alvherre@alvh.no-ip. 1436 : 79 : RoleSpec *role = (RoleSpec *) stmt->user;
1437 : :
1438 [ + + ]: 79 : if (role->roletype == ROLESPEC_PUBLIC)
1439 : 20 : useId = ACL_ID_PUBLIC;
1440 : : else
1441 : : {
1442 : 59 : useId = get_rolespec_oid(stmt->user, stmt->missing_ok);
1443 [ + + ]: 54 : if (!OidIsValid(useId))
1444 : : {
1445 : : /*
1446 : : * IF EXISTS specified, role not found and not public. Notice this
1447 : : * and leave.
1448 : : */
1449 [ + - ]: 5 : elog(NOTICE, "role \"%s\" does not exist, skipping",
1450 : : role->rolename);
1451 : 5 : return InvalidOid;
1452 : : }
1453 : : }
1454 : :
1455 : 69 : srv = GetForeignServerByName(stmt->servername, true);
1456 : :
6346 peter_e@gmx.net 1457 [ + + ]: 69 : if (!srv)
1458 : : {
1459 [ + + ]: 8 : if (!stmt->missing_ok)
1460 [ + - ]: 4 : ereport(ERROR,
1461 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
1462 : : errmsg("server \"%s\" does not exist",
1463 : : stmt->servername)));
1464 : : /* IF EXISTS, just note it */
2554 alvherre@alvh.no-ip. 1465 [ + - ]: 4 : ereport(NOTICE,
1466 : : (errmsg("server \"%s\" does not exist, skipping",
1467 : : stmt->servername)));
4875 rhaas@postgresql.org 1468 : 4 : return InvalidOid;
1469 : : }
1470 : :
2723 andres@anarazel.de 1471 : 61 : umId = GetSysCacheOid2(USERMAPPINGUSERSERVER, Anum_pg_user_mapping_oid,
1472 : : ObjectIdGetDatum(useId),
1473 : : ObjectIdGetDatum(srv->serverid));
1474 : :
6346 peter_e@gmx.net 1475 [ + + ]: 61 : if (!OidIsValid(umId))
1476 : : {
1477 [ + + ]: 8 : if (!stmt->missing_ok)
1478 [ + - - + ]: 4 : ereport(ERROR,
1479 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
1480 : : errmsg("user mapping for \"%s\" does not exist for server \"%s\"",
1481 : : MappingUserName(useId), stmt->servername)));
1482 : :
1483 : : /* IF EXISTS specified, just note it */
1484 [ + - - + ]: 4 : ereport(NOTICE,
1485 : : (errmsg("user mapping for \"%s\" does not exist for server \"%s\", skipping",
1486 : : MappingUserName(useId), stmt->servername)));
4875 rhaas@postgresql.org 1487 : 4 : return InvalidOid;
1488 : : }
1489 : :
6314 peter_e@gmx.net 1490 : 53 : user_mapping_ddl_aclcheck(useId, srv->serverid, srv->servername);
1491 : :
1492 : : /*
1493 : : * Do the deletion
1494 : : */
6346 1495 : 41 : object.classId = UserMappingRelationId;
1496 : 41 : object.objectId = umId;
1497 : 41 : object.objectSubId = 0;
1498 : :
5213 rhaas@postgresql.org 1499 : 41 : performDeletion(&object, DROP_CASCADE, 0);
1500 : :
4875 1501 : 41 : return umId;
1502 : : }
1503 : :
1504 : :
1505 : : /*
1506 : : * Create a foreign table
1507 : : * call after DefineRelation().
1508 : : */
1509 : : void
5603 1510 : 269 : CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
1511 : : {
1512 : : Relation ftrel;
1513 : : Datum ftoptions;
1514 : : Datum values[Natts_pg_foreign_table];
1515 : : bool nulls[Natts_pg_foreign_table];
1516 : : HeapTuple tuple;
1517 : : AclResult aclresult;
1518 : : ObjectAddress myself;
1519 : : ObjectAddress referenced;
1520 : : Oid ownerId;
1521 : : ForeignDataWrapper *fdw;
1522 : : ForeignServer *server;
1523 : :
1524 : : /*
1525 : : * Advance command counter to ensure the pg_attribute tuple is visible;
1526 : : * the tuple might be updated to add constraints in previous step.
1527 : : */
1528 : 269 : CommandCounterIncrement();
1529 : :
2661 andres@anarazel.de 1530 : 269 : ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
1531 : :
1532 : : /*
1533 : : * For now the owner cannot be specified on create. Use effective user ID.
1534 : : */
5603 rhaas@postgresql.org 1535 : 269 : ownerId = GetUserId();
1536 : :
1537 : : /*
1538 : : * Check that the foreign server exists and that we have USAGE on it. Also
1539 : : * get the actual FDW for option validation etc.
1540 : : */
1541 : 269 : server = GetForeignServerByName(stmt->servername, false);
1269 peter@eisentraut.org 1542 : 265 : aclresult = object_aclcheck(ForeignServerRelationId, server->serverid, ownerId, ACL_USAGE);
5603 rhaas@postgresql.org 1543 [ - + ]: 265 : if (aclresult != ACLCHECK_OK)
3076 peter_e@gmx.net 1544 :UBC 0 : aclcheck_error(aclresult, OBJECT_FOREIGN_SERVER, server->servername);
1545 : :
5603 rhaas@postgresql.org 1546 :CBC 265 : fdw = GetForeignDataWrapper(server->fdwid);
1547 : :
1548 : : /*
1549 : : * Insert tuple into pg_foreign_table.
1550 : : */
1551 : 265 : memset(values, 0, sizeof(values));
1552 : 265 : memset(nulls, false, sizeof(nulls));
1553 : :
1554 : 265 : values[Anum_pg_foreign_table_ftrelid - 1] = ObjectIdGetDatum(relid);
1555 : 265 : values[Anum_pg_foreign_table_ftserver - 1] = ObjectIdGetDatum(server->serverid);
1556 : : /* Add table generic options */
1557 : 265 : ftoptions = transformGenericOptions(ForeignTableRelationId,
1558 : : PointerGetDatum(NULL),
1559 : : stmt->options,
1560 : : fdw->fdwvalidator);
1561 : :
223 peter@eisentraut.org 1562 [ + + ]:GNC 230 : if (DatumGetPointer(ftoptions) != NULL)
5603 rhaas@postgresql.org 1563 :CBC 158 : values[Anum_pg_foreign_table_ftoptions - 1] = ftoptions;
1564 : : else
1565 : 72 : nulls[Anum_pg_foreign_table_ftoptions - 1] = true;
1566 : :
1567 : 230 : tuple = heap_form_tuple(ftrel->rd_att, values, nulls);
1568 : :
3381 alvherre@alvh.no-ip. 1569 : 230 : CatalogTupleInsert(ftrel, tuple);
1570 : :
5603 rhaas@postgresql.org 1571 : 230 : heap_freetuple(tuple);
1572 : :
1573 : : /* Add pg_class dependency on the server */
1574 : 230 : myself.classId = RelationRelationId;
1575 : 230 : myself.objectId = relid;
1576 : 230 : myself.objectSubId = 0;
1577 : :
1578 : 230 : referenced.classId = ForeignServerRelationId;
1579 : 230 : referenced.objectId = server->serverid;
1580 : 230 : referenced.objectSubId = 0;
1581 : 230 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1582 : :
2661 andres@anarazel.de 1583 : 230 : table_close(ftrel, RowExclusiveLock);
5603 rhaas@postgresql.org 1584 : 230 : }
1585 : :
1586 : : /*
1587 : : * Import a foreign schema
1588 : : */
1589 : : void
4317 tgl@sss.pgh.pa.us 1590 : 28 : ImportForeignSchema(ImportForeignSchemaStmt *stmt)
1591 : : {
1592 : : ForeignServer *server;
1593 : : ForeignDataWrapper *fdw;
1594 : : FdwRoutine *fdw_routine;
1595 : : AclResult aclresult;
1596 : : List *cmd_list;
1597 : : ListCell *lc;
1598 : :
1599 : : /* Check that the foreign server exists and that we have USAGE on it */
1600 : 28 : server = GetForeignServerByName(stmt->server_name, false);
1269 peter@eisentraut.org 1601 : 27 : aclresult = object_aclcheck(ForeignServerRelationId, server->serverid, GetUserId(), ACL_USAGE);
4317 tgl@sss.pgh.pa.us 1602 [ - + ]: 27 : if (aclresult != ACLCHECK_OK)
3076 peter_e@gmx.net 1603 :UBC 0 : aclcheck_error(aclresult, OBJECT_FOREIGN_SERVER, server->servername);
1604 : :
1605 : : /* Check that the schema exists and we have CREATE permissions on it */
4317 tgl@sss.pgh.pa.us 1606 :CBC 27 : (void) LookupCreationNamespace(stmt->local_schema);
1607 : :
1608 : : /* Get the FDW and check it supports IMPORT */
1609 : 26 : fdw = GetForeignDataWrapper(server->fdwid);
1610 [ + + ]: 26 : if (!OidIsValid(fdw->fdwhandler))
1611 [ + - ]: 16 : ereport(ERROR,
1612 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1613 : : errmsg("foreign-data wrapper \"%s\" has no handler",
1614 : : fdw->fdwname)));
1615 : 10 : fdw_routine = GetFdwRoutine(fdw->fdwhandler);
1616 [ - + ]: 10 : if (fdw_routine->ImportForeignSchema == NULL)
4317 tgl@sss.pgh.pa.us 1617 [ # # ]:UBC 0 : ereport(ERROR,
1618 : : (errcode(ERRCODE_FDW_NO_SCHEMAS),
1619 : : errmsg("foreign-data wrapper \"%s\" does not support IMPORT FOREIGN SCHEMA",
1620 : : fdw->fdwname)));
1621 : :
1622 : : /* Call FDW to get a list of commands */
4317 tgl@sss.pgh.pa.us 1623 :CBC 10 : cmd_list = fdw_routine->ImportForeignSchema(stmt, server->serverid);
1624 : :
1625 : : /* Parse and execute each command */
1626 [ + - + + : 39 : foreach(lc, cmd_list)
+ + ]
1627 : : {
1628 : 32 : char *cmd = (char *) lfirst(lc);
1629 : : import_error_callback_arg callback_arg;
1630 : : ErrorContextCallback sqlerrcontext;
1631 : : List *raw_parsetree_list;
1632 : : ListCell *lc2;
1633 : :
1634 : : /*
1635 : : * Setup error traceback support for ereport(). This is so that any
1636 : : * error in the generated SQL will be displayed nicely.
1637 : : */
1638 : 32 : callback_arg.tablename = NULL; /* not known yet */
1639 : 32 : callback_arg.cmd = cmd;
1640 : 32 : sqlerrcontext.callback = import_error_callback;
523 peter@eisentraut.org 1641 : 32 : sqlerrcontext.arg = &callback_arg;
4317 tgl@sss.pgh.pa.us 1642 : 32 : sqlerrcontext.previous = error_context_stack;
1643 : 32 : error_context_stack = &sqlerrcontext;
1644 : :
1645 : : /*
1646 : : * Parse the SQL string into a list of raw parse trees.
1647 : : */
1648 : 32 : raw_parsetree_list = pg_parse_query(cmd);
1649 : :
1650 : : /*
1651 : : * Process each parse tree (we allow the FDW to put more than one
1652 : : * command per string, though this isn't really advised).
1653 : : */
1654 [ + - + + : 62 : foreach(lc2, raw_parsetree_list)
+ + ]
1655 : : {
3312 1656 : 32 : RawStmt *rs = lfirst_node(RawStmt, lc2);
3398 1657 : 32 : CreateForeignTableStmt *cstmt = (CreateForeignTableStmt *) rs->stmt;
1658 : : PlannedStmt *pstmt;
1659 : :
1660 : : /*
1661 : : * Because we only allow CreateForeignTableStmt, we can skip parse
1662 : : * analysis, rewrite, and planning steps here.
1663 : : */
4317 1664 [ - + ]: 32 : if (!IsA(cstmt, CreateForeignTableStmt))
4317 tgl@sss.pgh.pa.us 1665 [ # # ]:UBC 0 : elog(ERROR,
1666 : : "foreign-data wrapper \"%s\" returned incorrect statement type %d",
1667 : : fdw->fdwname, (int) nodeTag(cstmt));
1668 : :
1669 : : /* Ignore commands for tables excluded by filter options */
4317 tgl@sss.pgh.pa.us 1670 [ - + ]:CBC 32 : if (!IsImportableForeignTable(cstmt->base.relation->relname, stmt))
4317 tgl@sss.pgh.pa.us 1671 :UBC 0 : continue;
1672 : :
1673 : : /* Enable reporting of current table's name on error */
4317 tgl@sss.pgh.pa.us 1674 :CBC 32 : callback_arg.tablename = cstmt->base.relation->relname;
1675 : :
1676 : : /* Ensure creation schema is the one given in IMPORT statement */
1677 : 32 : cstmt->base.relation->schemaname = pstrdup(stmt->local_schema);
1678 : :
1679 : : /* No planning needed, just make a wrapper PlannedStmt */
3398 1680 : 32 : pstmt = makeNode(PlannedStmt);
1681 : 32 : pstmt->commandType = CMD_UTILITY;
1682 : 32 : pstmt->canSetTag = false;
1683 : 32 : pstmt->utilityStmt = (Node *) cstmt;
1684 : 32 : pstmt->stmt_location = rs->stmt_location;
1685 : 32 : pstmt->stmt_len = rs->stmt_len;
278 michael@paquier.xyz 1686 :GNC 32 : pstmt->planOrigin = PLAN_STMT_INTERNAL;
1687 : :
1688 : : /* Execute statement */
1782 tgl@sss.pgh.pa.us 1689 :CBC 32 : ProcessUtility(pstmt, cmd, false,
1690 : : PROCESS_UTILITY_SUBCOMMAND, NULL, NULL,
1691 : : None_Receiver, NULL);
1692 : :
1693 : : /* Be sure to advance the command counter between subcommands */
4317 1694 : 30 : CommandCounterIncrement();
1695 : :
1696 : 30 : callback_arg.tablename = NULL;
1697 : : }
1698 : :
1699 : 30 : error_context_stack = sqlerrcontext.previous;
1700 : : }
1701 : 7 : }
1702 : :
1703 : : /*
1704 : : * error context callback to let us supply the failing SQL statement's text
1705 : : */
1706 : : static void
1707 : 2 : import_error_callback(void *arg)
1708 : : {
1709 : 2 : import_error_callback_arg *callback_arg = (import_error_callback_arg *) arg;
1710 : : int syntaxerrposition;
1711 : :
1712 : : /* If it's a syntax error, convert to internal syntax error report */
1713 : 2 : syntaxerrposition = geterrposition();
1714 [ + + ]: 2 : if (syntaxerrposition > 0)
1715 : : {
1716 : 1 : errposition(0);
1717 : 1 : internalerrposition(syntaxerrposition);
1718 : 1 : internalerrquery(callback_arg->cmd);
1719 : : }
1720 : :
1721 [ + - ]: 2 : if (callback_arg->tablename)
1722 : 2 : errcontext("importing foreign table \"%s\"",
1723 : : callback_arg->tablename);
1724 : 2 : }
|