1 /*-------------------------------------------------------------------------
4 * foreign-data wrapper/server creation/manipulation commands
6 * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
10 * src/backend/commands/foreigncmds.c
12 *-------------------------------------------------------------------------
16 #include "access/heapam.h"
17 #include "access/xact.h"
18 #include "access/reloptions.h"
19 #include "catalog/dependency.h"
20 #include "catalog/indexing.h"
21 #include "catalog/objectaccess.h"
22 #include "catalog/pg_foreign_data_wrapper.h"
23 #include "catalog/pg_foreign_server.h"
24 #include "catalog/pg_foreign_table.h"
25 #include "catalog/pg_proc.h"
26 #include "catalog/pg_type.h"
27 #include "catalog/pg_user_mapping.h"
28 #include "commands/defrem.h"
29 #include "foreign/foreign.h"
30 #include "miscadmin.h"
31 #include "parser/parse_func.h"
32 #include "utils/acl.h"
33 #include "utils/builtins.h"
34 #include "utils/lsyscache.h"
35 #include "utils/rel.h"
36 #include "utils/syscache.h"
40 * Convert a DefElem list to the text array format that is used in
41 * pg_foreign_data_wrapper, pg_foreign_server, and pg_user_mapping.
42 * Returns the array in the form of a Datum, or PointerGetDatum(NULL)
43 * if the list is empty.
45 * Note: The array is usually stored to database without further
46 * processing, hence any validation should be done before this
50 optionListToArray(List *options)
52 ArrayBuildState *astate = NULL;
55 foreach(cell, options)
57 DefElem *def = lfirst(cell);
62 value = defGetString(def);
63 len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
66 sprintf(VARDATA(t), "%s=%s", def->defname, value);
68 astate = accumArrayResult(astate, PointerGetDatum(t),
70 CurrentMemoryContext);
74 return makeArrayResult(astate, CurrentMemoryContext);
76 return PointerGetDatum(NULL);
81 * Transform a list of DefElem into text array format. This is substantially
82 * the same thing as optionListToArray(), except we recognize SET/ADD/DROP
83 * actions for modifying an existing list of options, which is passed in
84 * Datum form as oldOptions. Also, if fdwvalidator isn't InvalidOid
85 * it specifies a validator function to call on the result.
87 * Returns the array in the form of a Datum, or PointerGetDatum(NULL)
88 * if the list is empty.
90 * This is used by CREATE/ALTER of FOREIGN DATA WRAPPER/SERVER/USER MAPPING.
93 transformGenericOptions(Oid catalogId,
98 List *resultOptions = untransformRelOptions(oldOptions);
102 foreach(optcell, options)
104 DefElem *od = lfirst(optcell);
106 ListCell *prev = NULL;
109 * Find the element in resultOptions. We need this for validation in
110 * all cases. Also identify the previous element.
112 foreach(cell, resultOptions)
114 DefElem *def = lfirst(cell);
116 if (strcmp(def->defname, od->defname) == 0)
123 * It is possible to perform multiple SET/DROP actions on the same
124 * option. The standard permits this, as long as the options to be
125 * added are unique. Note that an unspecified action is taken to be
128 switch (od->defaction)
133 (errcode(ERRCODE_UNDEFINED_OBJECT),
134 errmsg("option \"%s\" not found",
136 resultOptions = list_delete_cell(resultOptions, cell, prev);
142 (errcode(ERRCODE_UNDEFINED_OBJECT),
143 errmsg("option \"%s\" not found",
152 (errcode(ERRCODE_DUPLICATE_OBJECT),
153 errmsg("option \"%s\" provided more than once",
155 resultOptions = lappend(resultOptions, od);
159 elog(ERROR, "unrecognized action %d on option \"%s\"",
160 (int) od->defaction, od->defname);
165 result = optionListToArray(resultOptions);
167 if (OidIsValid(fdwvalidator))
169 Datum valarg = result;
172 * Pass a null options list as an empty array, so that validators
173 * don't have to be declared non-strict to handle the case.
175 if (DatumGetPointer(valarg) == NULL)
176 valarg = PointerGetDatum(construct_empty_array(TEXTOID));
177 OidFunctionCall2(fdwvalidator, valarg, ObjectIdGetDatum(catalogId));
185 * Convert the user mapping user name to OID
188 GetUserOidFromMapping(const char *username, bool missing_ok)
191 /* PUBLIC user mapping */
194 if (strcmp(username, "current_user") == 0)
195 /* map to the owner */
198 /* map to provided user */
199 return get_role_oid(username, missing_ok);
204 * Rename foreign-data wrapper
207 RenameForeignDataWrapper(const char *oldname, const char *newname)
212 rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
214 tup = SearchSysCacheCopy1(FOREIGNDATAWRAPPERNAME, CStringGetDatum(oldname));
215 if (!HeapTupleIsValid(tup))
217 (errcode(ERRCODE_UNDEFINED_OBJECT),
218 errmsg("foreign-data wrapper \"%s\" does not exist", oldname)));
220 /* make sure the new name doesn't exist */
221 if (SearchSysCacheExists1(FOREIGNDATAWRAPPERNAME, CStringGetDatum(newname)))
223 (errcode(ERRCODE_DUPLICATE_OBJECT),
224 errmsg("foreign-data wrapper \"%s\" already exists", newname)));
226 /* must be owner of FDW */
227 if (!pg_foreign_data_wrapper_ownercheck(HeapTupleGetOid(tup), GetUserId()))
228 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FDW,
232 namestrcpy(&(((Form_pg_foreign_data_wrapper) GETSTRUCT(tup))->fdwname), newname);
233 simple_heap_update(rel, &tup->t_self, tup);
234 CatalogUpdateIndexes(rel, tup);
236 heap_close(rel, NoLock);
242 * Rename foreign server
245 RenameForeignServer(const char *oldname, const char *newname)
250 rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
252 tup = SearchSysCacheCopy1(FOREIGNSERVERNAME, CStringGetDatum(oldname));
253 if (!HeapTupleIsValid(tup))
255 (errcode(ERRCODE_UNDEFINED_OBJECT),
256 errmsg("server \"%s\" does not exist", oldname)));
258 /* make sure the new name doesn't exist */
259 if (SearchSysCacheExists1(FOREIGNSERVERNAME, CStringGetDatum(newname)))
261 (errcode(ERRCODE_DUPLICATE_OBJECT),
262 errmsg("server \"%s\" already exists", newname)));
264 /* must be owner of server */
265 if (!pg_foreign_server_ownercheck(HeapTupleGetOid(tup), GetUserId()))
266 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
270 namestrcpy(&(((Form_pg_foreign_server) GETSTRUCT(tup))->srvname), newname);
271 simple_heap_update(rel, &tup->t_self, tup);
272 CatalogUpdateIndexes(rel, tup);
274 heap_close(rel, NoLock);
280 * Internal workhorse for changing a data wrapper's owner.
282 * Allow this only for superusers; also the new owner must be a
286 AlterForeignDataWrapperOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
288 Form_pg_foreign_data_wrapper form;
290 form = (Form_pg_foreign_data_wrapper) GETSTRUCT(tup);
292 /* Must be a superuser to change a FDW owner */
295 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
296 errmsg("permission denied to change owner of foreign-data wrapper \"%s\"",
297 NameStr(form->fdwname)),
298 errhint("Must be superuser to change owner of a foreign-data wrapper.")));
300 /* New owner must also be a superuser */
301 if (!superuser_arg(newOwnerId))
303 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
304 errmsg("permission denied to change owner of foreign-data wrapper \"%s\"",
305 NameStr(form->fdwname)),
306 errhint("The owner of a foreign-data wrapper must be a superuser.")));
308 if (form->fdwowner != newOwnerId)
310 form->fdwowner = newOwnerId;
312 simple_heap_update(rel, &tup->t_self, tup);
313 CatalogUpdateIndexes(rel, tup);
315 /* Update owner dependency reference */
316 changeDependencyOnOwner(ForeignDataWrapperRelationId,
317 HeapTupleGetOid(tup),
323 * Change foreign-data wrapper owner -- by name
325 * Note restrictions in the "_internal" function, above.
328 AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId)
333 rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
335 tup = SearchSysCacheCopy1(FOREIGNDATAWRAPPERNAME, CStringGetDatum(name));
337 if (!HeapTupleIsValid(tup))
339 (errcode(ERRCODE_UNDEFINED_OBJECT),
340 errmsg("foreign-data wrapper \"%s\" does not exist", name)));
342 AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId);
346 heap_close(rel, RowExclusiveLock);
350 * Change foreign-data wrapper owner -- by OID
352 * Note restrictions in the "_internal" function, above.
355 AlterForeignDataWrapperOwner_oid(Oid fwdId, Oid newOwnerId)
360 rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
362 tup = SearchSysCacheCopy1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fwdId));
364 if (!HeapTupleIsValid(tup))
366 (errcode(ERRCODE_UNDEFINED_OBJECT),
367 errmsg("foreign-data wrapper with OID %u does not exist", fwdId)));
369 AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId);
373 heap_close(rel, RowExclusiveLock);
377 * Internal workhorse for changing a foreign server's owner
380 AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
382 Form_pg_foreign_server form;
384 form = (Form_pg_foreign_server) GETSTRUCT(tup);
386 if (form->srvowner != newOwnerId)
388 /* Superusers can always do it */
394 srvId = HeapTupleGetOid(tup);
397 if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
398 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
399 NameStr(form->srvname));
401 /* Must be able to become new owner */
402 check_is_member_of_role(GetUserId(), newOwnerId);
404 /* New owner must have USAGE privilege on foreign-data wrapper */
405 aclresult = pg_foreign_data_wrapper_aclcheck(form->srvfdw, newOwnerId, ACL_USAGE);
406 if (aclresult != ACLCHECK_OK)
408 ForeignDataWrapper *fdw = GetForeignDataWrapper(form->srvfdw);
410 aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname);
414 form->srvowner = newOwnerId;
416 simple_heap_update(rel, &tup->t_self, tup);
417 CatalogUpdateIndexes(rel, tup);
419 /* Update owner dependency reference */
420 changeDependencyOnOwner(ForeignServerRelationId, HeapTupleGetOid(tup),
426 * Change foreign server owner -- by name
429 AlterForeignServerOwner(const char *name, Oid newOwnerId)
434 rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
436 tup = SearchSysCacheCopy1(FOREIGNSERVERNAME, CStringGetDatum(name));
438 if (!HeapTupleIsValid(tup))
440 (errcode(ERRCODE_UNDEFINED_OBJECT),
441 errmsg("server \"%s\" does not exist", name)));
443 AlterForeignServerOwner_internal(rel, tup, newOwnerId);
447 heap_close(rel, RowExclusiveLock);
451 * Change foreign server owner -- by OID
454 AlterForeignServerOwner_oid(Oid srvId, Oid newOwnerId)
459 rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
461 tup = SearchSysCacheCopy1(FOREIGNSERVEROID, ObjectIdGetDatum(srvId));
463 if (!HeapTupleIsValid(tup))
465 (errcode(ERRCODE_UNDEFINED_OBJECT),
466 errmsg("foreign server with OID %u does not exist", srvId)));
468 AlterForeignServerOwner_internal(rel, tup, newOwnerId);
472 heap_close(rel, RowExclusiveLock);
476 * Convert a handler function name passed from the parser to an Oid.
479 lookup_fdw_handler_func(DefElem *handler)
483 if (handler == NULL || handler->arg == NULL)
486 /* handlers have no arguments */
487 handlerOid = LookupFuncName((List *) handler->arg, 0, NULL, false);
489 /* check that handler has correct return type */
490 if (get_func_rettype(handlerOid) != FDW_HANDLEROID)
492 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
493 errmsg("function %s must return type \"fdw_handler\"",
494 NameListToString((List *) handler->arg))));
500 * Convert a validator function name passed from the parser to an Oid.
503 lookup_fdw_validator_func(DefElem *validator)
507 if (validator == NULL || validator->arg == NULL)
510 /* validators take text[], oid */
511 funcargtypes[0] = TEXTARRAYOID;
512 funcargtypes[1] = OIDOID;
514 return LookupFuncName((List *) validator->arg, 2, funcargtypes, false);
515 /* validator's return value is ignored, so we don't check the type */
519 * Process function options of CREATE/ALTER FDW
522 parse_func_options(List *func_options,
523 bool *handler_given, Oid *fdwhandler,
524 bool *validator_given, Oid *fdwvalidator)
528 *handler_given = false;
529 *validator_given = false;
530 /* return InvalidOid if not given */
531 *fdwhandler = InvalidOid;
532 *fdwvalidator = InvalidOid;
534 foreach(cell, func_options)
536 DefElem *def = (DefElem *) lfirst(cell);
538 if (strcmp(def->defname, "handler") == 0)
542 (errcode(ERRCODE_SYNTAX_ERROR),
543 errmsg("conflicting or redundant options")));
544 *handler_given = true;
545 *fdwhandler = lookup_fdw_handler_func(def);
547 else if (strcmp(def->defname, "validator") == 0)
549 if (*validator_given)
551 (errcode(ERRCODE_SYNTAX_ERROR),
552 errmsg("conflicting or redundant options")));
553 *validator_given = true;
554 *fdwvalidator = lookup_fdw_validator_func(def);
557 elog(ERROR, "option \"%s\" not recognized",
563 * Create a foreign-data wrapper
566 CreateForeignDataWrapper(CreateFdwStmt *stmt)
569 Datum values[Natts_pg_foreign_data_wrapper];
570 bool nulls[Natts_pg_foreign_data_wrapper];
574 bool validator_given;
579 ObjectAddress myself;
580 ObjectAddress referenced;
582 rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
584 /* Must be super user */
587 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
588 errmsg("permission denied to create foreign-data wrapper \"%s\"",
590 errhint("Must be superuser to create a foreign-data wrapper.")));
592 /* For now the owner cannot be specified on create. Use effective user ID. */
593 ownerId = GetUserId();
596 * Check that there is no other foreign-data wrapper by this name.
598 if (GetForeignDataWrapperByName(stmt->fdwname, true) != NULL)
600 (errcode(ERRCODE_DUPLICATE_OBJECT),
601 errmsg("foreign-data wrapper \"%s\" already exists",
605 * Insert tuple into pg_foreign_data_wrapper.
607 memset(values, 0, sizeof(values));
608 memset(nulls, false, sizeof(nulls));
610 values[Anum_pg_foreign_data_wrapper_fdwname - 1] =
611 DirectFunctionCall1(namein, CStringGetDatum(stmt->fdwname));
612 values[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(ownerId);
614 /* Lookup handler and validator functions, if given */
615 parse_func_options(stmt->func_options,
616 &handler_given, &fdwhandler,
617 &validator_given, &fdwvalidator);
619 values[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fdwhandler);
620 values[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fdwvalidator);
622 nulls[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
624 fdwoptions = transformGenericOptions(ForeignDataWrapperRelationId,
625 PointerGetDatum(NULL),
629 if (PointerIsValid(DatumGetPointer(fdwoptions)))
630 values[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = fdwoptions;
632 nulls[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
634 tuple = heap_form_tuple(rel->rd_att, values, nulls);
636 fdwId = simple_heap_insert(rel, tuple);
637 CatalogUpdateIndexes(rel, tuple);
639 heap_freetuple(tuple);
641 /* record dependencies */
642 myself.classId = ForeignDataWrapperRelationId;
643 myself.objectId = fdwId;
644 myself.objectSubId = 0;
646 if (OidIsValid(fdwhandler))
648 referenced.classId = ProcedureRelationId;
649 referenced.objectId = fdwhandler;
650 referenced.objectSubId = 0;
651 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
654 if (OidIsValid(fdwvalidator))
656 referenced.classId = ProcedureRelationId;
657 referenced.objectId = fdwvalidator;
658 referenced.objectSubId = 0;
659 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
662 recordDependencyOnOwner(ForeignDataWrapperRelationId, fdwId, ownerId);
664 /* dependency on extension */
665 recordDependencyOnCurrentExtension(&myself, false);
667 /* Post creation hook for new foreign data wrapper */
668 InvokeObjectAccessHook(OAT_POST_CREATE,
669 ForeignDataWrapperRelationId, fdwId, 0, NULL);
671 heap_close(rel, RowExclusiveLock);
676 * Alter foreign-data wrapper
679 AlterForeignDataWrapper(AlterFdwStmt *stmt)
683 Form_pg_foreign_data_wrapper fdwForm;
684 Datum repl_val[Natts_pg_foreign_data_wrapper];
685 bool repl_null[Natts_pg_foreign_data_wrapper];
686 bool repl_repl[Natts_pg_foreign_data_wrapper];
691 bool validator_given;
695 rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
697 /* Must be super user */
700 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
701 errmsg("permission denied to alter foreign-data wrapper \"%s\"",
703 errhint("Must be superuser to alter a foreign-data wrapper.")));
705 tp = SearchSysCacheCopy1(FOREIGNDATAWRAPPERNAME,
706 CStringGetDatum(stmt->fdwname));
708 if (!HeapTupleIsValid(tp))
710 (errcode(ERRCODE_UNDEFINED_OBJECT),
711 errmsg("foreign-data wrapper \"%s\" does not exist", stmt->fdwname)));
713 fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
714 fdwId = HeapTupleGetOid(tp);
716 memset(repl_val, 0, sizeof(repl_val));
717 memset(repl_null, false, sizeof(repl_null));
718 memset(repl_repl, false, sizeof(repl_repl));
720 parse_func_options(stmt->func_options,
721 &handler_given, &fdwhandler,
722 &validator_given, &fdwvalidator);
726 repl_val[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fdwhandler);
727 repl_repl[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = true;
730 * It could be that the behavior of accessing foreign table changes
731 * with the new handler. Warn about this.
734 (errmsg("changing the foreign-data wrapper handler can change behavior of existing foreign tables")));
739 repl_val[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fdwvalidator);
740 repl_repl[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = true;
743 * It could be that the options for the FDW, SERVER and USER MAPPING
744 * are no longer valid with the new validator. Warn about this.
746 if (OidIsValid(fdwvalidator))
748 (errmsg("changing the foreign-data wrapper validator can cause "
749 "the options for dependent objects to become invalid")));
754 * Validator is not changed, but we need it for validating options.
756 fdwvalidator = fdwForm->fdwvalidator;
760 * If options specified, validate and update.
764 /* Extract the current options */
765 datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
767 Anum_pg_foreign_data_wrapper_fdwoptions,
770 datum = PointerGetDatum(NULL);
772 /* Transform the options */
773 datum = transformGenericOptions(ForeignDataWrapperRelationId,
778 if (PointerIsValid(DatumGetPointer(datum)))
779 repl_val[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = datum;
781 repl_null[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
783 repl_repl[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
786 /* Everything looks good - update the tuple */
787 tp = heap_modify_tuple(tp, RelationGetDescr(rel),
788 repl_val, repl_null, repl_repl);
790 simple_heap_update(rel, &tp->t_self, tp);
791 CatalogUpdateIndexes(rel, tp);
795 /* Update function dependencies if we changed them */
796 if (handler_given || validator_given)
798 ObjectAddress myself;
799 ObjectAddress referenced;
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.
805 deleteDependencyRecordsForClass(ForeignDataWrapperRelationId,
810 /* And build new ones. */
811 myself.classId = ForeignDataWrapperRelationId;
812 myself.objectId = fdwId;
813 myself.objectSubId = 0;
815 if (OidIsValid(fdwhandler))
817 referenced.classId = ProcedureRelationId;
818 referenced.objectId = fdwhandler;
819 referenced.objectSubId = 0;
820 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
823 if (OidIsValid(fdwvalidator))
825 referenced.classId = ProcedureRelationId;
826 referenced.objectId = fdwvalidator;
827 referenced.objectSubId = 0;
828 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
832 heap_close(rel, RowExclusiveLock);
837 * Drop foreign-data wrapper by OID
840 RemoveForeignDataWrapperById(Oid fdwId)
845 rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
847 tp = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwId));
849 if (!HeapTupleIsValid(tp))
850 elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwId);
852 simple_heap_delete(rel, &tp->t_self);
856 heap_close(rel, RowExclusiveLock);
861 * Create a foreign server
864 CreateForeignServer(CreateForeignServerStmt *stmt)
868 Datum values[Natts_pg_foreign_server];
869 bool nulls[Natts_pg_foreign_server];
874 ObjectAddress myself;
875 ObjectAddress referenced;
876 ForeignDataWrapper *fdw;
878 rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
880 /* For now the owner cannot be specified on create. Use effective user ID. */
881 ownerId = GetUserId();
884 * Check that there is no other foreign server by this name.
886 if (GetForeignServerByName(stmt->servername, true) != NULL)
888 (errcode(ERRCODE_DUPLICATE_OBJECT),
889 errmsg("server \"%s\" already exists",
893 * Check that the FDW exists and that we have USAGE on it. Also get the
894 * actual FDW for option validation etc.
896 fdw = GetForeignDataWrapperByName(stmt->fdwname, false);
898 aclresult = pg_foreign_data_wrapper_aclcheck(fdw->fdwid, ownerId, ACL_USAGE);
899 if (aclresult != ACLCHECK_OK)
900 aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname);
903 * Insert tuple into pg_foreign_server.
905 memset(values, 0, sizeof(values));
906 memset(nulls, false, sizeof(nulls));
908 values[Anum_pg_foreign_server_srvname - 1] =
909 DirectFunctionCall1(namein, CStringGetDatum(stmt->servername));
910 values[Anum_pg_foreign_server_srvowner - 1] = ObjectIdGetDatum(ownerId);
911 values[Anum_pg_foreign_server_srvfdw - 1] = ObjectIdGetDatum(fdw->fdwid);
913 /* Add server type if supplied */
914 if (stmt->servertype)
915 values[Anum_pg_foreign_server_srvtype - 1] =
916 CStringGetTextDatum(stmt->servertype);
918 nulls[Anum_pg_foreign_server_srvtype - 1] = true;
920 /* Add server version if supplied */
922 values[Anum_pg_foreign_server_srvversion - 1] =
923 CStringGetTextDatum(stmt->version);
925 nulls[Anum_pg_foreign_server_srvversion - 1] = true;
927 /* Start with a blank acl */
928 nulls[Anum_pg_foreign_server_srvacl - 1] = true;
930 /* Add server options */
931 srvoptions = transformGenericOptions(ForeignServerRelationId,
932 PointerGetDatum(NULL),
936 if (PointerIsValid(DatumGetPointer(srvoptions)))
937 values[Anum_pg_foreign_server_srvoptions - 1] = srvoptions;
939 nulls[Anum_pg_foreign_server_srvoptions - 1] = true;
941 tuple = heap_form_tuple(rel->rd_att, values, nulls);
943 srvId = simple_heap_insert(rel, tuple);
945 CatalogUpdateIndexes(rel, tuple);
947 heap_freetuple(tuple);
949 /* record dependencies */
950 myself.classId = ForeignServerRelationId;
951 myself.objectId = srvId;
952 myself.objectSubId = 0;
954 referenced.classId = ForeignDataWrapperRelationId;
955 referenced.objectId = fdw->fdwid;
956 referenced.objectSubId = 0;
957 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
959 recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId);
961 /* dependency on extension */
962 recordDependencyOnCurrentExtension(&myself, false);
964 /* Post creation hook for new foreign server */
965 InvokeObjectAccessHook(OAT_POST_CREATE,
966 ForeignServerRelationId, srvId, 0, NULL);
968 heap_close(rel, RowExclusiveLock);
973 * Alter foreign server
976 AlterForeignServer(AlterForeignServerStmt *stmt)
980 Datum repl_val[Natts_pg_foreign_server];
981 bool repl_null[Natts_pg_foreign_server];
982 bool repl_repl[Natts_pg_foreign_server];
984 Form_pg_foreign_server srvForm;
986 rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
988 tp = SearchSysCacheCopy1(FOREIGNSERVERNAME,
989 CStringGetDatum(stmt->servername));
991 if (!HeapTupleIsValid(tp))
993 (errcode(ERRCODE_UNDEFINED_OBJECT),
994 errmsg("server \"%s\" does not exist", stmt->servername)));
996 srvId = HeapTupleGetOid(tp);
997 srvForm = (Form_pg_foreign_server) GETSTRUCT(tp);
1000 * Only owner or a superuser can ALTER a SERVER.
1002 if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
1003 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
1006 memset(repl_val, 0, sizeof(repl_val));
1007 memset(repl_null, false, sizeof(repl_null));
1008 memset(repl_repl, false, sizeof(repl_repl));
1010 if (stmt->has_version)
1013 * Change the server VERSION string.
1016 repl_val[Anum_pg_foreign_server_srvversion - 1] =
1017 CStringGetTextDatum(stmt->version);
1019 repl_null[Anum_pg_foreign_server_srvversion - 1] = true;
1021 repl_repl[Anum_pg_foreign_server_srvversion - 1] = true;
1026 ForeignDataWrapper *fdw = GetForeignDataWrapper(srvForm->srvfdw);
1030 /* Extract the current srvoptions */
1031 datum = SysCacheGetAttr(FOREIGNSERVEROID,
1033 Anum_pg_foreign_server_srvoptions,
1036 datum = PointerGetDatum(NULL);
1038 /* Prepare the options array */
1039 datum = transformGenericOptions(ForeignServerRelationId,
1044 if (PointerIsValid(DatumGetPointer(datum)))
1045 repl_val[Anum_pg_foreign_server_srvoptions - 1] = datum;
1047 repl_null[Anum_pg_foreign_server_srvoptions - 1] = true;
1049 repl_repl[Anum_pg_foreign_server_srvoptions - 1] = true;
1052 /* Everything looks good - update the tuple */
1053 tp = heap_modify_tuple(tp, RelationGetDescr(rel),
1054 repl_val, repl_null, repl_repl);
1056 simple_heap_update(rel, &tp->t_self, tp);
1057 CatalogUpdateIndexes(rel, tp);
1061 heap_close(rel, RowExclusiveLock);
1066 * Drop foreign server by OID
1069 RemoveForeignServerById(Oid srvId)
1074 rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
1076 tp = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(srvId));
1078 if (!HeapTupleIsValid(tp))
1079 elog(ERROR, "cache lookup failed for foreign server %u", srvId);
1081 simple_heap_delete(rel, &tp->t_self);
1083 ReleaseSysCache(tp);
1085 heap_close(rel, RowExclusiveLock);
1090 * Common routine to check permission for user-mapping-related DDL
1091 * commands. We allow server owners to operate on any mapping, and
1092 * users to operate on their own mapping.
1095 user_mapping_ddl_aclcheck(Oid umuserid, Oid serverid, const char *servername)
1097 Oid curuserid = GetUserId();
1099 if (!pg_foreign_server_ownercheck(serverid, curuserid))
1101 if (umuserid == curuserid)
1103 AclResult aclresult;
1105 aclresult = pg_foreign_server_aclcheck(serverid, curuserid, ACL_USAGE);
1106 if (aclresult != ACLCHECK_OK)
1107 aclcheck_error(aclresult, ACL_KIND_FOREIGN_SERVER, servername);
1110 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
1117 * Create user mapping
1120 CreateUserMapping(CreateUserMappingStmt *stmt)
1124 Datum values[Natts_pg_user_mapping];
1125 bool nulls[Natts_pg_user_mapping];
1129 ObjectAddress myself;
1130 ObjectAddress referenced;
1132 ForeignDataWrapper *fdw;
1134 rel = heap_open(UserMappingRelationId, RowExclusiveLock);
1136 useId = GetUserOidFromMapping(stmt->username, false);
1138 /* Check that the server exists. */
1139 srv = GetForeignServerByName(stmt->servername, false);
1141 user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
1144 * Check that the user mapping is unique within server.
1146 umId = GetSysCacheOid2(USERMAPPINGUSERSERVER,
1147 ObjectIdGetDatum(useId),
1148 ObjectIdGetDatum(srv->serverid));
1149 if (OidIsValid(umId))
1151 (errcode(ERRCODE_DUPLICATE_OBJECT),
1152 errmsg("user mapping \"%s\" already exists for server %s",
1153 MappingUserName(useId),
1154 stmt->servername)));
1156 fdw = GetForeignDataWrapper(srv->fdwid);
1159 * Insert tuple into pg_user_mapping.
1161 memset(values, 0, sizeof(values));
1162 memset(nulls, false, sizeof(nulls));
1164 values[Anum_pg_user_mapping_umuser - 1] = ObjectIdGetDatum(useId);
1165 values[Anum_pg_user_mapping_umserver - 1] = ObjectIdGetDatum(srv->serverid);
1167 /* Add user options */
1168 useoptions = transformGenericOptions(UserMappingRelationId,
1169 PointerGetDatum(NULL),
1173 if (PointerIsValid(DatumGetPointer(useoptions)))
1174 values[Anum_pg_user_mapping_umoptions - 1] = useoptions;
1176 nulls[Anum_pg_user_mapping_umoptions - 1] = true;
1178 tuple = heap_form_tuple(rel->rd_att, values, nulls);
1180 umId = simple_heap_insert(rel, tuple);
1182 CatalogUpdateIndexes(rel, tuple);
1184 heap_freetuple(tuple);
1186 /* Add dependency on the server */
1187 myself.classId = UserMappingRelationId;
1188 myself.objectId = umId;
1189 myself.objectSubId = 0;
1191 referenced.classId = ForeignServerRelationId;
1192 referenced.objectId = srv->serverid;
1193 referenced.objectSubId = 0;
1194 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1196 if (OidIsValid(useId))
1198 /* Record the mapped user dependency */
1199 recordDependencyOnOwner(UserMappingRelationId, umId, useId);
1202 /* dependency on extension */
1203 recordDependencyOnCurrentExtension(&myself, false);
1205 /* Post creation hook for new user mapping */
1206 InvokeObjectAccessHook(OAT_POST_CREATE,
1207 UserMappingRelationId, umId, 0, NULL);
1209 heap_close(rel, RowExclusiveLock);
1214 * Alter user mapping
1217 AlterUserMapping(AlterUserMappingStmt *stmt)
1221 Datum repl_val[Natts_pg_user_mapping];
1222 bool repl_null[Natts_pg_user_mapping];
1223 bool repl_repl[Natts_pg_user_mapping];
1228 rel = heap_open(UserMappingRelationId, RowExclusiveLock);
1230 useId = GetUserOidFromMapping(stmt->username, false);
1231 srv = GetForeignServerByName(stmt->servername, false);
1233 umId = GetSysCacheOid2(USERMAPPINGUSERSERVER,
1234 ObjectIdGetDatum(useId),
1235 ObjectIdGetDatum(srv->serverid));
1236 if (!OidIsValid(umId))
1238 (errcode(ERRCODE_UNDEFINED_OBJECT),
1239 errmsg("user mapping \"%s\" does not exist for the server",
1240 MappingUserName(useId))));
1242 user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
1244 tp = SearchSysCacheCopy1(USERMAPPINGOID, ObjectIdGetDatum(umId));
1246 if (!HeapTupleIsValid(tp))
1247 elog(ERROR, "cache lookup failed for user mapping %u", umId);
1249 memset(repl_val, 0, sizeof(repl_val));
1250 memset(repl_null, false, sizeof(repl_null));
1251 memset(repl_repl, false, sizeof(repl_repl));
1255 ForeignDataWrapper *fdw;
1260 * Process the options.
1263 fdw = GetForeignDataWrapper(srv->fdwid);
1265 datum = SysCacheGetAttr(USERMAPPINGUSERSERVER,
1267 Anum_pg_user_mapping_umoptions,
1270 datum = PointerGetDatum(NULL);
1272 /* Prepare the options array */
1273 datum = transformGenericOptions(UserMappingRelationId,
1278 if (PointerIsValid(DatumGetPointer(datum)))
1279 repl_val[Anum_pg_user_mapping_umoptions - 1] = datum;
1281 repl_null[Anum_pg_user_mapping_umoptions - 1] = true;
1283 repl_repl[Anum_pg_user_mapping_umoptions - 1] = true;
1286 /* Everything looks good - update the tuple */
1287 tp = heap_modify_tuple(tp, RelationGetDescr(rel),
1288 repl_val, repl_null, repl_repl);
1290 simple_heap_update(rel, &tp->t_self, tp);
1291 CatalogUpdateIndexes(rel, tp);
1295 heap_close(rel, RowExclusiveLock);
1303 RemoveUserMapping(DropUserMappingStmt *stmt)
1305 ObjectAddress object;
1310 useId = GetUserOidFromMapping(stmt->username, stmt->missing_ok);
1311 srv = GetForeignServerByName(stmt->servername, true);
1313 if (stmt->username && !OidIsValid(useId))
1316 * IF EXISTS specified, role not found and not public. Notice this and
1319 elog(NOTICE, "role \"%s\" does not exist, skipping", stmt->username);
1325 if (!stmt->missing_ok)
1327 (errcode(ERRCODE_UNDEFINED_OBJECT),
1328 errmsg("server \"%s\" does not exist",
1329 stmt->servername)));
1330 /* IF EXISTS, just note it */
1331 ereport(NOTICE, (errmsg("server does not exist, skipping")));
1335 umId = GetSysCacheOid2(USERMAPPINGUSERSERVER,
1336 ObjectIdGetDatum(useId),
1337 ObjectIdGetDatum(srv->serverid));
1339 if (!OidIsValid(umId))
1341 if (!stmt->missing_ok)
1343 (errcode(ERRCODE_UNDEFINED_OBJECT),
1344 errmsg("user mapping \"%s\" does not exist for the server",
1345 MappingUserName(useId))));
1347 /* IF EXISTS specified, just note it */
1349 (errmsg("user mapping \"%s\" does not exist for the server, skipping",
1350 MappingUserName(useId))));
1354 user_mapping_ddl_aclcheck(useId, srv->serverid, srv->servername);
1359 object.classId = UserMappingRelationId;
1360 object.objectId = umId;
1361 object.objectSubId = 0;
1363 performDeletion(&object, DROP_CASCADE, 0);
1368 * Drop user mapping by OID. This is called to clean up dependencies.
1371 RemoveUserMappingById(Oid umId)
1376 rel = heap_open(UserMappingRelationId, RowExclusiveLock);
1378 tp = SearchSysCache1(USERMAPPINGOID, ObjectIdGetDatum(umId));
1380 if (!HeapTupleIsValid(tp))
1381 elog(ERROR, "cache lookup failed for user mapping %u", umId);
1383 simple_heap_delete(rel, &tp->t_self);
1385 ReleaseSysCache(tp);
1387 heap_close(rel, RowExclusiveLock);
1391 * Create a foreign table
1392 * call after DefineRelation().
1395 CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
1399 Datum values[Natts_pg_foreign_table];
1400 bool nulls[Natts_pg_foreign_table];
1402 AclResult aclresult;
1403 ObjectAddress myself;
1404 ObjectAddress referenced;
1406 ForeignDataWrapper *fdw;
1407 ForeignServer *server;
1410 * Advance command counter to ensure the pg_attribute tuple is visible;
1411 * the tuple might be updated to add constraints in previous step.
1413 CommandCounterIncrement();
1415 ftrel = heap_open(ForeignTableRelationId, RowExclusiveLock);
1418 * For now the owner cannot be specified on create. Use effective user ID.
1420 ownerId = GetUserId();
1423 * Check that the foreign server exists and that we have USAGE on it. Also
1424 * get the actual FDW for option validation etc.
1426 server = GetForeignServerByName(stmt->servername, false);
1427 aclresult = pg_foreign_server_aclcheck(server->serverid, ownerId, ACL_USAGE);
1428 if (aclresult != ACLCHECK_OK)
1429 aclcheck_error(aclresult, ACL_KIND_FOREIGN_SERVER, server->servername);
1431 fdw = GetForeignDataWrapper(server->fdwid);
1434 * Insert tuple into pg_foreign_table.
1436 memset(values, 0, sizeof(values));
1437 memset(nulls, false, sizeof(nulls));
1439 values[Anum_pg_foreign_table_ftrelid - 1] = ObjectIdGetDatum(relid);
1440 values[Anum_pg_foreign_table_ftserver - 1] = ObjectIdGetDatum(server->serverid);
1441 /* Add table generic options */
1442 ftoptions = transformGenericOptions(ForeignTableRelationId,
1443 PointerGetDatum(NULL),
1447 if (PointerIsValid(DatumGetPointer(ftoptions)))
1448 values[Anum_pg_foreign_table_ftoptions - 1] = ftoptions;
1450 nulls[Anum_pg_foreign_table_ftoptions - 1] = true;
1452 tuple = heap_form_tuple(ftrel->rd_att, values, nulls);
1454 simple_heap_insert(ftrel, tuple);
1455 CatalogUpdateIndexes(ftrel, tuple);
1457 heap_freetuple(tuple);
1459 /* Add pg_class dependency on the server */
1460 myself.classId = RelationRelationId;
1461 myself.objectId = relid;
1462 myself.objectSubId = 0;
1464 referenced.classId = ForeignServerRelationId;
1465 referenced.objectId = server->serverid;
1466 referenced.objectSubId = 0;
1467 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1469 heap_close(ftrel, RowExclusiveLock);