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