]> granicus.if.org Git - postgresql/blob - src/backend/commands/foreigncmds.c
Provide CatalogTupleDelete() as a wrapper around simple_heap_delete().
[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-2017, PostgreSQL Global Development Group
7  *
8  *
9  * IDENTIFICATION
10  *        src/backend/commands/foreigncmds.c
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include "postgres.h"
15
16 #include "access/heapam.h"
17 #include "access/htup_details.h"
18 #include "access/reloptions.h"
19 #include "access/xact.h"
20 #include "catalog/dependency.h"
21 #include "catalog/indexing.h"
22 #include "catalog/objectaccess.h"
23 #include "catalog/pg_foreign_data_wrapper.h"
24 #include "catalog/pg_foreign_server.h"
25 #include "catalog/pg_foreign_table.h"
26 #include "catalog/pg_proc.h"
27 #include "catalog/pg_type.h"
28 #include "catalog/pg_user_mapping.h"
29 #include "commands/defrem.h"
30 #include "foreign/fdwapi.h"
31 #include "foreign/foreign.h"
32 #include "miscadmin.h"
33 #include "parser/parse_func.h"
34 #include "tcop/utility.h"
35 #include "utils/acl.h"
36 #include "utils/builtins.h"
37 #include "utils/lsyscache.h"
38 #include "utils/rel.h"
39 #include "utils/syscache.h"
40
41
42 typedef struct
43 {
44         char       *tablename;
45         char       *cmd;
46 } import_error_callback_arg;
47
48 /* Internal functions */
49 static void import_error_callback(void *arg);
50
51
52 /*
53  * Convert a DefElem list to the text array format that is used in
54  * pg_foreign_data_wrapper, pg_foreign_server, pg_user_mapping, and
55  * pg_foreign_table.
56  *
57  * Returns the array in the form of a Datum, or PointerGetDatum(NULL)
58  * if the list is empty.
59  *
60  * Note: The array is usually stored to database without further
61  * processing, hence any validation should be done before this
62  * conversion.
63  */
64 static Datum
65 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
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
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
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
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
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
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
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
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
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
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
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
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
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
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.
882          */
883         if (GetForeignServerByName(stmt->servername, true) != NULL)
884                 ereport(ERROR,
885                                 (errcode(ERRCODE_DUPLICATE_OBJECT),
886                                  errmsg("server \"%s\" already exists",
887                                                 stmt->servername)));
888
889         /*
890          * Check that the FDW exists and that we have USAGE on it. Also get the
891          * actual FDW for option validation etc.
892          */
893         fdw = GetForeignDataWrapperByName(stmt->fdwname, false);
894
895         aclresult = pg_foreign_data_wrapper_aclcheck(fdw->fdwid, ownerId, ACL_USAGE);
896         if (aclresult != ACLCHECK_OK)
897                 aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname);
898
899         /*
900          * Insert tuple into pg_foreign_server.
901          */
902         memset(values, 0, sizeof(values));
903         memset(nulls, false, sizeof(nulls));
904
905         values[Anum_pg_foreign_server_srvname - 1] =
906                 DirectFunctionCall1(namein, CStringGetDatum(stmt->servername));
907         values[Anum_pg_foreign_server_srvowner - 1] = ObjectIdGetDatum(ownerId);
908         values[Anum_pg_foreign_server_srvfdw - 1] = ObjectIdGetDatum(fdw->fdwid);
909
910         /* Add server type if supplied */
911         if (stmt->servertype)
912                 values[Anum_pg_foreign_server_srvtype - 1] =
913                         CStringGetTextDatum(stmt->servertype);
914         else
915                 nulls[Anum_pg_foreign_server_srvtype - 1] = true;
916
917         /* Add server version if supplied */
918         if (stmt->version)
919                 values[Anum_pg_foreign_server_srvversion - 1] =
920                         CStringGetTextDatum(stmt->version);
921         else
922                 nulls[Anum_pg_foreign_server_srvversion - 1] = true;
923
924         /* Start with a blank acl */
925         nulls[Anum_pg_foreign_server_srvacl - 1] = true;
926
927         /* Add server options */
928         srvoptions = transformGenericOptions(ForeignServerRelationId,
929                                                                                  PointerGetDatum(NULL),
930                                                                                  stmt->options,
931                                                                                  fdw->fdwvalidator);
932
933         if (PointerIsValid(DatumGetPointer(srvoptions)))
934                 values[Anum_pg_foreign_server_srvoptions - 1] = srvoptions;
935         else
936                 nulls[Anum_pg_foreign_server_srvoptions - 1] = true;
937
938         tuple = heap_form_tuple(rel->rd_att, values, nulls);
939
940         srvId = CatalogTupleInsert(rel, tuple);
941
942         heap_freetuple(tuple);
943
944         /* record dependencies */
945         myself.classId = ForeignServerRelationId;
946         myself.objectId = srvId;
947         myself.objectSubId = 0;
948
949         referenced.classId = ForeignDataWrapperRelationId;
950         referenced.objectId = fdw->fdwid;
951         referenced.objectSubId = 0;
952         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
953
954         recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId);
955
956         /* dependency on extension */
957         recordDependencyOnCurrentExtension(&myself, false);
958
959         /* Post creation hook for new foreign server */
960         InvokeObjectPostCreateHook(ForeignServerRelationId, srvId, 0);
961
962         heap_close(rel, RowExclusiveLock);
963
964         return myself;
965 }
966
967
968 /*
969  * Alter foreign server
970  */
971 ObjectAddress
972 AlterForeignServer(AlterForeignServerStmt *stmt)
973 {
974         Relation        rel;
975         HeapTuple       tp;
976         Datum           repl_val[Natts_pg_foreign_server];
977         bool            repl_null[Natts_pg_foreign_server];
978         bool            repl_repl[Natts_pg_foreign_server];
979         Oid                     srvId;
980         Form_pg_foreign_server srvForm;
981         ObjectAddress address;
982
983         rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
984
985         tp = SearchSysCacheCopy1(FOREIGNSERVERNAME,
986                                                          CStringGetDatum(stmt->servername));
987
988         if (!HeapTupleIsValid(tp))
989                 ereport(ERROR,
990                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
991                                  errmsg("server \"%s\" does not exist", stmt->servername)));
992
993         srvId = HeapTupleGetOid(tp);
994         srvForm = (Form_pg_foreign_server) GETSTRUCT(tp);
995
996         /*
997          * Only owner or a superuser can ALTER a SERVER.
998          */
999         if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
1000                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
1001                                            stmt->servername);
1002
1003         memset(repl_val, 0, sizeof(repl_val));
1004         memset(repl_null, false, sizeof(repl_null));
1005         memset(repl_repl, false, sizeof(repl_repl));
1006
1007         if (stmt->has_version)
1008         {
1009                 /*
1010                  * Change the server VERSION string.
1011                  */
1012                 if (stmt->version)
1013                         repl_val[Anum_pg_foreign_server_srvversion - 1] =
1014                                 CStringGetTextDatum(stmt->version);
1015                 else
1016                         repl_null[Anum_pg_foreign_server_srvversion - 1] = true;
1017
1018                 repl_repl[Anum_pg_foreign_server_srvversion - 1] = true;
1019         }
1020
1021         if (stmt->options)
1022         {
1023                 ForeignDataWrapper *fdw = GetForeignDataWrapper(srvForm->srvfdw);
1024                 Datum           datum;
1025                 bool            isnull;
1026
1027                 /* Extract the current srvoptions */
1028                 datum = SysCacheGetAttr(FOREIGNSERVEROID,
1029                                                                 tp,
1030                                                                 Anum_pg_foreign_server_srvoptions,
1031                                                                 &isnull);
1032                 if (isnull)
1033                         datum = PointerGetDatum(NULL);
1034
1035                 /* Prepare the options array */
1036                 datum = transformGenericOptions(ForeignServerRelationId,
1037                                                                                 datum,
1038                                                                                 stmt->options,
1039                                                                                 fdw->fdwvalidator);
1040
1041                 if (PointerIsValid(DatumGetPointer(datum)))
1042                         repl_val[Anum_pg_foreign_server_srvoptions - 1] = datum;
1043                 else
1044                         repl_null[Anum_pg_foreign_server_srvoptions - 1] = true;
1045
1046                 repl_repl[Anum_pg_foreign_server_srvoptions - 1] = true;
1047         }
1048
1049         /* Everything looks good - update the tuple */
1050         tp = heap_modify_tuple(tp, RelationGetDescr(rel),
1051                                                    repl_val, repl_null, repl_repl);
1052
1053         CatalogTupleUpdate(rel, &tp->t_self, tp);
1054
1055         InvokeObjectPostAlterHook(ForeignServerRelationId, srvId, 0);
1056
1057         ObjectAddressSet(address, ForeignServerRelationId, srvId);
1058
1059         heap_freetuple(tp);
1060
1061         heap_close(rel, RowExclusiveLock);
1062
1063         return address;
1064 }
1065
1066
1067 /*
1068  * Drop foreign server by OID
1069  */
1070 void
1071 RemoveForeignServerById(Oid srvId)
1072 {
1073         HeapTuple       tp;
1074         Relation        rel;
1075
1076         rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
1077
1078         tp = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(srvId));
1079
1080         if (!HeapTupleIsValid(tp))
1081                 elog(ERROR, "cache lookup failed for foreign server %u", srvId);
1082
1083         CatalogTupleDelete(rel, &tp->t_self);
1084
1085         ReleaseSysCache(tp);
1086
1087         heap_close(rel, RowExclusiveLock);
1088 }
1089
1090
1091 /*
1092  * Common routine to check permission for user-mapping-related DDL
1093  * commands.  We allow server owners to operate on any mapping, and
1094  * users to operate on their own mapping.
1095  */
1096 static void
1097 user_mapping_ddl_aclcheck(Oid umuserid, Oid serverid, const char *servername)
1098 {
1099         Oid                     curuserid = GetUserId();
1100
1101         if (!pg_foreign_server_ownercheck(serverid, curuserid))
1102         {
1103                 if (umuserid == curuserid)
1104                 {
1105                         AclResult       aclresult;
1106
1107                         aclresult = pg_foreign_server_aclcheck(serverid, curuserid, ACL_USAGE);
1108                         if (aclresult != ACLCHECK_OK)
1109                                 aclcheck_error(aclresult, ACL_KIND_FOREIGN_SERVER, servername);
1110                 }
1111                 else
1112                         aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
1113                                                    servername);
1114         }
1115 }
1116
1117
1118 /*
1119  * Create user mapping
1120  */
1121 ObjectAddress
1122 CreateUserMapping(CreateUserMappingStmt *stmt)
1123 {
1124         Relation        rel;
1125         Datum           useoptions;
1126         Datum           values[Natts_pg_user_mapping];
1127         bool            nulls[Natts_pg_user_mapping];
1128         HeapTuple       tuple;
1129         Oid                     useId;
1130         Oid                     umId;
1131         ObjectAddress myself;
1132         ObjectAddress referenced;
1133         ForeignServer *srv;
1134         ForeignDataWrapper *fdw;
1135         RoleSpec   *role = (RoleSpec *) stmt->user;
1136
1137         rel = heap_open(UserMappingRelationId, RowExclusiveLock);
1138
1139         if (role->roletype == ROLESPEC_PUBLIC)
1140                 useId = ACL_ID_PUBLIC;
1141         else
1142                 useId = get_rolespec_oid(stmt->user, false);
1143
1144         /* Check that the server exists. */
1145         srv = GetForeignServerByName(stmt->servername, false);
1146
1147         user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
1148
1149         /*
1150          * Check that the user mapping is unique within server.
1151          */
1152         umId = GetSysCacheOid2(USERMAPPINGUSERSERVER,
1153                                                    ObjectIdGetDatum(useId),
1154                                                    ObjectIdGetDatum(srv->serverid));
1155         if (OidIsValid(umId))
1156                 ereport(ERROR,
1157                                 (errcode(ERRCODE_DUPLICATE_OBJECT),
1158                                  errmsg("user mapping \"%s\" already exists for server %s",
1159                                                 MappingUserName(useId),
1160                                                 stmt->servername)));
1161
1162         fdw = GetForeignDataWrapper(srv->fdwid);
1163
1164         /*
1165          * Insert tuple into pg_user_mapping.
1166          */
1167         memset(values, 0, sizeof(values));
1168         memset(nulls, false, sizeof(nulls));
1169
1170         values[Anum_pg_user_mapping_umuser - 1] = ObjectIdGetDatum(useId);
1171         values[Anum_pg_user_mapping_umserver - 1] = ObjectIdGetDatum(srv->serverid);
1172
1173         /* Add user options */
1174         useoptions = transformGenericOptions(UserMappingRelationId,
1175                                                                                  PointerGetDatum(NULL),
1176                                                                                  stmt->options,
1177                                                                                  fdw->fdwvalidator);
1178
1179         if (PointerIsValid(DatumGetPointer(useoptions)))
1180                 values[Anum_pg_user_mapping_umoptions - 1] = useoptions;
1181         else
1182                 nulls[Anum_pg_user_mapping_umoptions - 1] = true;
1183
1184         tuple = heap_form_tuple(rel->rd_att, values, nulls);
1185
1186         umId = CatalogTupleInsert(rel, tuple);
1187
1188         heap_freetuple(tuple);
1189
1190         /* Add dependency on the server */
1191         myself.classId = UserMappingRelationId;
1192         myself.objectId = umId;
1193         myself.objectSubId = 0;
1194
1195         referenced.classId = ForeignServerRelationId;
1196         referenced.objectId = srv->serverid;
1197         referenced.objectSubId = 0;
1198         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1199
1200         if (OidIsValid(useId))
1201         {
1202                 /* Record the mapped user dependency */
1203                 recordDependencyOnOwner(UserMappingRelationId, umId, useId);
1204         }
1205
1206         /* dependency on extension */
1207         recordDependencyOnCurrentExtension(&myself, false);
1208
1209         /* Post creation hook for new user mapping */
1210         InvokeObjectPostCreateHook(UserMappingRelationId, umId, 0);
1211
1212         heap_close(rel, RowExclusiveLock);
1213
1214         return myself;
1215 }
1216
1217
1218 /*
1219  * Alter user mapping
1220  */
1221 ObjectAddress
1222 AlterUserMapping(AlterUserMappingStmt *stmt)
1223 {
1224         Relation        rel;
1225         HeapTuple       tp;
1226         Datum           repl_val[Natts_pg_user_mapping];
1227         bool            repl_null[Natts_pg_user_mapping];
1228         bool            repl_repl[Natts_pg_user_mapping];
1229         Oid                     useId;
1230         Oid                     umId;
1231         ForeignServer *srv;
1232         ObjectAddress address;
1233         RoleSpec   *role = (RoleSpec *) stmt->user;
1234
1235         rel = heap_open(UserMappingRelationId, RowExclusiveLock);
1236
1237         if (role->roletype == ROLESPEC_PUBLIC)
1238                 useId = ACL_ID_PUBLIC;
1239         else
1240                 useId = get_rolespec_oid(stmt->user, false);
1241
1242         srv = GetForeignServerByName(stmt->servername, false);
1243
1244         umId = GetSysCacheOid2(USERMAPPINGUSERSERVER,
1245                                                    ObjectIdGetDatum(useId),
1246                                                    ObjectIdGetDatum(srv->serverid));
1247         if (!OidIsValid(umId))
1248                 ereport(ERROR,
1249                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
1250                                  errmsg("user mapping \"%s\" does not exist for the server",
1251                                                 MappingUserName(useId))));
1252
1253         user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
1254
1255         tp = SearchSysCacheCopy1(USERMAPPINGOID, ObjectIdGetDatum(umId));
1256
1257         if (!HeapTupleIsValid(tp))
1258                 elog(ERROR, "cache lookup failed for user mapping %u", umId);
1259
1260         memset(repl_val, 0, sizeof(repl_val));
1261         memset(repl_null, false, sizeof(repl_null));
1262         memset(repl_repl, false, sizeof(repl_repl));
1263
1264         if (stmt->options)
1265         {
1266                 ForeignDataWrapper *fdw;
1267                 Datum           datum;
1268                 bool            isnull;
1269
1270                 /*
1271                  * Process the options.
1272                  */
1273
1274                 fdw = GetForeignDataWrapper(srv->fdwid);
1275
1276                 datum = SysCacheGetAttr(USERMAPPINGUSERSERVER,
1277                                                                 tp,
1278                                                                 Anum_pg_user_mapping_umoptions,
1279                                                                 &isnull);
1280                 if (isnull)
1281                         datum = PointerGetDatum(NULL);
1282
1283                 /* Prepare the options array */
1284                 datum = transformGenericOptions(UserMappingRelationId,
1285                                                                                 datum,
1286                                                                                 stmt->options,
1287                                                                                 fdw->fdwvalidator);
1288
1289                 if (PointerIsValid(DatumGetPointer(datum)))
1290                         repl_val[Anum_pg_user_mapping_umoptions - 1] = datum;
1291                 else
1292                         repl_null[Anum_pg_user_mapping_umoptions - 1] = true;
1293
1294                 repl_repl[Anum_pg_user_mapping_umoptions - 1] = true;
1295         }
1296
1297         /* Everything looks good - update the tuple */
1298         tp = heap_modify_tuple(tp, RelationGetDescr(rel),
1299                                                    repl_val, repl_null, repl_repl);
1300
1301         CatalogTupleUpdate(rel, &tp->t_self, tp);
1302
1303         ObjectAddressSet(address, UserMappingRelationId, umId);
1304
1305         heap_freetuple(tp);
1306
1307         heap_close(rel, RowExclusiveLock);
1308
1309         return address;
1310 }
1311
1312
1313 /*
1314  * Drop user mapping
1315  */
1316 Oid
1317 RemoveUserMapping(DropUserMappingStmt *stmt)
1318 {
1319         ObjectAddress object;
1320         Oid                     useId;
1321         Oid                     umId;
1322         ForeignServer *srv;
1323         RoleSpec   *role = (RoleSpec *) stmt->user;
1324
1325         if (role->roletype == ROLESPEC_PUBLIC)
1326                 useId = ACL_ID_PUBLIC;
1327         else
1328         {
1329                 useId = get_rolespec_oid(stmt->user, stmt->missing_ok);
1330                 if (!OidIsValid(useId))
1331                 {
1332                         /*
1333                          * IF EXISTS specified, role not found and not public. Notice this
1334                          * and leave.
1335                          */
1336                         elog(NOTICE, "role \"%s\" does not exist, skipping",
1337                                  role->rolename);
1338                         return InvalidOid;
1339                 }
1340         }
1341
1342         srv = GetForeignServerByName(stmt->servername, true);
1343
1344         if (!srv)
1345         {
1346                 if (!stmt->missing_ok)
1347                         ereport(ERROR,
1348                                         (errcode(ERRCODE_UNDEFINED_OBJECT),
1349                                          errmsg("server \"%s\" does not exist",
1350                                                         stmt->servername)));
1351                 /* IF EXISTS, just note it */
1352                 ereport(NOTICE, (errmsg("server does not exist, skipping")));
1353                 return InvalidOid;
1354         }
1355
1356         umId = GetSysCacheOid2(USERMAPPINGUSERSERVER,
1357                                                    ObjectIdGetDatum(useId),
1358                                                    ObjectIdGetDatum(srv->serverid));
1359
1360         if (!OidIsValid(umId))
1361         {
1362                 if (!stmt->missing_ok)
1363                         ereport(ERROR,
1364                                         (errcode(ERRCODE_UNDEFINED_OBJECT),
1365                                   errmsg("user mapping \"%s\" does not exist for the server",
1366                                                  MappingUserName(useId))));
1367
1368                 /* IF EXISTS specified, just note it */
1369                 ereport(NOTICE,
1370                 (errmsg("user mapping \"%s\" does not exist for the server, skipping",
1371                                 MappingUserName(useId))));
1372                 return InvalidOid;
1373         }
1374
1375         user_mapping_ddl_aclcheck(useId, srv->serverid, srv->servername);
1376
1377         /*
1378          * Do the deletion
1379          */
1380         object.classId = UserMappingRelationId;
1381         object.objectId = umId;
1382         object.objectSubId = 0;
1383
1384         performDeletion(&object, DROP_CASCADE, 0);
1385
1386         return umId;
1387 }
1388
1389
1390 /*
1391  * Drop user mapping by OID.  This is called to clean up dependencies.
1392  */
1393 void
1394 RemoveUserMappingById(Oid umId)
1395 {
1396         HeapTuple       tp;
1397         Relation        rel;
1398
1399         rel = heap_open(UserMappingRelationId, RowExclusiveLock);
1400
1401         tp = SearchSysCache1(USERMAPPINGOID, ObjectIdGetDatum(umId));
1402
1403         if (!HeapTupleIsValid(tp))
1404                 elog(ERROR, "cache lookup failed for user mapping %u", umId);
1405
1406         CatalogTupleDelete(rel, &tp->t_self);
1407
1408         ReleaseSysCache(tp);
1409
1410         heap_close(rel, RowExclusiveLock);
1411 }
1412
1413 /*
1414  * Create a foreign table
1415  * call after DefineRelation().
1416  */
1417 void
1418 CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
1419 {
1420         Relation        ftrel;
1421         Datum           ftoptions;
1422         Datum           values[Natts_pg_foreign_table];
1423         bool            nulls[Natts_pg_foreign_table];
1424         HeapTuple       tuple;
1425         AclResult       aclresult;
1426         ObjectAddress myself;
1427         ObjectAddress referenced;
1428         Oid                     ownerId;
1429         ForeignDataWrapper *fdw;
1430         ForeignServer *server;
1431
1432         /*
1433          * Advance command counter to ensure the pg_attribute tuple is visible;
1434          * the tuple might be updated to add constraints in previous step.
1435          */
1436         CommandCounterIncrement();
1437
1438         ftrel = heap_open(ForeignTableRelationId, RowExclusiveLock);
1439
1440         /*
1441          * For now the owner cannot be specified on create. Use effective user ID.
1442          */
1443         ownerId = GetUserId();
1444
1445         /*
1446          * Check that the foreign server exists and that we have USAGE on it. Also
1447          * get the actual FDW for option validation etc.
1448          */
1449         server = GetForeignServerByName(stmt->servername, false);
1450         aclresult = pg_foreign_server_aclcheck(server->serverid, ownerId, ACL_USAGE);
1451         if (aclresult != ACLCHECK_OK)
1452                 aclcheck_error(aclresult, ACL_KIND_FOREIGN_SERVER, server->servername);
1453
1454         fdw = GetForeignDataWrapper(server->fdwid);
1455
1456         /*
1457          * Insert tuple into pg_foreign_table.
1458          */
1459         memset(values, 0, sizeof(values));
1460         memset(nulls, false, sizeof(nulls));
1461
1462         values[Anum_pg_foreign_table_ftrelid - 1] = ObjectIdGetDatum(relid);
1463         values[Anum_pg_foreign_table_ftserver - 1] = ObjectIdGetDatum(server->serverid);
1464         /* Add table generic options */
1465         ftoptions = transformGenericOptions(ForeignTableRelationId,
1466                                                                                 PointerGetDatum(NULL),
1467                                                                                 stmt->options,
1468                                                                                 fdw->fdwvalidator);
1469
1470         if (PointerIsValid(DatumGetPointer(ftoptions)))
1471                 values[Anum_pg_foreign_table_ftoptions - 1] = ftoptions;
1472         else
1473                 nulls[Anum_pg_foreign_table_ftoptions - 1] = true;
1474
1475         tuple = heap_form_tuple(ftrel->rd_att, values, nulls);
1476
1477         CatalogTupleInsert(ftrel, tuple);
1478
1479         heap_freetuple(tuple);
1480
1481         /* Add pg_class dependency on the server */
1482         myself.classId = RelationRelationId;
1483         myself.objectId = relid;
1484         myself.objectSubId = 0;
1485
1486         referenced.classId = ForeignServerRelationId;
1487         referenced.objectId = server->serverid;
1488         referenced.objectSubId = 0;
1489         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1490
1491         heap_close(ftrel, RowExclusiveLock);
1492 }
1493
1494 /*
1495  * Import a foreign schema
1496  */
1497 void
1498 ImportForeignSchema(ImportForeignSchemaStmt *stmt)
1499 {
1500         ForeignServer *server;
1501         ForeignDataWrapper *fdw;
1502         FdwRoutine *fdw_routine;
1503         AclResult       aclresult;
1504         List       *cmd_list;
1505         ListCell   *lc;
1506
1507         /* Check that the foreign server exists and that we have USAGE on it */
1508         server = GetForeignServerByName(stmt->server_name, false);
1509         aclresult = pg_foreign_server_aclcheck(server->serverid, GetUserId(), ACL_USAGE);
1510         if (aclresult != ACLCHECK_OK)
1511                 aclcheck_error(aclresult, ACL_KIND_FOREIGN_SERVER, server->servername);
1512
1513         /* Check that the schema exists and we have CREATE permissions on it */
1514         (void) LookupCreationNamespace(stmt->local_schema);
1515
1516         /* Get the FDW and check it supports IMPORT */
1517         fdw = GetForeignDataWrapper(server->fdwid);
1518         if (!OidIsValid(fdw->fdwhandler))
1519                 ereport(ERROR,
1520                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1521                                  errmsg("foreign-data wrapper \"%s\" has no handler",
1522                                                 fdw->fdwname)));
1523         fdw_routine = GetFdwRoutine(fdw->fdwhandler);
1524         if (fdw_routine->ImportForeignSchema == NULL)
1525                 ereport(ERROR,
1526                                 (errcode(ERRCODE_FDW_NO_SCHEMAS),
1527                                  errmsg("foreign-data wrapper \"%s\" does not support IMPORT FOREIGN SCHEMA",
1528                                                 fdw->fdwname)));
1529
1530         /* Call FDW to get a list of commands */
1531         cmd_list = fdw_routine->ImportForeignSchema(stmt, server->serverid);
1532
1533         /* Parse and execute each command */
1534         foreach(lc, cmd_list)
1535         {
1536                 char       *cmd = (char *) lfirst(lc);
1537                 import_error_callback_arg callback_arg;
1538                 ErrorContextCallback sqlerrcontext;
1539                 List       *raw_parsetree_list;
1540                 ListCell   *lc2;
1541
1542                 /*
1543                  * Setup error traceback support for ereport().  This is so that any
1544                  * error in the generated SQL will be displayed nicely.
1545                  */
1546                 callback_arg.tablename = NULL;  /* not known yet */
1547                 callback_arg.cmd = cmd;
1548                 sqlerrcontext.callback = import_error_callback;
1549                 sqlerrcontext.arg = (void *) &callback_arg;
1550                 sqlerrcontext.previous = error_context_stack;
1551                 error_context_stack = &sqlerrcontext;
1552
1553                 /*
1554                  * Parse the SQL string into a list of raw parse trees.
1555                  */
1556                 raw_parsetree_list = pg_parse_query(cmd);
1557
1558                 /*
1559                  * Process each parse tree (we allow the FDW to put more than one
1560                  * command per string, though this isn't really advised).
1561                  */
1562                 foreach(lc2, raw_parsetree_list)
1563                 {
1564                         RawStmt    *rs = castNode(RawStmt, lfirst(lc2));
1565                         CreateForeignTableStmt *cstmt = (CreateForeignTableStmt *) rs->stmt;
1566                         PlannedStmt *pstmt;
1567
1568                         /*
1569                          * Because we only allow CreateForeignTableStmt, we can skip parse
1570                          * analysis, rewrite, and planning steps here.
1571                          */
1572                         if (!IsA(cstmt, CreateForeignTableStmt))
1573                                 elog(ERROR,
1574                                          "foreign-data wrapper \"%s\" returned incorrect statement type %d",
1575                                          fdw->fdwname, (int) nodeTag(cstmt));
1576
1577                         /* Ignore commands for tables excluded by filter options */
1578                         if (!IsImportableForeignTable(cstmt->base.relation->relname, stmt))
1579                                 continue;
1580
1581                         /* Enable reporting of current table's name on error */
1582                         callback_arg.tablename = cstmt->base.relation->relname;
1583
1584                         /* Ensure creation schema is the one given in IMPORT statement */
1585                         cstmt->base.relation->schemaname = pstrdup(stmt->local_schema);
1586
1587                         /* No planning needed, just make a wrapper PlannedStmt */
1588                         pstmt = makeNode(PlannedStmt);
1589                         pstmt->commandType = CMD_UTILITY;
1590                         pstmt->canSetTag = false;
1591                         pstmt->utilityStmt = (Node *) cstmt;
1592                         pstmt->stmt_location = rs->stmt_location;
1593                         pstmt->stmt_len = rs->stmt_len;
1594
1595                         /* Execute statement */
1596                         ProcessUtility(pstmt,
1597                                                    cmd,
1598                                                    PROCESS_UTILITY_SUBCOMMAND, NULL,
1599                                                    None_Receiver, NULL);
1600
1601                         /* Be sure to advance the command counter between subcommands */
1602                         CommandCounterIncrement();
1603
1604                         callback_arg.tablename = NULL;
1605                 }
1606
1607                 error_context_stack = sqlerrcontext.previous;
1608         }
1609 }
1610
1611 /*
1612  * error context callback to let us supply the failing SQL statement's text
1613  */
1614 static void
1615 import_error_callback(void *arg)
1616 {
1617         import_error_callback_arg *callback_arg = (import_error_callback_arg *) arg;
1618         int                     syntaxerrposition;
1619
1620         /* If it's a syntax error, convert to internal syntax error report */
1621         syntaxerrposition = geterrposition();
1622         if (syntaxerrposition > 0)
1623         {
1624                 errposition(0);
1625                 internalerrposition(syntaxerrposition);
1626                 internalerrquery(callback_arg->cmd);
1627         }
1628
1629         if (callback_arg->tablename)
1630                 errcontext("importing foreign table \"%s\"",
1631                                    callback_arg->tablename);
1632 }