1 /*-------------------------------------------------------------------------
4 * foreign-data wrapper/server creation/manipulation commands
6 * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
10 * src/backend/commands/foreigncmds.c
12 *-------------------------------------------------------------------------
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"
46 } import_error_callback_arg;
48 /* Internal functions */
49 static void import_error_callback(void *arg);
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
57 * Returns the array in the form of a Datum, or PointerGetDatum(NULL)
58 * if the list is empty.
60 * Note: The array is usually stored to database without further
61 * processing, hence any validation should be done before this
65 optionListToArray(List *options)
67 ArrayBuildState *astate = NULL;
70 foreach(cell, options)
72 DefElem *def = lfirst(cell);
77 value = defGetString(def);
78 len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
81 sprintf(VARDATA(t), "%s=%s", def->defname, value);
83 astate = accumArrayResult(astate, PointerGetDatum(t),
85 CurrentMemoryContext);
89 return makeArrayResult(astate, CurrentMemoryContext);
91 return PointerGetDatum(NULL);
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.
102 * Returns the array in the form of a Datum, or PointerGetDatum(NULL)
103 * if the list is empty.
105 * This is used by CREATE/ALTER of FOREIGN DATA WRAPPER/SERVER/USER MAPPING/
109 transformGenericOptions(Oid catalogId,
114 List *resultOptions = untransformRelOptions(oldOptions);
118 foreach(optcell, options)
120 DefElem *od = lfirst(optcell);
122 ListCell *prev = NULL;
125 * Find the element in resultOptions. We need this for validation in
126 * all cases. Also identify the previous element.
128 foreach(cell, resultOptions)
130 DefElem *def = lfirst(cell);
132 if (strcmp(def->defname, od->defname) == 0)
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
144 switch (od->defaction)
149 (errcode(ERRCODE_UNDEFINED_OBJECT),
150 errmsg("option \"%s\" not found",
152 resultOptions = list_delete_cell(resultOptions, cell, prev);
158 (errcode(ERRCODE_UNDEFINED_OBJECT),
159 errmsg("option \"%s\" not found",
168 (errcode(ERRCODE_DUPLICATE_OBJECT),
169 errmsg("option \"%s\" provided more than once",
171 resultOptions = lappend(resultOptions, od);
175 elog(ERROR, "unrecognized action %d on option \"%s\"",
176 (int) od->defaction, od->defname);
181 result = optionListToArray(resultOptions);
183 if (OidIsValid(fdwvalidator))
185 Datum valarg = result;
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.
191 if (DatumGetPointer(valarg) == NULL)
192 valarg = PointerGetDatum(construct_empty_array(TEXTOID));
193 OidFunctionCall2(fdwvalidator, valarg, ObjectIdGetDatum(catalogId));
201 * Internal workhorse for changing a data wrapper's owner.
203 * Allow this only for superusers; also the new owner must be a
207 AlterForeignDataWrapperOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
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];
217 form = (Form_pg_foreign_data_wrapper) GETSTRUCT(tup);
219 /* Must be a superuser to change a FDW owner */
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.")));
227 /* New owner must also be a superuser */
228 if (!superuser_arg(newOwnerId))
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.")));
235 if (form->fdwowner != newOwnerId)
237 memset(repl_null, false, sizeof(repl_null));
238 memset(repl_repl, false, sizeof(repl_repl));
240 repl_repl[Anum_pg_foreign_data_wrapper_fdwowner - 1] = true;
241 repl_val[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(newOwnerId);
243 aclDatum = heap_getattr(tup,
244 Anum_pg_foreign_data_wrapper_fdwacl,
245 RelationGetDescr(rel),
247 /* Null ACLs do not require changes */
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);
256 tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null,
259 simple_heap_update(rel, &tup->t_self, tup);
260 CatalogUpdateIndexes(rel, tup);
262 /* Update owner dependency reference */
263 changeDependencyOnOwner(ForeignDataWrapperRelationId,
264 HeapTupleGetOid(tup),
268 InvokeObjectPostAlterHook(ForeignDataWrapperRelationId,
269 HeapTupleGetOid(tup), 0);
273 * Change foreign-data wrapper owner -- by name
275 * Note restrictions in the "_internal" function, above.
278 AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId)
283 ObjectAddress address;
285 rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
287 tup = SearchSysCacheCopy1(FOREIGNDATAWRAPPERNAME, CStringGetDatum(name));
289 if (!HeapTupleIsValid(tup))
291 (errcode(ERRCODE_UNDEFINED_OBJECT),
292 errmsg("foreign-data wrapper \"%s\" does not exist", name)));
294 fdwId = HeapTupleGetOid(tup);
296 AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId);
298 ObjectAddressSet(address, ForeignDataWrapperRelationId, fdwId);
302 heap_close(rel, RowExclusiveLock);
308 * Change foreign-data wrapper owner -- by OID
310 * Note restrictions in the "_internal" function, above.
313 AlterForeignDataWrapperOwner_oid(Oid fwdId, Oid newOwnerId)
318 rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
320 tup = SearchSysCacheCopy1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fwdId));
322 if (!HeapTupleIsValid(tup))
324 (errcode(ERRCODE_UNDEFINED_OBJECT),
325 errmsg("foreign-data wrapper with OID %u does not exist", fwdId)));
327 AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId);
331 heap_close(rel, RowExclusiveLock);
335 * Internal workhorse for changing a foreign server's owner
338 AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
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];
348 form = (Form_pg_foreign_server) GETSTRUCT(tup);
350 if (form->srvowner != newOwnerId)
352 /* Superusers can always do it */
358 srvId = HeapTupleGetOid(tup);
361 if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
362 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
363 NameStr(form->srvname));
365 /* Must be able to become new owner */
366 check_is_member_of_role(GetUserId(), newOwnerId);
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)
372 ForeignDataWrapper *fdw = GetForeignDataWrapper(form->srvfdw);
374 aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname);
378 memset(repl_null, false, sizeof(repl_null));
379 memset(repl_repl, false, sizeof(repl_repl));
381 repl_repl[Anum_pg_foreign_server_srvowner - 1] = true;
382 repl_val[Anum_pg_foreign_server_srvowner - 1] = ObjectIdGetDatum(newOwnerId);
384 aclDatum = heap_getattr(tup,
385 Anum_pg_foreign_server_srvacl,
386 RelationGetDescr(rel),
388 /* Null ACLs do not require changes */
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);
397 tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null,
400 simple_heap_update(rel, &tup->t_self, tup);
401 CatalogUpdateIndexes(rel, tup);
403 /* Update owner dependency reference */
404 changeDependencyOnOwner(ForeignServerRelationId, HeapTupleGetOid(tup),
408 InvokeObjectPostAlterHook(ForeignServerRelationId,
409 HeapTupleGetOid(tup), 0);
413 * Change foreign server owner -- by name
416 AlterForeignServerOwner(const char *name, Oid newOwnerId)
421 ObjectAddress address;
423 rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
425 tup = SearchSysCacheCopy1(FOREIGNSERVERNAME, CStringGetDatum(name));
427 if (!HeapTupleIsValid(tup))
429 (errcode(ERRCODE_UNDEFINED_OBJECT),
430 errmsg("server \"%s\" does not exist", name)));
432 servOid = HeapTupleGetOid(tup);
434 AlterForeignServerOwner_internal(rel, tup, newOwnerId);
436 ObjectAddressSet(address, ForeignServerRelationId, servOid);
440 heap_close(rel, RowExclusiveLock);
446 * Change foreign server owner -- by OID
449 AlterForeignServerOwner_oid(Oid srvId, Oid newOwnerId)
454 rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
456 tup = SearchSysCacheCopy1(FOREIGNSERVEROID, ObjectIdGetDatum(srvId));
458 if (!HeapTupleIsValid(tup))
460 (errcode(ERRCODE_UNDEFINED_OBJECT),
461 errmsg("foreign server with OID %u does not exist", srvId)));
463 AlterForeignServerOwner_internal(rel, tup, newOwnerId);
467 heap_close(rel, RowExclusiveLock);
471 * Convert a handler function name passed from the parser to an Oid.
474 lookup_fdw_handler_func(DefElem *handler)
477 Oid funcargtypes[1]; /* dummy */
479 if (handler == NULL || handler->arg == NULL)
482 /* handlers have no arguments */
483 handlerOid = LookupFuncName((List *) handler->arg, 0, funcargtypes, false);
485 /* check that handler has correct return type */
486 if (get_func_rettype(handlerOid) != FDW_HANDLEROID)
488 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
489 errmsg("function %s must return type \"%s\"",
490 NameListToString((List *) handler->arg), "fdw_handler")));
496 * Convert a validator function name passed from the parser to an Oid.
499 lookup_fdw_validator_func(DefElem *validator)
503 if (validator == NULL || validator->arg == NULL)
506 /* validators take text[], oid */
507 funcargtypes[0] = TEXTARRAYOID;
508 funcargtypes[1] = OIDOID;
510 return LookupFuncName((List *) validator->arg, 2, funcargtypes, false);
511 /* validator's return value is ignored, so we don't check the type */
515 * Process function options of CREATE/ALTER FDW
518 parse_func_options(List *func_options,
519 bool *handler_given, Oid *fdwhandler,
520 bool *validator_given, Oid *fdwvalidator)
524 *handler_given = false;
525 *validator_given = false;
526 /* return InvalidOid if not given */
527 *fdwhandler = InvalidOid;
528 *fdwvalidator = InvalidOid;
530 foreach(cell, func_options)
532 DefElem *def = (DefElem *) lfirst(cell);
534 if (strcmp(def->defname, "handler") == 0)
538 (errcode(ERRCODE_SYNTAX_ERROR),
539 errmsg("conflicting or redundant options")));
540 *handler_given = true;
541 *fdwhandler = lookup_fdw_handler_func(def);
543 else if (strcmp(def->defname, "validator") == 0)
545 if (*validator_given)
547 (errcode(ERRCODE_SYNTAX_ERROR),
548 errmsg("conflicting or redundant options")));
549 *validator_given = true;
550 *fdwvalidator = lookup_fdw_validator_func(def);
553 elog(ERROR, "option \"%s\" not recognized",
559 * Create a foreign-data wrapper
562 CreateForeignDataWrapper(CreateFdwStmt *stmt)
565 Datum values[Natts_pg_foreign_data_wrapper];
566 bool nulls[Natts_pg_foreign_data_wrapper];
570 bool validator_given;
575 ObjectAddress myself;
576 ObjectAddress referenced;
578 rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
580 /* Must be super user */
583 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
584 errmsg("permission denied to create foreign-data wrapper \"%s\"",
586 errhint("Must be superuser to create a foreign-data wrapper.")));
588 /* For now the owner cannot be specified on create. Use effective user ID. */
589 ownerId = GetUserId();
592 * Check that there is no other foreign-data wrapper by this name.
594 if (GetForeignDataWrapperByName(stmt->fdwname, true) != NULL)
596 (errcode(ERRCODE_DUPLICATE_OBJECT),
597 errmsg("foreign-data wrapper \"%s\" already exists",
601 * Insert tuple into pg_foreign_data_wrapper.
603 memset(values, 0, sizeof(values));
604 memset(nulls, false, sizeof(nulls));
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);
610 /* Lookup handler and validator functions, if given */
611 parse_func_options(stmt->func_options,
612 &handler_given, &fdwhandler,
613 &validator_given, &fdwvalidator);
615 values[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fdwhandler);
616 values[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fdwvalidator);
618 nulls[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
620 fdwoptions = transformGenericOptions(ForeignDataWrapperRelationId,
621 PointerGetDatum(NULL),
625 if (PointerIsValid(DatumGetPointer(fdwoptions)))
626 values[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = fdwoptions;
628 nulls[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
630 tuple = heap_form_tuple(rel->rd_att, values, nulls);
632 fdwId = simple_heap_insert(rel, tuple);
633 CatalogUpdateIndexes(rel, tuple);
635 heap_freetuple(tuple);
637 /* record dependencies */
638 myself.classId = ForeignDataWrapperRelationId;
639 myself.objectId = fdwId;
640 myself.objectSubId = 0;
642 if (OidIsValid(fdwhandler))
644 referenced.classId = ProcedureRelationId;
645 referenced.objectId = fdwhandler;
646 referenced.objectSubId = 0;
647 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
650 if (OidIsValid(fdwvalidator))
652 referenced.classId = ProcedureRelationId;
653 referenced.objectId = fdwvalidator;
654 referenced.objectSubId = 0;
655 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
658 recordDependencyOnOwner(ForeignDataWrapperRelationId, fdwId, ownerId);
660 /* dependency on extension */
661 recordDependencyOnCurrentExtension(&myself, false);
663 /* Post creation hook for new foreign data wrapper */
664 InvokeObjectPostCreateHook(ForeignDataWrapperRelationId, fdwId, 0);
666 heap_close(rel, RowExclusiveLock);
673 * Alter foreign-data wrapper
676 AlterForeignDataWrapper(AlterFdwStmt *stmt)
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];
688 bool validator_given;
691 ObjectAddress myself;
693 rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
695 /* Must be super user */
698 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
699 errmsg("permission denied to alter foreign-data wrapper \"%s\"",
701 errhint("Must be superuser to alter a foreign-data wrapper.")));
703 tp = SearchSysCacheCopy1(FOREIGNDATAWRAPPERNAME,
704 CStringGetDatum(stmt->fdwname));
706 if (!HeapTupleIsValid(tp))
708 (errcode(ERRCODE_UNDEFINED_OBJECT),
709 errmsg("foreign-data wrapper \"%s\" does not exist", stmt->fdwname)));
711 fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
712 fdwId = HeapTupleGetOid(tp);
714 memset(repl_val, 0, sizeof(repl_val));
715 memset(repl_null, false, sizeof(repl_null));
716 memset(repl_repl, false, sizeof(repl_repl));
718 parse_func_options(stmt->func_options,
719 &handler_given, &fdwhandler,
720 &validator_given, &fdwvalidator);
724 repl_val[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fdwhandler);
725 repl_repl[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = true;
728 * It could be that the behavior of accessing foreign table changes
729 * with the new handler. Warn about this.
732 (errmsg("changing the foreign-data wrapper handler can change behavior of existing foreign tables")));
737 repl_val[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fdwvalidator);
738 repl_repl[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = true;
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.
745 if (OidIsValid(fdwvalidator))
747 (errmsg("changing the foreign-data wrapper validator can cause "
748 "the options for dependent objects to become invalid")));
753 * Validator is not changed, but we need it for validating options.
755 fdwvalidator = fdwForm->fdwvalidator;
759 * If options specified, validate and update.
763 /* Extract the current options */
764 datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
766 Anum_pg_foreign_data_wrapper_fdwoptions,
769 datum = PointerGetDatum(NULL);
771 /* Transform the options */
772 datum = transformGenericOptions(ForeignDataWrapperRelationId,
777 if (PointerIsValid(DatumGetPointer(datum)))
778 repl_val[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = datum;
780 repl_null[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
782 repl_repl[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
785 /* Everything looks good - update the tuple */
786 tp = heap_modify_tuple(tp, RelationGetDescr(rel),
787 repl_val, repl_null, repl_repl);
789 simple_heap_update(rel, &tp->t_self, tp);
790 CatalogUpdateIndexes(rel, tp);
794 ObjectAddressSet(myself, ForeignDataWrapperRelationId, fdwId);
796 /* Update function dependencies if we changed them */
797 if (handler_given || validator_given)
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. */
812 if (OidIsValid(fdwhandler))
814 referenced.classId = ProcedureRelationId;
815 referenced.objectId = fdwhandler;
816 referenced.objectSubId = 0;
817 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
820 if (OidIsValid(fdwvalidator))
822 referenced.classId = ProcedureRelationId;
823 referenced.objectId = fdwvalidator;
824 referenced.objectSubId = 0;
825 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
829 InvokeObjectPostAlterHook(ForeignDataWrapperRelationId, fdwId, 0);
831 heap_close(rel, RowExclusiveLock);
838 * Drop foreign-data wrapper by OID
841 RemoveForeignDataWrapperById(Oid fdwId)
846 rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
848 tp = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwId));
850 if (!HeapTupleIsValid(tp))
851 elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwId);
853 simple_heap_delete(rel, &tp->t_self);
857 heap_close(rel, RowExclusiveLock);
862 * Create a foreign server
865 CreateForeignServer(CreateForeignServerStmt *stmt)
869 Datum values[Natts_pg_foreign_server];
870 bool nulls[Natts_pg_foreign_server];
875 ObjectAddress myself;
876 ObjectAddress referenced;
877 ForeignDataWrapper *fdw;
879 rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
881 /* For now the owner cannot be specified on create. Use effective user ID. */
882 ownerId = GetUserId();
885 * Check that there is no other foreign server by this name.
887 if (GetForeignServerByName(stmt->servername, true) != NULL)
889 (errcode(ERRCODE_DUPLICATE_OBJECT),
890 errmsg("server \"%s\" already exists",
894 * Check that the FDW exists and that we have USAGE on it. Also get the
895 * actual FDW for option validation etc.
897 fdw = GetForeignDataWrapperByName(stmt->fdwname, false);
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);
904 * Insert tuple into pg_foreign_server.
906 memset(values, 0, sizeof(values));
907 memset(nulls, false, sizeof(nulls));
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);
914 /* Add server type if supplied */
915 if (stmt->servertype)
916 values[Anum_pg_foreign_server_srvtype - 1] =
917 CStringGetTextDatum(stmt->servertype);
919 nulls[Anum_pg_foreign_server_srvtype - 1] = true;
921 /* Add server version if supplied */
923 values[Anum_pg_foreign_server_srvversion - 1] =
924 CStringGetTextDatum(stmt->version);
926 nulls[Anum_pg_foreign_server_srvversion - 1] = true;
928 /* Start with a blank acl */
929 nulls[Anum_pg_foreign_server_srvacl - 1] = true;
931 /* Add server options */
932 srvoptions = transformGenericOptions(ForeignServerRelationId,
933 PointerGetDatum(NULL),
937 if (PointerIsValid(DatumGetPointer(srvoptions)))
938 values[Anum_pg_foreign_server_srvoptions - 1] = srvoptions;
940 nulls[Anum_pg_foreign_server_srvoptions - 1] = true;
942 tuple = heap_form_tuple(rel->rd_att, values, nulls);
944 srvId = simple_heap_insert(rel, tuple);
946 CatalogUpdateIndexes(rel, tuple);
948 heap_freetuple(tuple);
950 /* record dependencies */
951 myself.classId = ForeignServerRelationId;
952 myself.objectId = srvId;
953 myself.objectSubId = 0;
955 referenced.classId = ForeignDataWrapperRelationId;
956 referenced.objectId = fdw->fdwid;
957 referenced.objectSubId = 0;
958 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
960 recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId);
962 /* dependency on extension */
963 recordDependencyOnCurrentExtension(&myself, false);
965 /* Post creation hook for new foreign server */
966 InvokeObjectPostCreateHook(ForeignServerRelationId, srvId, 0);
968 heap_close(rel, RowExclusiveLock);
975 * Alter foreign server
978 AlterForeignServer(AlterForeignServerStmt *stmt)
982 Datum repl_val[Natts_pg_foreign_server];
983 bool repl_null[Natts_pg_foreign_server];
984 bool repl_repl[Natts_pg_foreign_server];
986 Form_pg_foreign_server srvForm;
987 ObjectAddress address;
989 rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
991 tp = SearchSysCacheCopy1(FOREIGNSERVERNAME,
992 CStringGetDatum(stmt->servername));
994 if (!HeapTupleIsValid(tp))
996 (errcode(ERRCODE_UNDEFINED_OBJECT),
997 errmsg("server \"%s\" does not exist", stmt->servername)));
999 srvId = HeapTupleGetOid(tp);
1000 srvForm = (Form_pg_foreign_server) GETSTRUCT(tp);
1003 * Only owner or a superuser can ALTER a SERVER.
1005 if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
1006 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
1009 memset(repl_val, 0, sizeof(repl_val));
1010 memset(repl_null, false, sizeof(repl_null));
1011 memset(repl_repl, false, sizeof(repl_repl));
1013 if (stmt->has_version)
1016 * Change the server VERSION string.
1019 repl_val[Anum_pg_foreign_server_srvversion - 1] =
1020 CStringGetTextDatum(stmt->version);
1022 repl_null[Anum_pg_foreign_server_srvversion - 1] = true;
1024 repl_repl[Anum_pg_foreign_server_srvversion - 1] = true;
1029 ForeignDataWrapper *fdw = GetForeignDataWrapper(srvForm->srvfdw);
1033 /* Extract the current srvoptions */
1034 datum = SysCacheGetAttr(FOREIGNSERVEROID,
1036 Anum_pg_foreign_server_srvoptions,
1039 datum = PointerGetDatum(NULL);
1041 /* Prepare the options array */
1042 datum = transformGenericOptions(ForeignServerRelationId,
1047 if (PointerIsValid(DatumGetPointer(datum)))
1048 repl_val[Anum_pg_foreign_server_srvoptions - 1] = datum;
1050 repl_null[Anum_pg_foreign_server_srvoptions - 1] = true;
1052 repl_repl[Anum_pg_foreign_server_srvoptions - 1] = true;
1055 /* Everything looks good - update the tuple */
1056 tp = heap_modify_tuple(tp, RelationGetDescr(rel),
1057 repl_val, repl_null, repl_repl);
1059 simple_heap_update(rel, &tp->t_self, tp);
1060 CatalogUpdateIndexes(rel, tp);
1062 InvokeObjectPostAlterHook(ForeignServerRelationId, srvId, 0);
1064 ObjectAddressSet(address, ForeignServerRelationId, srvId);
1068 heap_close(rel, RowExclusiveLock);
1075 * Drop foreign server by OID
1078 RemoveForeignServerById(Oid srvId)
1083 rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
1085 tp = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(srvId));
1087 if (!HeapTupleIsValid(tp))
1088 elog(ERROR, "cache lookup failed for foreign server %u", srvId);
1090 simple_heap_delete(rel, &tp->t_self);
1092 ReleaseSysCache(tp);
1094 heap_close(rel, RowExclusiveLock);
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.
1104 user_mapping_ddl_aclcheck(Oid umuserid, Oid serverid, const char *servername)
1106 Oid curuserid = GetUserId();
1108 if (!pg_foreign_server_ownercheck(serverid, curuserid))
1110 if (umuserid == curuserid)
1112 AclResult aclresult;
1114 aclresult = pg_foreign_server_aclcheck(serverid, curuserid, ACL_USAGE);
1115 if (aclresult != ACLCHECK_OK)
1116 aclcheck_error(aclresult, ACL_KIND_FOREIGN_SERVER, servername);
1119 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
1126 * Create user mapping
1129 CreateUserMapping(CreateUserMappingStmt *stmt)
1133 Datum values[Natts_pg_user_mapping];
1134 bool nulls[Natts_pg_user_mapping];
1138 ObjectAddress myself;
1139 ObjectAddress referenced;
1141 ForeignDataWrapper *fdw;
1142 RoleSpec *role = (RoleSpec *) stmt->user;
1144 rel = heap_open(UserMappingRelationId, RowExclusiveLock);
1146 if (role->roletype == ROLESPEC_PUBLIC)
1147 useId = ACL_ID_PUBLIC;
1149 useId = get_rolespec_oid(stmt->user, false);
1151 /* Check that the server exists. */
1152 srv = GetForeignServerByName(stmt->servername, false);
1154 user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
1157 * Check that the user mapping is unique within server.
1159 umId = GetSysCacheOid2(USERMAPPINGUSERSERVER,
1160 ObjectIdGetDatum(useId),
1161 ObjectIdGetDatum(srv->serverid));
1162 if (OidIsValid(umId))
1164 (errcode(ERRCODE_DUPLICATE_OBJECT),
1165 errmsg("user mapping \"%s\" already exists for server %s",
1166 MappingUserName(useId),
1167 stmt->servername)));
1169 fdw = GetForeignDataWrapper(srv->fdwid);
1172 * Insert tuple into pg_user_mapping.
1174 memset(values, 0, sizeof(values));
1175 memset(nulls, false, sizeof(nulls));
1177 values[Anum_pg_user_mapping_umuser - 1] = ObjectIdGetDatum(useId);
1178 values[Anum_pg_user_mapping_umserver - 1] = ObjectIdGetDatum(srv->serverid);
1180 /* Add user options */
1181 useoptions = transformGenericOptions(UserMappingRelationId,
1182 PointerGetDatum(NULL),
1186 if (PointerIsValid(DatumGetPointer(useoptions)))
1187 values[Anum_pg_user_mapping_umoptions - 1] = useoptions;
1189 nulls[Anum_pg_user_mapping_umoptions - 1] = true;
1191 tuple = heap_form_tuple(rel->rd_att, values, nulls);
1193 umId = simple_heap_insert(rel, tuple);
1195 CatalogUpdateIndexes(rel, tuple);
1197 heap_freetuple(tuple);
1199 /* Add dependency on the server */
1200 myself.classId = UserMappingRelationId;
1201 myself.objectId = umId;
1202 myself.objectSubId = 0;
1204 referenced.classId = ForeignServerRelationId;
1205 referenced.objectId = srv->serverid;
1206 referenced.objectSubId = 0;
1207 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1209 if (OidIsValid(useId))
1211 /* Record the mapped user dependency */
1212 recordDependencyOnOwner(UserMappingRelationId, umId, useId);
1215 /* dependency on extension */
1216 recordDependencyOnCurrentExtension(&myself, false);
1218 /* Post creation hook for new user mapping */
1219 InvokeObjectPostCreateHook(UserMappingRelationId, umId, 0);
1221 heap_close(rel, RowExclusiveLock);
1228 * Alter user mapping
1231 AlterUserMapping(AlterUserMappingStmt *stmt)
1235 Datum repl_val[Natts_pg_user_mapping];
1236 bool repl_null[Natts_pg_user_mapping];
1237 bool repl_repl[Natts_pg_user_mapping];
1241 ObjectAddress address;
1242 RoleSpec *role = (RoleSpec *) stmt->user;
1244 rel = heap_open(UserMappingRelationId, RowExclusiveLock);
1246 if (role->roletype == ROLESPEC_PUBLIC)
1247 useId = ACL_ID_PUBLIC;
1249 useId = get_rolespec_oid(stmt->user, false);
1251 srv = GetForeignServerByName(stmt->servername, false);
1253 umId = GetSysCacheOid2(USERMAPPINGUSERSERVER,
1254 ObjectIdGetDatum(useId),
1255 ObjectIdGetDatum(srv->serverid));
1256 if (!OidIsValid(umId))
1258 (errcode(ERRCODE_UNDEFINED_OBJECT),
1259 errmsg("user mapping \"%s\" does not exist for the server",
1260 MappingUserName(useId))));
1262 user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
1264 tp = SearchSysCacheCopy1(USERMAPPINGOID, ObjectIdGetDatum(umId));
1266 if (!HeapTupleIsValid(tp))
1267 elog(ERROR, "cache lookup failed for user mapping %u", umId);
1269 memset(repl_val, 0, sizeof(repl_val));
1270 memset(repl_null, false, sizeof(repl_null));
1271 memset(repl_repl, false, sizeof(repl_repl));
1275 ForeignDataWrapper *fdw;
1280 * Process the options.
1283 fdw = GetForeignDataWrapper(srv->fdwid);
1285 datum = SysCacheGetAttr(USERMAPPINGUSERSERVER,
1287 Anum_pg_user_mapping_umoptions,
1290 datum = PointerGetDatum(NULL);
1292 /* Prepare the options array */
1293 datum = transformGenericOptions(UserMappingRelationId,
1298 if (PointerIsValid(DatumGetPointer(datum)))
1299 repl_val[Anum_pg_user_mapping_umoptions - 1] = datum;
1301 repl_null[Anum_pg_user_mapping_umoptions - 1] = true;
1303 repl_repl[Anum_pg_user_mapping_umoptions - 1] = true;
1306 /* Everything looks good - update the tuple */
1307 tp = heap_modify_tuple(tp, RelationGetDescr(rel),
1308 repl_val, repl_null, repl_repl);
1310 simple_heap_update(rel, &tp->t_self, tp);
1311 CatalogUpdateIndexes(rel, tp);
1313 ObjectAddressSet(address, UserMappingRelationId, umId);
1317 heap_close(rel, RowExclusiveLock);
1327 RemoveUserMapping(DropUserMappingStmt *stmt)
1329 ObjectAddress object;
1333 RoleSpec *role = (RoleSpec *) stmt->user;
1335 if (role->roletype == ROLESPEC_PUBLIC)
1336 useId = ACL_ID_PUBLIC;
1339 useId = get_rolespec_oid(stmt->user, stmt->missing_ok);
1340 if (!OidIsValid(useId))
1343 * IF EXISTS specified, role not found and not public. Notice this
1346 elog(NOTICE, "role \"%s\" does not exist, skipping",
1352 srv = GetForeignServerByName(stmt->servername, true);
1356 if (!stmt->missing_ok)
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")));
1366 umId = GetSysCacheOid2(USERMAPPINGUSERSERVER,
1367 ObjectIdGetDatum(useId),
1368 ObjectIdGetDatum(srv->serverid));
1370 if (!OidIsValid(umId))
1372 if (!stmt->missing_ok)
1374 (errcode(ERRCODE_UNDEFINED_OBJECT),
1375 errmsg("user mapping \"%s\" does not exist for the server",
1376 MappingUserName(useId))));
1378 /* IF EXISTS specified, just note it */
1380 (errmsg("user mapping \"%s\" does not exist for the server, skipping",
1381 MappingUserName(useId))));
1385 user_mapping_ddl_aclcheck(useId, srv->serverid, srv->servername);
1390 object.classId = UserMappingRelationId;
1391 object.objectId = umId;
1392 object.objectSubId = 0;
1394 performDeletion(&object, DROP_CASCADE, 0);
1401 * Drop user mapping by OID. This is called to clean up dependencies.
1404 RemoveUserMappingById(Oid umId)
1409 rel = heap_open(UserMappingRelationId, RowExclusiveLock);
1411 tp = SearchSysCache1(USERMAPPINGOID, ObjectIdGetDatum(umId));
1413 if (!HeapTupleIsValid(tp))
1414 elog(ERROR, "cache lookup failed for user mapping %u", umId);
1416 simple_heap_delete(rel, &tp->t_self);
1418 ReleaseSysCache(tp);
1420 heap_close(rel, RowExclusiveLock);
1424 * Create a foreign table
1425 * call after DefineRelation().
1428 CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
1432 Datum values[Natts_pg_foreign_table];
1433 bool nulls[Natts_pg_foreign_table];
1435 AclResult aclresult;
1436 ObjectAddress myself;
1437 ObjectAddress referenced;
1439 ForeignDataWrapper *fdw;
1440 ForeignServer *server;
1443 * Advance command counter to ensure the pg_attribute tuple is visible;
1444 * the tuple might be updated to add constraints in previous step.
1446 CommandCounterIncrement();
1448 ftrel = heap_open(ForeignTableRelationId, RowExclusiveLock);
1451 * For now the owner cannot be specified on create. Use effective user ID.
1453 ownerId = GetUserId();
1456 * Check that the foreign server exists and that we have USAGE on it. Also
1457 * get the actual FDW for option validation etc.
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);
1464 fdw = GetForeignDataWrapper(server->fdwid);
1467 * Insert tuple into pg_foreign_table.
1469 memset(values, 0, sizeof(values));
1470 memset(nulls, false, sizeof(nulls));
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),
1480 if (PointerIsValid(DatumGetPointer(ftoptions)))
1481 values[Anum_pg_foreign_table_ftoptions - 1] = ftoptions;
1483 nulls[Anum_pg_foreign_table_ftoptions - 1] = true;
1485 tuple = heap_form_tuple(ftrel->rd_att, values, nulls);
1487 simple_heap_insert(ftrel, tuple);
1488 CatalogUpdateIndexes(ftrel, tuple);
1490 heap_freetuple(tuple);
1492 /* Add pg_class dependency on the server */
1493 myself.classId = RelationRelationId;
1494 myself.objectId = relid;
1495 myself.objectSubId = 0;
1497 referenced.classId = ForeignServerRelationId;
1498 referenced.objectId = server->serverid;
1499 referenced.objectSubId = 0;
1500 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1502 heap_close(ftrel, RowExclusiveLock);
1506 * Import a foreign schema
1509 ImportForeignSchema(ImportForeignSchemaStmt *stmt)
1511 ForeignServer *server;
1512 ForeignDataWrapper *fdw;
1513 FdwRoutine *fdw_routine;
1514 AclResult aclresult;
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);
1524 /* Check that the schema exists and we have CREATE permissions on it */
1525 (void) LookupCreationNamespace(stmt->local_schema);
1527 /* Get the FDW and check it supports IMPORT */
1528 fdw = GetForeignDataWrapper(server->fdwid);
1529 if (!OidIsValid(fdw->fdwhandler))
1531 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1532 errmsg("foreign-data wrapper \"%s\" has no handler",
1534 fdw_routine = GetFdwRoutine(fdw->fdwhandler);
1535 if (fdw_routine->ImportForeignSchema == NULL)
1537 (errcode(ERRCODE_FDW_NO_SCHEMAS),
1538 errmsg("foreign-data wrapper \"%s\" does not support IMPORT FOREIGN SCHEMA",
1541 /* Call FDW to get a list of commands */
1542 cmd_list = fdw_routine->ImportForeignSchema(stmt, server->serverid);
1544 /* Parse and execute each command */
1545 foreach(lc, cmd_list)
1547 char *cmd = (char *) lfirst(lc);
1548 import_error_callback_arg callback_arg;
1549 ErrorContextCallback sqlerrcontext;
1550 List *raw_parsetree_list;
1554 * Setup error traceback support for ereport(). This is so that any
1555 * error in the generated SQL will be displayed nicely.
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;
1565 * Parse the SQL string into a list of raw parse trees.
1567 raw_parsetree_list = pg_parse_query(cmd);
1570 * Process each parse tree (we allow the FDW to put more than one
1571 * command per string, though this isn't really advised).
1573 foreach(lc2, raw_parsetree_list)
1575 CreateForeignTableStmt *cstmt = lfirst(lc2);
1578 * Because we only allow CreateForeignTableStmt, we can skip parse
1579 * analysis, rewrite, and planning steps here.
1581 if (!IsA(cstmt, CreateForeignTableStmt))
1583 "foreign-data wrapper \"%s\" returned incorrect statement type %d",
1584 fdw->fdwname, (int) nodeTag(cstmt));
1586 /* Ignore commands for tables excluded by filter options */
1587 if (!IsImportableForeignTable(cstmt->base.relation->relname, stmt))
1590 /* Enable reporting of current table's name on error */
1591 callback_arg.tablename = cstmt->base.relation->relname;
1593 /* Ensure creation schema is the one given in IMPORT statement */
1594 cstmt->base.relation->schemaname = pstrdup(stmt->local_schema);
1596 /* Execute statement */
1597 ProcessUtility((Node *) cstmt,
1599 PROCESS_UTILITY_SUBCOMMAND, NULL,
1600 None_Receiver, NULL);
1602 /* Be sure to advance the command counter between subcommands */
1603 CommandCounterIncrement();
1605 callback_arg.tablename = NULL;
1608 error_context_stack = sqlerrcontext.previous;
1613 * error context callback to let us supply the failing SQL statement's text
1616 import_error_callback(void *arg)
1618 import_error_callback_arg *callback_arg = (import_error_callback_arg *) arg;
1619 int syntaxerrposition;
1621 /* If it's a syntax error, convert to internal syntax error report */
1622 syntaxerrposition = geterrposition();
1623 if (syntaxerrposition > 0)
1626 internalerrposition(syntaxerrposition);
1627 internalerrquery(callback_arg->cmd);
1630 if (callback_arg->tablename)
1631 errcontext("importing foreign table \"%s\"",
1632 callback_arg->tablename);