Fix xslt_process() to ensure that it inserts a NULL terminator after the
[PostgreSQL.git] / src / backend / commands / foreigncmds.c
blob46493b144aab6e329c48460db32fd129c95a9144
1 /*-------------------------------------------------------------------------
3 * foreigncmds.c
4 * foreign-data wrapper/server creation/manipulation commands
6 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
9 * IDENTIFICATION
10 * $PostgreSQL$
12 *-------------------------------------------------------------------------
14 #include "postgres.h"
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
45 * conversion.
47 static Datum
48 optionListToArray(List *options)
50 ArrayBuildState *astate = NULL;
51 ListCell *cell;
53 foreach(cell, options)
55 DefElem *def = lfirst(cell);
56 const char *value;
57 Size len;
58 text *t;
60 value = defGetString(def);
61 len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
62 t = palloc(len + 1);
63 SET_VARSIZE(t, len);
64 sprintf(VARDATA(t), "%s=%s", def->defname, value);
66 astate = accumArrayResult(astate, PointerGetDatum(t),
67 false, TEXTOID,
68 CurrentMemoryContext);
71 if (astate)
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.
90 static Datum
91 transformGenericOptions(Datum oldOptions,
92 List *options,
93 Oid fdwvalidator)
95 List *resultOptions = untransformRelOptions(oldOptions);
96 ListCell *optcell;
97 Datum result;
99 foreach(optcell, options)
101 DefElem *od = lfirst(optcell);
102 ListCell *cell;
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)
114 break;
115 else
116 prev = cell;
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
123 * ADD.
125 switch (od->defaction)
127 case DEFELEM_DROP:
128 if (!cell)
129 ereport(ERROR,
130 (errcode(ERRCODE_UNDEFINED_OBJECT),
131 errmsg("option \"%s\" not found",
132 od->defname)));
133 resultOptions = list_delete_cell(resultOptions, cell, prev);
134 break;
136 case DEFELEM_SET:
137 if (!cell)
138 ereport(ERROR,
139 (errcode(ERRCODE_UNDEFINED_OBJECT),
140 errmsg("option \"%s\" not found",
141 od->defname)));
142 lfirst(cell) = od;
143 break;
145 case DEFELEM_ADD:
146 case DEFELEM_UNSPEC:
147 if (cell)
148 ereport(ERROR,
149 (errcode(ERRCODE_DUPLICATE_OBJECT),
150 errmsg("option \"%s\" provided more than once",
151 od->defname)));
152 resultOptions = lappend(resultOptions, od);
153 break;
155 default:
156 elog(ERROR, "unrecognized action %d on option \"%s\"",
157 (int) od->defaction, od->defname);
158 break;
162 result = optionListToArray(resultOptions);
164 if (fdwvalidator)
165 OidFunctionCall2(fdwvalidator, result, (Datum) 0);
167 return result;
172 * Convert the user mapping user name to OID
174 static Oid
175 GetUserOidFromMapping(const char *username, bool missing_ok)
177 if (!username)
178 /* PUBLIC user mapping */
179 return InvalidOid;
181 if (strcmp(username, "current_user") == 0)
182 /* map to the owner */
183 return GetUserId();
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
194 * superuser.
196 void
197 AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId)
199 HeapTuple tup;
200 Relation rel;
201 Oid fdwId;
202 Form_pg_foreign_data_wrapper form;
204 /* Must be a superuser to change a FDW owner */
205 if (!superuser())
206 ereport(ERROR,
207 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
208 errmsg("permission denied to change owner of foreign-data wrapper \"%s\"",
209 name),
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))
214 ereport(ERROR,
215 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
216 errmsg("permission denied to change owner of foreign-data wrapper \"%s\"",
217 name),
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),
224 0, 0, 0);
226 if (!HeapTupleIsValid(tup))
227 ereport(ERROR,
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,
243 fdwId,
244 newOwnerId);
247 heap_close(rel, NoLock);
248 heap_freetuple(tup);
253 * Change foreign server owner
255 void
256 AlterForeignServerOwner(const char *name, Oid newOwnerId)
258 HeapTuple tup;
259 Relation rel;
260 Oid srvId;
261 AclResult aclresult;
262 Form_pg_foreign_server form;
264 rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
266 tup = SearchSysCacheCopy(FOREIGNSERVERNAME,
267 CStringGetDatum(name),
268 0, 0, 0);
270 if (!HeapTupleIsValid(tup))
271 ereport(ERROR,
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 */
281 if (!superuser())
283 /* Must be owner */
284 if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
285 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
286 name);
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),
308 newOwnerId);
311 heap_close(rel, NoLock);
312 heap_freetuple(tup);
317 * Convert a validator function name passed from the parser to an Oid.
319 static Oid
320 lookup_fdw_validator_func(List *validator)
322 Oid funcargtypes[2];
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
334 void
335 CreateForeignDataWrapper(CreateFdwStmt *stmt)
337 Relation rel;
338 Datum values[Natts_pg_foreign_data_wrapper];
339 bool nulls[Natts_pg_foreign_data_wrapper];
340 HeapTuple tuple;
341 Oid fdwId;
342 Oid fdwvalidator;
343 Datum fdwoptions;
344 Oid ownerId;
346 /* Must be super user */
347 if (!superuser())
348 ereport(ERROR,
349 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
350 errmsg("permission denied to create foreign-data wrapper \"%s\"",
351 stmt->fdwname),
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)
361 ereport(ERROR,
362 (errcode(ERRCODE_DUPLICATE_OBJECT),
363 errmsg("foreign-data wrapper \"%s\" already exists",
364 stmt->fdwname)));
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);
378 if (stmt->validator)
379 fdwvalidator = lookup_fdw_validator_func(stmt->validator);
380 else
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,
388 fdwvalidator);
390 if (PointerIsValid(DatumGetPointer(fdwoptions)))
391 values[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = fdwoptions;
392 else
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);
402 if (fdwvalidator)
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
426 void
427 AlterForeignDataWrapper(AlterFdwStmt *stmt)
429 Relation rel;
430 HeapTuple tp;
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];
434 Oid fdwId;
435 bool isnull;
436 Datum datum;
437 Oid fdwvalidator;
439 /* Must be super user */
440 if (!superuser())
441 ereport(ERROR,
442 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
443 errmsg("permission denied to alter foreign-data wrapper \"%s\"",
444 stmt->fdwname),
445 errhint("Must be superuser to alter a foreign-data wrapper.")));
447 tp = SearchSysCacheCopy(FOREIGNDATAWRAPPERNAME,
448 CStringGetDatum(stmt->fdwname),
449 0, 0, 0);
451 if (!HeapTupleIsValid(tp))
452 ereport(ERROR,
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.
472 if (stmt->validator)
473 ereport(WARNING,
474 (errmsg("changing the foreign-data wrapper validator can cause "
475 "the options for dependent objects to become invalid")));
477 else
480 * Validator is not changed, but we need it for validating options.
482 datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
484 Anum_pg_foreign_data_wrapper_fdwvalidator,
485 &isnull);
486 Assert(!isnull);
487 fdwvalidator = DatumGetObjectId(datum);
491 * Options specified, validate and update.
493 if (stmt->options)
495 /* Extract the current options */
496 datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
498 Anum_pg_foreign_data_wrapper_fdwoptions,
499 &isnull);
500 if (isnull)
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;
508 else
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);
525 heap_freetuple(tp);
530 * Drop foreign-data wrapper
532 void
533 RemoveForeignDataWrapper(DropFdwStmt *stmt)
535 Oid fdwId;
536 ObjectAddress object;
538 fdwId = GetForeignDataWrapperOidByName(stmt->fdwname, true);
540 if (!superuser())
541 ereport(ERROR,
542 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
543 errmsg("permission denied to drop foreign-data wrapper \"%s\"",
544 stmt->fdwname),
545 errhint("Must be superuser to drop a foreign-data wrapper.")));
547 if (!OidIsValid(fdwId))
549 if (!stmt->missing_ok)
550 ereport(ERROR,
551 (errcode(ERRCODE_UNDEFINED_OBJECT),
552 errmsg("foreign-data wrapper \"%s\" does not exist",
553 stmt->fdwname)));
555 /* IF EXISTS specified, just note it */
556 ereport(NOTICE,
557 (errmsg("foreign-data wrapper \"%s\" does not exist, skipping",
558 stmt->fdwname)));
559 return;
563 * Do the deletion
565 object.classId = ForeignDataWrapperRelationId;
566 object.objectId = fdwId;
567 object.objectSubId = 0;
569 performDeletion(&object, stmt->behavior);
574 * Drop foreign-data wrapper by OID
576 void
577 RemoveForeignDataWrapperById(Oid fdwId)
579 HeapTuple tp;
580 Relation rel;
582 rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
584 tp = SearchSysCache(FOREIGNDATAWRAPPEROID,
585 ObjectIdGetDatum(fdwId),
586 0, 0, 0);
588 if (!HeapTupleIsValid(tp))
589 elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwId);
591 simple_heap_delete(rel, &tp->t_self);
593 ReleaseSysCache(tp);
595 heap_close(rel, RowExclusiveLock);
600 * Create a foreign server
602 void
603 CreateForeignServer(CreateForeignServerStmt *stmt)
605 Relation rel;
606 Datum srvoptions;
607 Datum values[Natts_pg_foreign_server];
608 bool nulls[Natts_pg_foreign_server];
609 HeapTuple tuple;
610 Oid srvId;
611 Oid ownerId;
612 AclResult aclresult;
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)
624 ereport(ERROR,
625 (errcode(ERRCODE_DUPLICATE_OBJECT),
626 errmsg("server \"%s\" already exists",
627 stmt->servername)));
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);
656 else
657 nulls[Anum_pg_foreign_server_srvtype - 1] = true;
659 /* Add server version if supplied */
660 if (stmt->version)
661 values[Anum_pg_foreign_server_srvversion - 1] =
662 CStringGetTextDatum(stmt->version);
663 else
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,
671 fdw->fdwvalidator);
673 if (PointerIsValid(DatumGetPointer(srvoptions)))
674 values[Anum_pg_foreign_server_srvoptions - 1] = srvoptions;
675 else
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
705 void
706 AlterForeignServer(AlterForeignServerStmt *stmt)
708 Relation rel;
709 HeapTuple tp;
710 Datum repl_val[Natts_pg_foreign_server];
711 bool repl_null[Natts_pg_foreign_server];
712 bool repl_repl[Natts_pg_foreign_server];
713 Oid srvId;
714 Form_pg_foreign_server srvForm;
716 tp = SearchSysCacheCopy(FOREIGNSERVERNAME,
717 CStringGetDatum(stmt->servername),
718 0, 0, 0);
720 if (!HeapTupleIsValid(tp))
721 ereport(ERROR,
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,
733 stmt->servername);
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.
744 if (stmt->version)
745 repl_val[Anum_pg_foreign_server_srvversion - 1] =
746 CStringGetTextDatum(stmt->version);
747 else
748 repl_null[Anum_pg_foreign_server_srvversion - 1] = true;
750 repl_repl[Anum_pg_foreign_server_srvversion - 1] = true;
753 if (stmt->options)
755 ForeignDataWrapper *fdw = GetForeignDataWrapper(srvForm->srvfdw);
756 Datum datum;
757 bool isnull;
759 /* Extract the current srvoptions */
760 datum = SysCacheGetAttr(FOREIGNSERVEROID,
762 Anum_pg_foreign_server_srvoptions,
763 &isnull);
764 if (isnull)
765 datum = PointerGetDatum(NULL);
767 /* Prepare the options array */
768 datum = transformGenericOptions(datum, stmt->options,
769 fdw->fdwvalidator);
771 if (PointerIsValid(DatumGetPointer(datum)))
772 repl_val[Anum_pg_foreign_server_srvoptions - 1] = datum;
773 else
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);
790 heap_freetuple(tp);
795 * Drop foreign server
797 void
798 RemoveForeignServer(DropForeignServerStmt *stmt)
800 Oid srvId;
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)
809 ereport(ERROR,
810 (errcode(ERRCODE_UNDEFINED_OBJECT),
811 errmsg("server \"%s\" does not exist", stmt->servername)));
813 /* IF EXISTS specified, just note it */
814 ereport(NOTICE,
815 (errmsg("server \"%s\" does not exist, skipping",
816 stmt->servername)));
817 return;
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,
823 stmt->servername);
825 object.classId = ForeignServerRelationId;
826 object.objectId = srvId;
827 object.objectSubId = 0;
829 performDeletion(&object, stmt->behavior);
834 * Drop foreign server by OID
836 void
837 RemoveForeignServerById(Oid srvId)
839 HeapTuple tp;
840 Relation rel;
842 rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
844 tp = SearchSysCache(FOREIGNSERVEROID,
845 ObjectIdGetDatum(srvId),
846 0, 0, 0);
848 if (!HeapTupleIsValid(tp))
849 elog(ERROR, "cache lookup failed for foreign server %u", srvId);
851 simple_heap_delete(rel, &tp->t_self);
853 ReleaseSysCache(tp);
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.
864 static void
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)
873 AclResult aclresult;
875 aclresult = pg_foreign_server_aclcheck(serverid, curuserid, ACL_USAGE);
876 if (aclresult != ACLCHECK_OK)
877 aclcheck_error(aclresult, ACL_KIND_FOREIGN_SERVER, servername);
879 else
880 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
881 servername);
887 * Create user mapping
889 void
890 CreateUserMapping(CreateUserMappingStmt *stmt)
892 Relation rel;
893 Datum useoptions;
894 Datum values[Natts_pg_user_mapping];
895 bool nulls[Natts_pg_user_mapping];
896 HeapTuple tuple;
897 Oid useId;
898 Oid umId;
899 ObjectAddress myself;
900 ObjectAddress referenced;
901 ForeignServer *srv;
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),
917 0, 0);
918 if (OidIsValid(umId))
919 ereport(ERROR,
920 (errcode(ERRCODE_DUPLICATE_OBJECT),
921 errmsg("user mapping \"%s\" already exists for server %s",
922 MappingUserName(useId),
923 stmt->servername)));
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,
940 fdw->fdwvalidator);
942 if (PointerIsValid(DatumGetPointer(useoptions)))
943 values[Anum_pg_user_mapping_umoptions - 1] = useoptions;
944 else
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);
974 * Alter user mapping
976 void
977 AlterUserMapping(AlterUserMappingStmt *stmt)
979 Relation rel;
980 HeapTuple tp;
981 Datum repl_val[Natts_pg_user_mapping];
982 bool repl_null[Natts_pg_user_mapping];
983 bool repl_repl[Natts_pg_user_mapping];
984 Oid useId;
985 Oid umId;
986 ForeignServer *srv;
988 useId = GetUserOidFromMapping(stmt->username, false);
989 srv = GetForeignServerByName(stmt->servername, false);
991 umId = GetSysCacheOid(USERMAPPINGUSERSERVER,
992 ObjectIdGetDatum(useId),
993 ObjectIdGetDatum(srv->serverid),
994 0, 0);
995 if (!OidIsValid(umId))
996 ereport(ERROR,
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),
1005 0, 0, 0);
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));
1014 if (stmt->options)
1016 ForeignDataWrapper *fdw;
1017 Datum datum;
1018 bool isnull;
1021 * Process the options.
1024 fdw = GetForeignDataWrapper(srv->fdwid);
1026 datum = SysCacheGetAttr(USERMAPPINGUSERSERVER,
1028 Anum_pg_user_mapping_umoptions,
1029 &isnull);
1030 if (isnull)
1031 datum = PointerGetDatum(NULL);
1033 /* Prepare the options array */
1034 datum = transformGenericOptions(datum, stmt->options,
1035 fdw->fdwvalidator);
1037 if (PointerIsValid(DatumGetPointer(datum)))
1038 repl_val[Anum_pg_user_mapping_umoptions - 1] = datum;
1039 else
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);
1056 heap_freetuple(tp);
1061 * Drop user mapping
1063 void
1064 RemoveUserMapping(DropUserMappingStmt *stmt)
1066 ObjectAddress object;
1067 Oid useId;
1068 Oid umId;
1069 ForeignServer *srv;
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
1078 * leave.
1080 elog(NOTICE, "role \"%s\" does not exist, skipping", stmt->username);
1081 return;
1084 if (!srv)
1086 if (!stmt->missing_ok)
1087 ereport(ERROR,
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")));
1093 return;
1096 umId = GetSysCacheOid(USERMAPPINGUSERSERVER,
1097 ObjectIdGetDatum(useId),
1098 ObjectIdGetDatum(srv->serverid),
1099 0, 0);
1101 if (!OidIsValid(umId))
1103 if (!stmt->missing_ok)
1104 ereport(ERROR,
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 */
1110 ereport(NOTICE,
1111 (errmsg("user mapping \"%s\" does not exist for the server, skipping",
1112 MappingUserName(useId))));
1113 return;
1116 user_mapping_ddl_aclcheck(useId, srv->serverid, srv->servername);
1119 * Do the deletion
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.
1132 void
1133 RemoveUserMappingById(Oid umId)
1135 HeapTuple tp;
1136 Relation rel;
1138 rel = heap_open(UserMappingRelationId, RowExclusiveLock);
1140 tp = SearchSysCache(USERMAPPINGOID,
1141 ObjectIdGetDatum(umId),
1142 0, 0, 0);
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);