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