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