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