1 /*-------------------------------------------------------------------------
4 * foreign-data wrapper/server creation/manipulation commands
6 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
12 *-------------------------------------------------------------------------
16 #include "access/heapam.h"
17 #include "access/reloptions.h"
18 #include "catalog/catalog.h"
19 #include "catalog/dependency.h"
20 #include "catalog/indexing.h"
21 #include "catalog/pg_foreign_data_wrapper.h"
22 #include "catalog/pg_foreign_server.h"
23 #include "catalog/pg_proc.h"
24 #include "catalog/pg_type.h"
25 #include "catalog/pg_user_mapping.h"
26 #include "commands/defrem.h"
27 #include "foreign/foreign.h"
28 #include "miscadmin.h"
29 #include "parser/parse_func.h"
30 #include "utils/acl.h"
31 #include "utils/builtins.h"
32 #include "utils/lsyscache.h"
33 #include "utils/rel.h"
34 #include "utils/syscache.h"
38 * Convert a DefElem list to the text array format that is used in
39 * pg_foreign_data_wrapper, pg_foreign_server, and pg_user_mapping.
40 * Returns the array in the form of a Datum, or PointerGetDatum(NULL)
41 * if the list is empty.
43 * Note: The array is usually stored to database without further
44 * processing, hence any validation should be done before this
48 optionListToArray(List
*options
)
50 ArrayBuildState
*astate
= NULL
;
53 foreach(cell
, options
)
55 DefElem
*def
= lfirst(cell
);
60 value
= defGetString(def
);
61 len
= VARHDRSZ
+ strlen(def
->defname
) + 1 + strlen(value
);
64 sprintf(VARDATA(t
), "%s=%s", def
->defname
, value
);
66 astate
= accumArrayResult(astate
, PointerGetDatum(t
),
68 CurrentMemoryContext
);
72 return makeArrayResult(astate
, CurrentMemoryContext
);
74 return PointerGetDatum(NULL
);
79 * Transform a list of DefElem into text array format. This is substantially
80 * the same thing as optionListToArray(), except we recognize SET/ADD/DROP
81 * actions for modifying an existing list of options, which is passed in
82 * Datum form as oldOptions. Also, if fdwvalidator isn't InvalidOid
83 * it specifies a validator function to call on the result.
85 * Returns the array in the form of a Datum, or PointerGetDatum(NULL)
86 * if the list is empty.
88 * This is used by CREATE/ALTER of FOREIGN DATA WRAPPER/SERVER/USER MAPPING.
91 transformGenericOptions(Datum oldOptions
,
95 List
*resultOptions
= untransformRelOptions(oldOptions
);
99 foreach(optcell
, options
)
101 DefElem
*od
= lfirst(optcell
);
103 ListCell
*prev
= NULL
;
106 * Find the element in resultOptions. We need this for validation in
107 * all cases. Also identify the previous element.
109 foreach(cell
, resultOptions
)
111 DefElem
*def
= lfirst(cell
);
113 if (strcmp(def
->defname
, od
->defname
) == 0)
120 * It is possible to perform multiple SET/DROP actions on the same
121 * option. The standard permits this, as long as the options to be
122 * added are unique. Note that an unspecified action is taken to be
125 switch (od
->defaction
)
130 (errcode(ERRCODE_UNDEFINED_OBJECT
),
131 errmsg("option \"%s\" not found",
133 resultOptions
= list_delete_cell(resultOptions
, cell
, prev
);
139 (errcode(ERRCODE_UNDEFINED_OBJECT
),
140 errmsg("option \"%s\" not found",
149 (errcode(ERRCODE_DUPLICATE_OBJECT
),
150 errmsg("option \"%s\" provided more than once",
152 resultOptions
= lappend(resultOptions
, od
);
156 elog(ERROR
, "unrecognized action %d on option \"%s\"",
157 (int) od
->defaction
, od
->defname
);
162 result
= optionListToArray(resultOptions
);
165 OidFunctionCall2(fdwvalidator
, result
, (Datum
) 0);
172 * Convert the user mapping user name to OID
175 GetUserOidFromMapping(const char *username
, bool missing_ok
)
178 /* PUBLIC user mapping */
181 if (strcmp(username
, "current_user") == 0)
182 /* map to the owner */
185 /* map to provided user */
186 return missing_ok
? get_roleid(username
) : get_roleid_checked(username
);
191 * Change foreign-data wrapper owner.
193 * Allow this only for superusers; also the new owner must be a
197 AlterForeignDataWrapperOwner(const char *name
, Oid newOwnerId
)
202 Form_pg_foreign_data_wrapper form
;
204 /* Must be a superuser to change a FDW owner */
207 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
208 errmsg("permission denied to change owner of foreign-data wrapper \"%s\"",
210 errhint("Must be superuser to change owner of a foreign-data wrapper.")));
212 /* New owner must also be a superuser */
213 if (!superuser_arg(newOwnerId
))
215 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
216 errmsg("permission denied to change owner of foreign-data wrapper \"%s\"",
218 errhint("The owner of a foreign-data wrapper must be a superuser.")));
220 rel
= heap_open(ForeignDataWrapperRelationId
, RowExclusiveLock
);
222 tup
= SearchSysCacheCopy(FOREIGNDATAWRAPPERNAME
,
223 CStringGetDatum(name
),
226 if (!HeapTupleIsValid(tup
))
228 (errcode(ERRCODE_UNDEFINED_OBJECT
),
229 errmsg("foreign-data wrapper \"%s\" does not exist", name
)));
231 fdwId
= HeapTupleGetOid(tup
);
232 form
= (Form_pg_foreign_data_wrapper
) GETSTRUCT(tup
);
234 if (form
->fdwowner
!= newOwnerId
)
236 form
->fdwowner
= newOwnerId
;
238 simple_heap_update(rel
, &tup
->t_self
, tup
);
239 CatalogUpdateIndexes(rel
, tup
);
241 /* Update owner dependency reference */
242 changeDependencyOnOwner(ForeignDataWrapperRelationId
,
247 heap_close(rel
, NoLock
);
253 * Change foreign server owner
256 AlterForeignServerOwner(const char *name
, Oid newOwnerId
)
262 Form_pg_foreign_server form
;
264 rel
= heap_open(ForeignServerRelationId
, RowExclusiveLock
);
266 tup
= SearchSysCacheCopy(FOREIGNSERVERNAME
,
267 CStringGetDatum(name
),
270 if (!HeapTupleIsValid(tup
))
272 (errcode(ERRCODE_UNDEFINED_OBJECT
),
273 errmsg("server \"%s\" does not exist", name
)));
275 srvId
= HeapTupleGetOid(tup
);
276 form
= (Form_pg_foreign_server
) GETSTRUCT(tup
);
278 if (form
->srvowner
!= newOwnerId
)
280 /* Superusers can always do it */
284 if (!pg_foreign_server_ownercheck(srvId
, GetUserId()))
285 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_FOREIGN_SERVER
,
288 /* Must be able to become new owner */
289 check_is_member_of_role(GetUserId(), newOwnerId
);
291 /* New owner must have USAGE privilege on foreign-data wrapper */
292 aclresult
= pg_foreign_data_wrapper_aclcheck(form
->srvfdw
, newOwnerId
, ACL_USAGE
);
293 if (aclresult
!= ACLCHECK_OK
)
295 ForeignDataWrapper
*fdw
= GetForeignDataWrapper(form
->srvfdw
);
297 aclcheck_error(aclresult
, ACL_KIND_FDW
, fdw
->fdwname
);
301 form
->srvowner
= newOwnerId
;
303 simple_heap_update(rel
, &tup
->t_self
, tup
);
304 CatalogUpdateIndexes(rel
, tup
);
306 /* Update owner dependency reference */
307 changeDependencyOnOwner(ForeignServerRelationId
, HeapTupleGetOid(tup
),
311 heap_close(rel
, NoLock
);
317 * Convert a validator function name passed from the parser to an Oid.
320 lookup_fdw_validator_func(List
*validator
)
324 funcargtypes
[0] = TEXTARRAYOID
;
325 funcargtypes
[1] = OIDOID
;
326 return LookupFuncName(validator
, 2, funcargtypes
, false);
327 /* return value is ignored, so we don't check the type */
332 * Create a foreign-data wrapper
335 CreateForeignDataWrapper(CreateFdwStmt
*stmt
)
338 Datum values
[Natts_pg_foreign_data_wrapper
];
339 bool nulls
[Natts_pg_foreign_data_wrapper
];
346 /* Must be super user */
349 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
350 errmsg("permission denied to create foreign-data wrapper \"%s\"",
352 errhint("Must be superuser to create a foreign-data wrapper.")));
354 /* For now the owner cannot be specified on create. Use effective user ID. */
355 ownerId
= GetUserId();
358 * Check that there is no other foreign-data wrapper by this name.
360 if (GetForeignDataWrapperByName(stmt
->fdwname
, true) != NULL
)
362 (errcode(ERRCODE_DUPLICATE_OBJECT
),
363 errmsg("foreign-data wrapper \"%s\" already exists",
367 * Insert tuple into pg_foreign_data_wrapper.
369 rel
= heap_open(ForeignDataWrapperRelationId
, RowExclusiveLock
);
371 memset(values
, 0, sizeof(values
));
372 memset(nulls
, false, sizeof(nulls
));
374 values
[Anum_pg_foreign_data_wrapper_fdwname
- 1] =
375 DirectFunctionCall1(namein
, CStringGetDatum(stmt
->fdwname
));
376 values
[Anum_pg_foreign_data_wrapper_fdwowner
- 1] = ObjectIdGetDatum(ownerId
);
379 fdwvalidator
= lookup_fdw_validator_func(stmt
->validator
);
381 fdwvalidator
= InvalidOid
;
383 values
[Anum_pg_foreign_data_wrapper_fdwvalidator
- 1] = fdwvalidator
;
385 nulls
[Anum_pg_foreign_data_wrapper_fdwacl
- 1] = true;
387 fdwoptions
= transformGenericOptions(PointerGetDatum(NULL
), stmt
->options
,
390 if (PointerIsValid(DatumGetPointer(fdwoptions
)))
391 values
[Anum_pg_foreign_data_wrapper_fdwoptions
- 1] = fdwoptions
;
393 nulls
[Anum_pg_foreign_data_wrapper_fdwoptions
- 1] = true;
395 tuple
= heap_form_tuple(rel
->rd_att
, values
, nulls
);
397 fdwId
= simple_heap_insert(rel
, tuple
);
398 CatalogUpdateIndexes(rel
, tuple
);
400 heap_freetuple(tuple
);
404 ObjectAddress myself
;
405 ObjectAddress referenced
;
407 myself
.classId
= ForeignDataWrapperRelationId
;
408 myself
.objectId
= fdwId
;
409 myself
.objectSubId
= 0;
411 referenced
.classId
= ProcedureRelationId
;
412 referenced
.objectId
= fdwvalidator
;
413 referenced
.objectSubId
= 0;
414 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
417 recordDependencyOnOwner(ForeignDataWrapperRelationId
, fdwId
, ownerId
);
419 heap_close(rel
, NoLock
);
424 * Alter foreign-data wrapper
427 AlterForeignDataWrapper(AlterFdwStmt
*stmt
)
431 Datum repl_val
[Natts_pg_foreign_data_wrapper
];
432 bool repl_null
[Natts_pg_foreign_data_wrapper
];
433 bool repl_repl
[Natts_pg_foreign_data_wrapper
];
439 /* Must be super user */
442 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
443 errmsg("permission denied to alter foreign-data wrapper \"%s\"",
445 errhint("Must be superuser to alter a foreign-data wrapper.")));
447 tp
= SearchSysCacheCopy(FOREIGNDATAWRAPPERNAME
,
448 CStringGetDatum(stmt
->fdwname
),
451 if (!HeapTupleIsValid(tp
))
453 (errcode(ERRCODE_UNDEFINED_OBJECT
),
454 errmsg("foreign-data wrapper \"%s\" does not exist", stmt
->fdwname
)));
456 fdwId
= HeapTupleGetOid(tp
);
458 memset(repl_val
, 0, sizeof(repl_val
));
459 memset(repl_null
, false, sizeof(repl_null
));
460 memset(repl_repl
, false, sizeof(repl_repl
));
462 if (stmt
->change_validator
)
464 fdwvalidator
= stmt
->validator
? lookup_fdw_validator_func(stmt
->validator
) : InvalidOid
;
465 repl_val
[Anum_pg_foreign_data_wrapper_fdwvalidator
- 1] = ObjectIdGetDatum(fdwvalidator
);
466 repl_repl
[Anum_pg_foreign_data_wrapper_fdwvalidator
- 1] = true;
469 * It could be that the options for the FDW, SERVER and USER MAPPING
470 * are no longer valid with the new validator. Warn about this.
474 (errmsg("changing the foreign-data wrapper validator can cause "
475 "the options for dependent objects to become invalid")));
480 * Validator is not changed, but we need it for validating options.
482 datum
= SysCacheGetAttr(FOREIGNDATAWRAPPEROID
,
484 Anum_pg_foreign_data_wrapper_fdwvalidator
,
487 fdwvalidator
= DatumGetObjectId(datum
);
491 * Options specified, validate and update.
495 /* Extract the current options */
496 datum
= SysCacheGetAttr(FOREIGNDATAWRAPPEROID
,
498 Anum_pg_foreign_data_wrapper_fdwoptions
,
501 datum
= PointerGetDatum(NULL
);
503 /* Transform the options */
504 datum
= transformGenericOptions(datum
, stmt
->options
, fdwvalidator
);
506 if (PointerIsValid(DatumGetPointer(datum
)))
507 repl_val
[Anum_pg_foreign_data_wrapper_fdwoptions
- 1] = datum
;
509 repl_null
[Anum_pg_foreign_data_wrapper_fdwoptions
- 1] = true;
511 repl_repl
[Anum_pg_foreign_data_wrapper_fdwoptions
- 1] = true;
514 /* Everything looks good - update the tuple */
516 rel
= heap_open(ForeignDataWrapperRelationId
, RowExclusiveLock
);
518 tp
= heap_modify_tuple(tp
, RelationGetDescr(rel
),
519 repl_val
, repl_null
, repl_repl
);
521 simple_heap_update(rel
, &tp
->t_self
, tp
);
522 CatalogUpdateIndexes(rel
, tp
);
524 heap_close(rel
, RowExclusiveLock
);
530 * Drop foreign-data wrapper
533 RemoveForeignDataWrapper(DropFdwStmt
*stmt
)
536 ObjectAddress object
;
538 fdwId
= GetForeignDataWrapperOidByName(stmt
->fdwname
, true);
542 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
543 errmsg("permission denied to drop foreign-data wrapper \"%s\"",
545 errhint("Must be superuser to drop a foreign-data wrapper.")));
547 if (!OidIsValid(fdwId
))
549 if (!stmt
->missing_ok
)
551 (errcode(ERRCODE_UNDEFINED_OBJECT
),
552 errmsg("foreign-data wrapper \"%s\" does not exist",
555 /* IF EXISTS specified, just note it */
557 (errmsg("foreign-data wrapper \"%s\" does not exist, skipping",
565 object
.classId
= ForeignDataWrapperRelationId
;
566 object
.objectId
= fdwId
;
567 object
.objectSubId
= 0;
569 performDeletion(&object
, stmt
->behavior
);
574 * Drop foreign-data wrapper by OID
577 RemoveForeignDataWrapperById(Oid fdwId
)
582 rel
= heap_open(ForeignDataWrapperRelationId
, RowExclusiveLock
);
584 tp
= SearchSysCache(FOREIGNDATAWRAPPEROID
,
585 ObjectIdGetDatum(fdwId
),
588 if (!HeapTupleIsValid(tp
))
589 elog(ERROR
, "cache lookup failed for foreign-data wrapper %u", fdwId
);
591 simple_heap_delete(rel
, &tp
->t_self
);
595 heap_close(rel
, RowExclusiveLock
);
600 * Create a foreign server
603 CreateForeignServer(CreateForeignServerStmt
*stmt
)
607 Datum values
[Natts_pg_foreign_server
];
608 bool nulls
[Natts_pg_foreign_server
];
613 ObjectAddress myself
;
614 ObjectAddress referenced
;
615 ForeignDataWrapper
*fdw
;
617 /* For now the owner cannot be specified on create. Use effective user ID. */
618 ownerId
= GetUserId();
621 * Check that there is no other foreign server by this name.
623 if (GetForeignServerByName(stmt
->servername
, true) != NULL
)
625 (errcode(ERRCODE_DUPLICATE_OBJECT
),
626 errmsg("server \"%s\" already exists",
630 * Check that the FDW exists and that we have USAGE on it. Also get the
631 * actual FDW for option validation etc.
633 fdw
= GetForeignDataWrapperByName(stmt
->fdwname
, false);
635 aclresult
= pg_foreign_data_wrapper_aclcheck(fdw
->fdwid
, ownerId
, ACL_USAGE
);
636 if (aclresult
!= ACLCHECK_OK
)
637 aclcheck_error(aclresult
, ACL_KIND_FDW
, fdw
->fdwname
);
640 * Insert tuple into pg_foreign_server.
642 rel
= heap_open(ForeignServerRelationId
, RowExclusiveLock
);
644 memset(values
, 0, sizeof(values
));
645 memset(nulls
, false, sizeof(nulls
));
647 values
[Anum_pg_foreign_server_srvname
- 1] =
648 DirectFunctionCall1(namein
, CStringGetDatum(stmt
->servername
));
649 values
[Anum_pg_foreign_server_srvowner
- 1] = ObjectIdGetDatum(ownerId
);
650 values
[Anum_pg_foreign_server_srvfdw
- 1] = ObjectIdGetDatum(fdw
->fdwid
);
652 /* Add server type if supplied */
653 if (stmt
->servertype
)
654 values
[Anum_pg_foreign_server_srvtype
- 1] =
655 CStringGetTextDatum(stmt
->servertype
);
657 nulls
[Anum_pg_foreign_server_srvtype
- 1] = true;
659 /* Add server version if supplied */
661 values
[Anum_pg_foreign_server_srvversion
- 1] =
662 CStringGetTextDatum(stmt
->version
);
664 nulls
[Anum_pg_foreign_server_srvversion
- 1] = true;
666 /* Start with a blank acl */
667 nulls
[Anum_pg_foreign_server_srvacl
- 1] = true;
669 /* Add server options */
670 srvoptions
= transformGenericOptions(PointerGetDatum(NULL
), stmt
->options
,
673 if (PointerIsValid(DatumGetPointer(srvoptions
)))
674 values
[Anum_pg_foreign_server_srvoptions
- 1] = srvoptions
;
676 nulls
[Anum_pg_foreign_server_srvoptions
- 1] = true;
678 tuple
= heap_form_tuple(rel
->rd_att
, values
, nulls
);
680 srvId
= simple_heap_insert(rel
, tuple
);
682 CatalogUpdateIndexes(rel
, tuple
);
684 heap_freetuple(tuple
);
686 /* Add dependency on FDW and owner */
687 myself
.classId
= ForeignServerRelationId
;
688 myself
.objectId
= srvId
;
689 myself
.objectSubId
= 0;
691 referenced
.classId
= ForeignDataWrapperRelationId
;
692 referenced
.objectId
= fdw
->fdwid
;
693 referenced
.objectSubId
= 0;
694 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
696 recordDependencyOnOwner(ForeignServerRelationId
, srvId
, ownerId
);
698 heap_close(rel
, NoLock
);
703 * Alter foreign server
706 AlterForeignServer(AlterForeignServerStmt
*stmt
)
710 Datum repl_val
[Natts_pg_foreign_server
];
711 bool repl_null
[Natts_pg_foreign_server
];
712 bool repl_repl
[Natts_pg_foreign_server
];
714 Form_pg_foreign_server srvForm
;
716 tp
= SearchSysCacheCopy(FOREIGNSERVERNAME
,
717 CStringGetDatum(stmt
->servername
),
720 if (!HeapTupleIsValid(tp
))
722 (errcode(ERRCODE_UNDEFINED_OBJECT
),
723 errmsg("server \"%s\" does not exist", stmt
->servername
)));
725 srvId
= HeapTupleGetOid(tp
);
726 srvForm
= (Form_pg_foreign_server
) GETSTRUCT(tp
);
729 * Only owner or a superuser can ALTER a SERVER.
731 if (!pg_foreign_server_ownercheck(srvId
, GetUserId()))
732 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_FOREIGN_SERVER
,
735 memset(repl_val
, 0, sizeof(repl_val
));
736 memset(repl_null
, false, sizeof(repl_null
));
737 memset(repl_repl
, false, sizeof(repl_repl
));
739 if (stmt
->has_version
)
742 * Change the server VERSION string.
745 repl_val
[Anum_pg_foreign_server_srvversion
- 1] =
746 CStringGetTextDatum(stmt
->version
);
748 repl_null
[Anum_pg_foreign_server_srvversion
- 1] = true;
750 repl_repl
[Anum_pg_foreign_server_srvversion
- 1] = true;
755 ForeignDataWrapper
*fdw
= GetForeignDataWrapper(srvForm
->srvfdw
);
759 /* Extract the current srvoptions */
760 datum
= SysCacheGetAttr(FOREIGNSERVEROID
,
762 Anum_pg_foreign_server_srvoptions
,
765 datum
= PointerGetDatum(NULL
);
767 /* Prepare the options array */
768 datum
= transformGenericOptions(datum
, stmt
->options
,
771 if (PointerIsValid(DatumGetPointer(datum
)))
772 repl_val
[Anum_pg_foreign_server_srvoptions
- 1] = datum
;
774 repl_null
[Anum_pg_foreign_server_srvoptions
- 1] = true;
776 repl_repl
[Anum_pg_foreign_server_srvoptions
- 1] = true;
779 /* Everything looks good - update the tuple */
781 rel
= heap_open(ForeignServerRelationId
, RowExclusiveLock
);
783 tp
= heap_modify_tuple(tp
, RelationGetDescr(rel
),
784 repl_val
, repl_null
, repl_repl
);
786 simple_heap_update(rel
, &tp
->t_self
, tp
);
787 CatalogUpdateIndexes(rel
, tp
);
789 heap_close(rel
, RowExclusiveLock
);
795 * Drop foreign server
798 RemoveForeignServer(DropForeignServerStmt
*stmt
)
801 ObjectAddress object
;
803 srvId
= GetForeignServerOidByName(stmt
->servername
, true);
805 if (!OidIsValid(srvId
))
807 /* Server not found, complain or notice */
808 if (!stmt
->missing_ok
)
810 (errcode(ERRCODE_UNDEFINED_OBJECT
),
811 errmsg("server \"%s\" does not exist", stmt
->servername
)));
813 /* IF EXISTS specified, just note it */
815 (errmsg("server \"%s\" does not exist, skipping",
820 /* Only allow DROP if the server is owned by the user. */
821 if (!pg_foreign_server_ownercheck(srvId
, GetUserId()))
822 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_FOREIGN_SERVER
,
825 object
.classId
= ForeignServerRelationId
;
826 object
.objectId
= srvId
;
827 object
.objectSubId
= 0;
829 performDeletion(&object
, stmt
->behavior
);
834 * Drop foreign server by OID
837 RemoveForeignServerById(Oid srvId
)
842 rel
= heap_open(ForeignServerRelationId
, RowExclusiveLock
);
844 tp
= SearchSysCache(FOREIGNSERVEROID
,
845 ObjectIdGetDatum(srvId
),
848 if (!HeapTupleIsValid(tp
))
849 elog(ERROR
, "cache lookup failed for foreign server %u", srvId
);
851 simple_heap_delete(rel
, &tp
->t_self
);
855 heap_close(rel
, RowExclusiveLock
);
860 * Common routine to check permission for user-mapping-related DDL
861 * commands. We allow server owners to operate on any mapping, and
862 * users to operate on their own mapping.
865 user_mapping_ddl_aclcheck(Oid umuserid
, Oid serverid
, const char *servername
)
867 Oid curuserid
= GetUserId();
869 if (!pg_foreign_server_ownercheck(serverid
, curuserid
))
871 if (umuserid
== curuserid
)
875 aclresult
= pg_foreign_server_aclcheck(serverid
, curuserid
, ACL_USAGE
);
876 if (aclresult
!= ACLCHECK_OK
)
877 aclcheck_error(aclresult
, ACL_KIND_FOREIGN_SERVER
, servername
);
880 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_FOREIGN_SERVER
,
887 * Create user mapping
890 CreateUserMapping(CreateUserMappingStmt
*stmt
)
894 Datum values
[Natts_pg_user_mapping
];
895 bool nulls
[Natts_pg_user_mapping
];
899 ObjectAddress myself
;
900 ObjectAddress referenced
;
902 ForeignDataWrapper
*fdw
;
904 useId
= GetUserOidFromMapping(stmt
->username
, false);
906 /* Check that the server exists. */
907 srv
= GetForeignServerByName(stmt
->servername
, false);
909 user_mapping_ddl_aclcheck(useId
, srv
->serverid
, stmt
->servername
);
912 * Check that the user mapping is unique within server.
914 umId
= GetSysCacheOid(USERMAPPINGUSERSERVER
,
915 ObjectIdGetDatum(useId
),
916 ObjectIdGetDatum(srv
->serverid
),
918 if (OidIsValid(umId
))
920 (errcode(ERRCODE_DUPLICATE_OBJECT
),
921 errmsg("user mapping \"%s\" already exists for server %s",
922 MappingUserName(useId
),
925 fdw
= GetForeignDataWrapper(srv
->fdwid
);
928 * Insert tuple into pg_user_mapping.
930 rel
= heap_open(UserMappingRelationId
, RowExclusiveLock
);
932 memset(values
, 0, sizeof(values
));
933 memset(nulls
, false, sizeof(nulls
));
935 values
[Anum_pg_user_mapping_umuser
- 1] = ObjectIdGetDatum(useId
);
936 values
[Anum_pg_user_mapping_umserver
- 1] = ObjectIdGetDatum(srv
->serverid
);
938 /* Add user options */
939 useoptions
= transformGenericOptions(PointerGetDatum(NULL
), stmt
->options
,
942 if (PointerIsValid(DatumGetPointer(useoptions
)))
943 values
[Anum_pg_user_mapping_umoptions
- 1] = useoptions
;
945 nulls
[Anum_pg_user_mapping_umoptions
- 1] = true;
947 tuple
= heap_form_tuple(rel
->rd_att
, values
, nulls
);
949 umId
= simple_heap_insert(rel
, tuple
);
951 CatalogUpdateIndexes(rel
, tuple
);
953 heap_freetuple(tuple
);
955 /* Add dependency on the server */
956 myself
.classId
= UserMappingRelationId
;
957 myself
.objectId
= umId
;
958 myself
.objectSubId
= 0;
960 referenced
.classId
= ForeignServerRelationId
;
961 referenced
.objectId
= srv
->serverid
;
962 referenced
.objectSubId
= 0;
963 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
965 if (OidIsValid(useId
))
966 /* Record the mapped user dependency */
967 recordDependencyOnOwner(UserMappingRelationId
, umId
, useId
);
969 heap_close(rel
, NoLock
);
977 AlterUserMapping(AlterUserMappingStmt
*stmt
)
981 Datum repl_val
[Natts_pg_user_mapping
];
982 bool repl_null
[Natts_pg_user_mapping
];
983 bool repl_repl
[Natts_pg_user_mapping
];
988 useId
= GetUserOidFromMapping(stmt
->username
, false);
989 srv
= GetForeignServerByName(stmt
->servername
, false);
991 umId
= GetSysCacheOid(USERMAPPINGUSERSERVER
,
992 ObjectIdGetDatum(useId
),
993 ObjectIdGetDatum(srv
->serverid
),
995 if (!OidIsValid(umId
))
997 (errcode(ERRCODE_UNDEFINED_OBJECT
),
998 errmsg("user mapping \"%s\" does not exist for the server",
999 MappingUserName(useId
))));
1001 user_mapping_ddl_aclcheck(useId
, srv
->serverid
, stmt
->servername
);
1003 tp
= SearchSysCacheCopy(USERMAPPINGOID
,
1004 ObjectIdGetDatum(umId
),
1007 if (!HeapTupleIsValid(tp
))
1008 elog(ERROR
, "cache lookup failed for user mapping %u", umId
);
1010 memset(repl_val
, 0, sizeof(repl_val
));
1011 memset(repl_null
, false, sizeof(repl_null
));
1012 memset(repl_repl
, false, sizeof(repl_repl
));
1016 ForeignDataWrapper
*fdw
;
1021 * Process the options.
1024 fdw
= GetForeignDataWrapper(srv
->fdwid
);
1026 datum
= SysCacheGetAttr(USERMAPPINGUSERSERVER
,
1028 Anum_pg_user_mapping_umoptions
,
1031 datum
= PointerGetDatum(NULL
);
1033 /* Prepare the options array */
1034 datum
= transformGenericOptions(datum
, stmt
->options
,
1037 if (PointerIsValid(DatumGetPointer(datum
)))
1038 repl_val
[Anum_pg_user_mapping_umoptions
- 1] = datum
;
1040 repl_null
[Anum_pg_user_mapping_umoptions
- 1] = true;
1042 repl_repl
[Anum_pg_user_mapping_umoptions
- 1] = true;
1045 /* Everything looks good - update the tuple */
1047 rel
= heap_open(UserMappingRelationId
, RowExclusiveLock
);
1049 tp
= heap_modify_tuple(tp
, RelationGetDescr(rel
),
1050 repl_val
, repl_null
, repl_repl
);
1052 simple_heap_update(rel
, &tp
->t_self
, tp
);
1053 CatalogUpdateIndexes(rel
, tp
);
1055 heap_close(rel
, RowExclusiveLock
);
1064 RemoveUserMapping(DropUserMappingStmt
*stmt
)
1066 ObjectAddress object
;
1071 useId
= GetUserOidFromMapping(stmt
->username
, stmt
->missing_ok
);
1072 srv
= GetForeignServerByName(stmt
->servername
, true);
1074 if (stmt
->username
&& !OidIsValid(useId
))
1077 * IF EXISTS specified, role not found and not public. Notice this and
1080 elog(NOTICE
, "role \"%s\" does not exist, skipping", stmt
->username
);
1086 if (!stmt
->missing_ok
)
1088 (errcode(ERRCODE_UNDEFINED_OBJECT
),
1089 errmsg("server \"%s\" does not exist",
1090 stmt
->servername
)));
1091 /* IF EXISTS, just note it */
1092 ereport(NOTICE
, (errmsg("server does not exist, skipping")));
1096 umId
= GetSysCacheOid(USERMAPPINGUSERSERVER
,
1097 ObjectIdGetDatum(useId
),
1098 ObjectIdGetDatum(srv
->serverid
),
1101 if (!OidIsValid(umId
))
1103 if (!stmt
->missing_ok
)
1105 (errcode(ERRCODE_UNDEFINED_OBJECT
),
1106 errmsg("user mapping \"%s\" does not exist for the server",
1107 MappingUserName(useId
))));
1109 /* IF EXISTS specified, just note it */
1111 (errmsg("user mapping \"%s\" does not exist for the server, skipping",
1112 MappingUserName(useId
))));
1116 user_mapping_ddl_aclcheck(useId
, srv
->serverid
, srv
->servername
);
1121 object
.classId
= UserMappingRelationId
;
1122 object
.objectId
= umId
;
1123 object
.objectSubId
= 0;
1125 performDeletion(&object
, DROP_CASCADE
);
1130 * Drop user mapping by OID. This is called to clean up dependencies.
1133 RemoveUserMappingById(Oid umId
)
1138 rel
= heap_open(UserMappingRelationId
, RowExclusiveLock
);
1140 tp
= SearchSysCache(USERMAPPINGOID
,
1141 ObjectIdGetDatum(umId
),
1144 if (!HeapTupleIsValid(tp
))
1145 elog(ERROR
, "cache lookup failed for user mapping %u", umId
);
1147 simple_heap_delete(rel
, &tp
->t_self
);
1149 ReleaseSysCache(tp
);
1151 heap_close(rel
, RowExclusiveLock
);