1 /*-------------------------------------------------------------------------
4 * foreign-data wrapper/server creation/manipulation commands
6 * Portions Copyright (c) 1996-2017, 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 CatalogTupleUpdate(rel, &tup->t_self, tup);
261 /* Update owner dependency reference */
262 changeDependencyOnOwner(ForeignDataWrapperRelationId,
263 HeapTupleGetOid(tup),
267 InvokeObjectPostAlterHook(ForeignDataWrapperRelationId,
268 HeapTupleGetOid(tup), 0);
272 * Change foreign-data wrapper owner -- by name
274 * Note restrictions in the "_internal" function, above.
277 AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId)
282 ObjectAddress address;
284 rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
286 tup = SearchSysCacheCopy1(FOREIGNDATAWRAPPERNAME, CStringGetDatum(name));
288 if (!HeapTupleIsValid(tup))
290 (errcode(ERRCODE_UNDEFINED_OBJECT),
291 errmsg("foreign-data wrapper \"%s\" does not exist", name)));
293 fdwId = HeapTupleGetOid(tup);
295 AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId);
297 ObjectAddressSet(address, ForeignDataWrapperRelationId, fdwId);
301 heap_close(rel, RowExclusiveLock);
307 * Change foreign-data wrapper owner -- by OID
309 * Note restrictions in the "_internal" function, above.
312 AlterForeignDataWrapperOwner_oid(Oid fwdId, Oid newOwnerId)
317 rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
319 tup = SearchSysCacheCopy1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fwdId));
321 if (!HeapTupleIsValid(tup))
323 (errcode(ERRCODE_UNDEFINED_OBJECT),
324 errmsg("foreign-data wrapper with OID %u does not exist", fwdId)));
326 AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId);
330 heap_close(rel, RowExclusiveLock);
334 * Internal workhorse for changing a foreign server's owner
337 AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
339 Form_pg_foreign_server form;
340 Datum repl_val[Natts_pg_foreign_server];
341 bool repl_null[Natts_pg_foreign_server];
342 bool repl_repl[Natts_pg_foreign_server];
347 form = (Form_pg_foreign_server) GETSTRUCT(tup);
349 if (form->srvowner != newOwnerId)
351 /* Superusers can always do it */
357 srvId = HeapTupleGetOid(tup);
360 if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
361 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
362 NameStr(form->srvname));
364 /* Must be able to become new owner */
365 check_is_member_of_role(GetUserId(), newOwnerId);
367 /* New owner must have USAGE privilege on foreign-data wrapper */
368 aclresult = pg_foreign_data_wrapper_aclcheck(form->srvfdw, newOwnerId, ACL_USAGE);
369 if (aclresult != ACLCHECK_OK)
371 ForeignDataWrapper *fdw = GetForeignDataWrapper(form->srvfdw);
373 aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname);
377 memset(repl_null, false, sizeof(repl_null));
378 memset(repl_repl, false, sizeof(repl_repl));
380 repl_repl[Anum_pg_foreign_server_srvowner - 1] = true;
381 repl_val[Anum_pg_foreign_server_srvowner - 1] = ObjectIdGetDatum(newOwnerId);
383 aclDatum = heap_getattr(tup,
384 Anum_pg_foreign_server_srvacl,
385 RelationGetDescr(rel),
387 /* Null ACLs do not require changes */
390 newAcl = aclnewowner(DatumGetAclP(aclDatum),
391 form->srvowner, newOwnerId);
392 repl_repl[Anum_pg_foreign_server_srvacl - 1] = true;
393 repl_val[Anum_pg_foreign_server_srvacl - 1] = PointerGetDatum(newAcl);
396 tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null,
399 CatalogTupleUpdate(rel, &tup->t_self, tup);
401 /* Update owner dependency reference */
402 changeDependencyOnOwner(ForeignServerRelationId, HeapTupleGetOid(tup),
406 InvokeObjectPostAlterHook(ForeignServerRelationId,
407 HeapTupleGetOid(tup), 0);
411 * Change foreign server owner -- by name
414 AlterForeignServerOwner(const char *name, Oid newOwnerId)
419 ObjectAddress address;
421 rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
423 tup = SearchSysCacheCopy1(FOREIGNSERVERNAME, CStringGetDatum(name));
425 if (!HeapTupleIsValid(tup))
427 (errcode(ERRCODE_UNDEFINED_OBJECT),
428 errmsg("server \"%s\" does not exist", name)));
430 servOid = HeapTupleGetOid(tup);
432 AlterForeignServerOwner_internal(rel, tup, newOwnerId);
434 ObjectAddressSet(address, ForeignServerRelationId, servOid);
438 heap_close(rel, RowExclusiveLock);
444 * Change foreign server owner -- by OID
447 AlterForeignServerOwner_oid(Oid srvId, Oid newOwnerId)
452 rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
454 tup = SearchSysCacheCopy1(FOREIGNSERVEROID, ObjectIdGetDatum(srvId));
456 if (!HeapTupleIsValid(tup))
458 (errcode(ERRCODE_UNDEFINED_OBJECT),
459 errmsg("foreign server with OID %u does not exist", srvId)));
461 AlterForeignServerOwner_internal(rel, tup, newOwnerId);
465 heap_close(rel, RowExclusiveLock);
469 * Convert a handler function name passed from the parser to an Oid.
472 lookup_fdw_handler_func(DefElem *handler)
475 Oid funcargtypes[1]; /* dummy */
477 if (handler == NULL || handler->arg == NULL)
480 /* handlers have no arguments */
481 handlerOid = LookupFuncName((List *) handler->arg, 0, funcargtypes, false);
483 /* check that handler has correct return type */
484 if (get_func_rettype(handlerOid) != FDW_HANDLEROID)
486 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
487 errmsg("function %s must return type %s",
488 NameListToString((List *) handler->arg), "fdw_handler")));
494 * Convert a validator function name passed from the parser to an Oid.
497 lookup_fdw_validator_func(DefElem *validator)
501 if (validator == NULL || validator->arg == NULL)
504 /* validators take text[], oid */
505 funcargtypes[0] = TEXTARRAYOID;
506 funcargtypes[1] = OIDOID;
508 return LookupFuncName((List *) validator->arg, 2, funcargtypes, false);
509 /* validator's return value is ignored, so we don't check the type */
513 * Process function options of CREATE/ALTER FDW
516 parse_func_options(List *func_options,
517 bool *handler_given, Oid *fdwhandler,
518 bool *validator_given, Oid *fdwvalidator)
522 *handler_given = false;
523 *validator_given = false;
524 /* return InvalidOid if not given */
525 *fdwhandler = InvalidOid;
526 *fdwvalidator = InvalidOid;
528 foreach(cell, func_options)
530 DefElem *def = (DefElem *) lfirst(cell);
532 if (strcmp(def->defname, "handler") == 0)
536 (errcode(ERRCODE_SYNTAX_ERROR),
537 errmsg("conflicting or redundant options")));
538 *handler_given = true;
539 *fdwhandler = lookup_fdw_handler_func(def);
541 else if (strcmp(def->defname, "validator") == 0)
543 if (*validator_given)
545 (errcode(ERRCODE_SYNTAX_ERROR),
546 errmsg("conflicting or redundant options")));
547 *validator_given = true;
548 *fdwvalidator = lookup_fdw_validator_func(def);
551 elog(ERROR, "option \"%s\" not recognized",
557 * Create a foreign-data wrapper
560 CreateForeignDataWrapper(CreateFdwStmt *stmt)
563 Datum values[Natts_pg_foreign_data_wrapper];
564 bool nulls[Natts_pg_foreign_data_wrapper];
568 bool validator_given;
573 ObjectAddress myself;
574 ObjectAddress referenced;
576 rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
578 /* Must be super user */
581 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
582 errmsg("permission denied to create foreign-data wrapper \"%s\"",
584 errhint("Must be superuser to create a foreign-data wrapper.")));
586 /* For now the owner cannot be specified on create. Use effective user ID. */
587 ownerId = GetUserId();
590 * Check that there is no other foreign-data wrapper by this name.
592 if (GetForeignDataWrapperByName(stmt->fdwname, true) != NULL)
594 (errcode(ERRCODE_DUPLICATE_OBJECT),
595 errmsg("foreign-data wrapper \"%s\" already exists",
599 * Insert tuple into pg_foreign_data_wrapper.
601 memset(values, 0, sizeof(values));
602 memset(nulls, false, sizeof(nulls));
604 values[Anum_pg_foreign_data_wrapper_fdwname - 1] =
605 DirectFunctionCall1(namein, CStringGetDatum(stmt->fdwname));
606 values[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(ownerId);
608 /* Lookup handler and validator functions, if given */
609 parse_func_options(stmt->func_options,
610 &handler_given, &fdwhandler,
611 &validator_given, &fdwvalidator);
613 values[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fdwhandler);
614 values[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fdwvalidator);
616 nulls[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
618 fdwoptions = transformGenericOptions(ForeignDataWrapperRelationId,
619 PointerGetDatum(NULL),
623 if (PointerIsValid(DatumGetPointer(fdwoptions)))
624 values[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = fdwoptions;
626 nulls[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
628 tuple = heap_form_tuple(rel->rd_att, values, nulls);
630 fdwId = CatalogTupleInsert(rel, tuple);
632 heap_freetuple(tuple);
634 /* record dependencies */
635 myself.classId = ForeignDataWrapperRelationId;
636 myself.objectId = fdwId;
637 myself.objectSubId = 0;
639 if (OidIsValid(fdwhandler))
641 referenced.classId = ProcedureRelationId;
642 referenced.objectId = fdwhandler;
643 referenced.objectSubId = 0;
644 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
647 if (OidIsValid(fdwvalidator))
649 referenced.classId = ProcedureRelationId;
650 referenced.objectId = fdwvalidator;
651 referenced.objectSubId = 0;
652 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
655 recordDependencyOnOwner(ForeignDataWrapperRelationId, fdwId, ownerId);
657 /* dependency on extension */
658 recordDependencyOnCurrentExtension(&myself, false);
660 /* Post creation hook for new foreign data wrapper */
661 InvokeObjectPostCreateHook(ForeignDataWrapperRelationId, fdwId, 0);
663 heap_close(rel, RowExclusiveLock);
670 * Alter foreign-data wrapper
673 AlterForeignDataWrapper(AlterFdwStmt *stmt)
677 Form_pg_foreign_data_wrapper fdwForm;
678 Datum repl_val[Natts_pg_foreign_data_wrapper];
679 bool repl_null[Natts_pg_foreign_data_wrapper];
680 bool repl_repl[Natts_pg_foreign_data_wrapper];
685 bool validator_given;
688 ObjectAddress myself;
690 rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
692 /* Must be super user */
695 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
696 errmsg("permission denied to alter foreign-data wrapper \"%s\"",
698 errhint("Must be superuser to alter a foreign-data wrapper.")));
700 tp = SearchSysCacheCopy1(FOREIGNDATAWRAPPERNAME,
701 CStringGetDatum(stmt->fdwname));
703 if (!HeapTupleIsValid(tp))
705 (errcode(ERRCODE_UNDEFINED_OBJECT),
706 errmsg("foreign-data wrapper \"%s\" does not exist", stmt->fdwname)));
708 fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
709 fdwId = HeapTupleGetOid(tp);
711 memset(repl_val, 0, sizeof(repl_val));
712 memset(repl_null, false, sizeof(repl_null));
713 memset(repl_repl, false, sizeof(repl_repl));
715 parse_func_options(stmt->func_options,
716 &handler_given, &fdwhandler,
717 &validator_given, &fdwvalidator);
721 repl_val[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fdwhandler);
722 repl_repl[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = true;
725 * It could be that the behavior of accessing foreign table changes
726 * with the new handler. Warn about this.
729 (errmsg("changing the foreign-data wrapper handler can change behavior of existing foreign tables")));
734 repl_val[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fdwvalidator);
735 repl_repl[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = true;
738 * It could be that existing options for the FDW or dependent SERVER,
739 * USER MAPPING or FOREIGN TABLE objects are no longer valid according
740 * to the new validator. Warn about this.
742 if (OidIsValid(fdwvalidator))
744 (errmsg("changing the foreign-data wrapper validator can cause "
745 "the options for dependent objects to become invalid")));
750 * Validator is not changed, but we need it for validating options.
752 fdwvalidator = fdwForm->fdwvalidator;
756 * If options specified, validate and update.
760 /* Extract the current options */
761 datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
763 Anum_pg_foreign_data_wrapper_fdwoptions,
766 datum = PointerGetDatum(NULL);
768 /* Transform the options */
769 datum = transformGenericOptions(ForeignDataWrapperRelationId,
774 if (PointerIsValid(DatumGetPointer(datum)))
775 repl_val[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = datum;
777 repl_null[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
779 repl_repl[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
782 /* Everything looks good - update the tuple */
783 tp = heap_modify_tuple(tp, RelationGetDescr(rel),
784 repl_val, repl_null, repl_repl);
786 CatalogTupleUpdate(rel, &tp->t_self, tp);
790 ObjectAddressSet(myself, ForeignDataWrapperRelationId, fdwId);
792 /* Update function dependencies if we changed them */
793 if (handler_given || validator_given)
795 ObjectAddress referenced;
798 * Flush all existing dependency records of this FDW on functions; we
799 * assume there can be none other than the ones we are fixing.
801 deleteDependencyRecordsForClass(ForeignDataWrapperRelationId,
806 /* And build new ones. */
808 if (OidIsValid(fdwhandler))
810 referenced.classId = ProcedureRelationId;
811 referenced.objectId = fdwhandler;
812 referenced.objectSubId = 0;
813 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
816 if (OidIsValid(fdwvalidator))
818 referenced.classId = ProcedureRelationId;
819 referenced.objectId = fdwvalidator;
820 referenced.objectSubId = 0;
821 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
825 InvokeObjectPostAlterHook(ForeignDataWrapperRelationId, fdwId, 0);
827 heap_close(rel, RowExclusiveLock);
834 * Drop foreign-data wrapper by OID
837 RemoveForeignDataWrapperById(Oid fdwId)
842 rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
844 tp = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwId));
846 if (!HeapTupleIsValid(tp))
847 elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwId);
849 CatalogTupleDelete(rel, &tp->t_self);
853 heap_close(rel, RowExclusiveLock);
858 * Create a foreign server
861 CreateForeignServer(CreateForeignServerStmt *stmt)
865 Datum values[Natts_pg_foreign_server];
866 bool nulls[Natts_pg_foreign_server];
871 ObjectAddress myself;
872 ObjectAddress referenced;
873 ForeignDataWrapper *fdw;
875 rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
877 /* For now the owner cannot be specified on create. Use effective user ID. */
878 ownerId = GetUserId();
881 * Check that there is no other foreign server by this name.
883 if (GetForeignServerByName(stmt->servername, true) != NULL)
885 (errcode(ERRCODE_DUPLICATE_OBJECT),
886 errmsg("server \"%s\" already exists",
890 * Check that the FDW exists and that we have USAGE on it. Also get the
891 * actual FDW for option validation etc.
893 fdw = GetForeignDataWrapperByName(stmt->fdwname, false);
895 aclresult = pg_foreign_data_wrapper_aclcheck(fdw->fdwid, ownerId, ACL_USAGE);
896 if (aclresult != ACLCHECK_OK)
897 aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname);
900 * Insert tuple into pg_foreign_server.
902 memset(values, 0, sizeof(values));
903 memset(nulls, false, sizeof(nulls));
905 values[Anum_pg_foreign_server_srvname - 1] =
906 DirectFunctionCall1(namein, CStringGetDatum(stmt->servername));
907 values[Anum_pg_foreign_server_srvowner - 1] = ObjectIdGetDatum(ownerId);
908 values[Anum_pg_foreign_server_srvfdw - 1] = ObjectIdGetDatum(fdw->fdwid);
910 /* Add server type if supplied */
911 if (stmt->servertype)
912 values[Anum_pg_foreign_server_srvtype - 1] =
913 CStringGetTextDatum(stmt->servertype);
915 nulls[Anum_pg_foreign_server_srvtype - 1] = true;
917 /* Add server version if supplied */
919 values[Anum_pg_foreign_server_srvversion - 1] =
920 CStringGetTextDatum(stmt->version);
922 nulls[Anum_pg_foreign_server_srvversion - 1] = true;
924 /* Start with a blank acl */
925 nulls[Anum_pg_foreign_server_srvacl - 1] = true;
927 /* Add server options */
928 srvoptions = transformGenericOptions(ForeignServerRelationId,
929 PointerGetDatum(NULL),
933 if (PointerIsValid(DatumGetPointer(srvoptions)))
934 values[Anum_pg_foreign_server_srvoptions - 1] = srvoptions;
936 nulls[Anum_pg_foreign_server_srvoptions - 1] = true;
938 tuple = heap_form_tuple(rel->rd_att, values, nulls);
940 srvId = CatalogTupleInsert(rel, tuple);
942 heap_freetuple(tuple);
944 /* record dependencies */
945 myself.classId = ForeignServerRelationId;
946 myself.objectId = srvId;
947 myself.objectSubId = 0;
949 referenced.classId = ForeignDataWrapperRelationId;
950 referenced.objectId = fdw->fdwid;
951 referenced.objectSubId = 0;
952 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
954 recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId);
956 /* dependency on extension */
957 recordDependencyOnCurrentExtension(&myself, false);
959 /* Post creation hook for new foreign server */
960 InvokeObjectPostCreateHook(ForeignServerRelationId, srvId, 0);
962 heap_close(rel, RowExclusiveLock);
969 * Alter foreign server
972 AlterForeignServer(AlterForeignServerStmt *stmt)
976 Datum repl_val[Natts_pg_foreign_server];
977 bool repl_null[Natts_pg_foreign_server];
978 bool repl_repl[Natts_pg_foreign_server];
980 Form_pg_foreign_server srvForm;
981 ObjectAddress address;
983 rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
985 tp = SearchSysCacheCopy1(FOREIGNSERVERNAME,
986 CStringGetDatum(stmt->servername));
988 if (!HeapTupleIsValid(tp))
990 (errcode(ERRCODE_UNDEFINED_OBJECT),
991 errmsg("server \"%s\" does not exist", stmt->servername)));
993 srvId = HeapTupleGetOid(tp);
994 srvForm = (Form_pg_foreign_server) GETSTRUCT(tp);
997 * Only owner or a superuser can ALTER a SERVER.
999 if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
1000 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
1003 memset(repl_val, 0, sizeof(repl_val));
1004 memset(repl_null, false, sizeof(repl_null));
1005 memset(repl_repl, false, sizeof(repl_repl));
1007 if (stmt->has_version)
1010 * Change the server VERSION string.
1013 repl_val[Anum_pg_foreign_server_srvversion - 1] =
1014 CStringGetTextDatum(stmt->version);
1016 repl_null[Anum_pg_foreign_server_srvversion - 1] = true;
1018 repl_repl[Anum_pg_foreign_server_srvversion - 1] = true;
1023 ForeignDataWrapper *fdw = GetForeignDataWrapper(srvForm->srvfdw);
1027 /* Extract the current srvoptions */
1028 datum = SysCacheGetAttr(FOREIGNSERVEROID,
1030 Anum_pg_foreign_server_srvoptions,
1033 datum = PointerGetDatum(NULL);
1035 /* Prepare the options array */
1036 datum = transformGenericOptions(ForeignServerRelationId,
1041 if (PointerIsValid(DatumGetPointer(datum)))
1042 repl_val[Anum_pg_foreign_server_srvoptions - 1] = datum;
1044 repl_null[Anum_pg_foreign_server_srvoptions - 1] = true;
1046 repl_repl[Anum_pg_foreign_server_srvoptions - 1] = true;
1049 /* Everything looks good - update the tuple */
1050 tp = heap_modify_tuple(tp, RelationGetDescr(rel),
1051 repl_val, repl_null, repl_repl);
1053 CatalogTupleUpdate(rel, &tp->t_self, tp);
1055 InvokeObjectPostAlterHook(ForeignServerRelationId, srvId, 0);
1057 ObjectAddressSet(address, ForeignServerRelationId, srvId);
1061 heap_close(rel, RowExclusiveLock);
1068 * Drop foreign server by OID
1071 RemoveForeignServerById(Oid srvId)
1076 rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
1078 tp = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(srvId));
1080 if (!HeapTupleIsValid(tp))
1081 elog(ERROR, "cache lookup failed for foreign server %u", srvId);
1083 CatalogTupleDelete(rel, &tp->t_self);
1085 ReleaseSysCache(tp);
1087 heap_close(rel, RowExclusiveLock);
1092 * Common routine to check permission for user-mapping-related DDL
1093 * commands. We allow server owners to operate on any mapping, and
1094 * users to operate on their own mapping.
1097 user_mapping_ddl_aclcheck(Oid umuserid, Oid serverid, const char *servername)
1099 Oid curuserid = GetUserId();
1101 if (!pg_foreign_server_ownercheck(serverid, curuserid))
1103 if (umuserid == curuserid)
1105 AclResult aclresult;
1107 aclresult = pg_foreign_server_aclcheck(serverid, curuserid, ACL_USAGE);
1108 if (aclresult != ACLCHECK_OK)
1109 aclcheck_error(aclresult, ACL_KIND_FOREIGN_SERVER, servername);
1112 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
1119 * Create user mapping
1122 CreateUserMapping(CreateUserMappingStmt *stmt)
1126 Datum values[Natts_pg_user_mapping];
1127 bool nulls[Natts_pg_user_mapping];
1131 ObjectAddress myself;
1132 ObjectAddress referenced;
1134 ForeignDataWrapper *fdw;
1135 RoleSpec *role = (RoleSpec *) stmt->user;
1137 rel = heap_open(UserMappingRelationId, RowExclusiveLock);
1139 if (role->roletype == ROLESPEC_PUBLIC)
1140 useId = ACL_ID_PUBLIC;
1142 useId = get_rolespec_oid(stmt->user, false);
1144 /* Check that the server exists. */
1145 srv = GetForeignServerByName(stmt->servername, false);
1147 user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
1150 * Check that the user mapping is unique within server.
1152 umId = GetSysCacheOid2(USERMAPPINGUSERSERVER,
1153 ObjectIdGetDatum(useId),
1154 ObjectIdGetDatum(srv->serverid));
1155 if (OidIsValid(umId))
1157 (errcode(ERRCODE_DUPLICATE_OBJECT),
1158 errmsg("user mapping \"%s\" already exists for server %s",
1159 MappingUserName(useId),
1160 stmt->servername)));
1162 fdw = GetForeignDataWrapper(srv->fdwid);
1165 * Insert tuple into pg_user_mapping.
1167 memset(values, 0, sizeof(values));
1168 memset(nulls, false, sizeof(nulls));
1170 values[Anum_pg_user_mapping_umuser - 1] = ObjectIdGetDatum(useId);
1171 values[Anum_pg_user_mapping_umserver - 1] = ObjectIdGetDatum(srv->serverid);
1173 /* Add user options */
1174 useoptions = transformGenericOptions(UserMappingRelationId,
1175 PointerGetDatum(NULL),
1179 if (PointerIsValid(DatumGetPointer(useoptions)))
1180 values[Anum_pg_user_mapping_umoptions - 1] = useoptions;
1182 nulls[Anum_pg_user_mapping_umoptions - 1] = true;
1184 tuple = heap_form_tuple(rel->rd_att, values, nulls);
1186 umId = CatalogTupleInsert(rel, tuple);
1188 heap_freetuple(tuple);
1190 /* Add dependency on the server */
1191 myself.classId = UserMappingRelationId;
1192 myself.objectId = umId;
1193 myself.objectSubId = 0;
1195 referenced.classId = ForeignServerRelationId;
1196 referenced.objectId = srv->serverid;
1197 referenced.objectSubId = 0;
1198 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1200 if (OidIsValid(useId))
1202 /* Record the mapped user dependency */
1203 recordDependencyOnOwner(UserMappingRelationId, umId, useId);
1206 /* dependency on extension */
1207 recordDependencyOnCurrentExtension(&myself, false);
1209 /* Post creation hook for new user mapping */
1210 InvokeObjectPostCreateHook(UserMappingRelationId, umId, 0);
1212 heap_close(rel, RowExclusiveLock);
1219 * Alter user mapping
1222 AlterUserMapping(AlterUserMappingStmt *stmt)
1226 Datum repl_val[Natts_pg_user_mapping];
1227 bool repl_null[Natts_pg_user_mapping];
1228 bool repl_repl[Natts_pg_user_mapping];
1232 ObjectAddress address;
1233 RoleSpec *role = (RoleSpec *) stmt->user;
1235 rel = heap_open(UserMappingRelationId, RowExclusiveLock);
1237 if (role->roletype == ROLESPEC_PUBLIC)
1238 useId = ACL_ID_PUBLIC;
1240 useId = get_rolespec_oid(stmt->user, false);
1242 srv = GetForeignServerByName(stmt->servername, false);
1244 umId = GetSysCacheOid2(USERMAPPINGUSERSERVER,
1245 ObjectIdGetDatum(useId),
1246 ObjectIdGetDatum(srv->serverid));
1247 if (!OidIsValid(umId))
1249 (errcode(ERRCODE_UNDEFINED_OBJECT),
1250 errmsg("user mapping \"%s\" does not exist for the server",
1251 MappingUserName(useId))));
1253 user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
1255 tp = SearchSysCacheCopy1(USERMAPPINGOID, ObjectIdGetDatum(umId));
1257 if (!HeapTupleIsValid(tp))
1258 elog(ERROR, "cache lookup failed for user mapping %u", umId);
1260 memset(repl_val, 0, sizeof(repl_val));
1261 memset(repl_null, false, sizeof(repl_null));
1262 memset(repl_repl, false, sizeof(repl_repl));
1266 ForeignDataWrapper *fdw;
1271 * Process the options.
1274 fdw = GetForeignDataWrapper(srv->fdwid);
1276 datum = SysCacheGetAttr(USERMAPPINGUSERSERVER,
1278 Anum_pg_user_mapping_umoptions,
1281 datum = PointerGetDatum(NULL);
1283 /* Prepare the options array */
1284 datum = transformGenericOptions(UserMappingRelationId,
1289 if (PointerIsValid(DatumGetPointer(datum)))
1290 repl_val[Anum_pg_user_mapping_umoptions - 1] = datum;
1292 repl_null[Anum_pg_user_mapping_umoptions - 1] = true;
1294 repl_repl[Anum_pg_user_mapping_umoptions - 1] = true;
1297 /* Everything looks good - update the tuple */
1298 tp = heap_modify_tuple(tp, RelationGetDescr(rel),
1299 repl_val, repl_null, repl_repl);
1301 CatalogTupleUpdate(rel, &tp->t_self, tp);
1303 ObjectAddressSet(address, UserMappingRelationId, umId);
1307 heap_close(rel, RowExclusiveLock);
1317 RemoveUserMapping(DropUserMappingStmt *stmt)
1319 ObjectAddress object;
1323 RoleSpec *role = (RoleSpec *) stmt->user;
1325 if (role->roletype == ROLESPEC_PUBLIC)
1326 useId = ACL_ID_PUBLIC;
1329 useId = get_rolespec_oid(stmt->user, stmt->missing_ok);
1330 if (!OidIsValid(useId))
1333 * IF EXISTS specified, role not found and not public. Notice this
1336 elog(NOTICE, "role \"%s\" does not exist, skipping",
1342 srv = GetForeignServerByName(stmt->servername, true);
1346 if (!stmt->missing_ok)
1348 (errcode(ERRCODE_UNDEFINED_OBJECT),
1349 errmsg("server \"%s\" does not exist",
1350 stmt->servername)));
1351 /* IF EXISTS, just note it */
1352 ereport(NOTICE, (errmsg("server does not exist, skipping")));
1356 umId = GetSysCacheOid2(USERMAPPINGUSERSERVER,
1357 ObjectIdGetDatum(useId),
1358 ObjectIdGetDatum(srv->serverid));
1360 if (!OidIsValid(umId))
1362 if (!stmt->missing_ok)
1364 (errcode(ERRCODE_UNDEFINED_OBJECT),
1365 errmsg("user mapping \"%s\" does not exist for the server",
1366 MappingUserName(useId))));
1368 /* IF EXISTS specified, just note it */
1370 (errmsg("user mapping \"%s\" does not exist for the server, skipping",
1371 MappingUserName(useId))));
1375 user_mapping_ddl_aclcheck(useId, srv->serverid, srv->servername);
1380 object.classId = UserMappingRelationId;
1381 object.objectId = umId;
1382 object.objectSubId = 0;
1384 performDeletion(&object, DROP_CASCADE, 0);
1391 * Drop user mapping by OID. This is called to clean up dependencies.
1394 RemoveUserMappingById(Oid umId)
1399 rel = heap_open(UserMappingRelationId, RowExclusiveLock);
1401 tp = SearchSysCache1(USERMAPPINGOID, ObjectIdGetDatum(umId));
1403 if (!HeapTupleIsValid(tp))
1404 elog(ERROR, "cache lookup failed for user mapping %u", umId);
1406 CatalogTupleDelete(rel, &tp->t_self);
1408 ReleaseSysCache(tp);
1410 heap_close(rel, RowExclusiveLock);
1414 * Create a foreign table
1415 * call after DefineRelation().
1418 CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
1422 Datum values[Natts_pg_foreign_table];
1423 bool nulls[Natts_pg_foreign_table];
1425 AclResult aclresult;
1426 ObjectAddress myself;
1427 ObjectAddress referenced;
1429 ForeignDataWrapper *fdw;
1430 ForeignServer *server;
1433 * Advance command counter to ensure the pg_attribute tuple is visible;
1434 * the tuple might be updated to add constraints in previous step.
1436 CommandCounterIncrement();
1438 ftrel = heap_open(ForeignTableRelationId, RowExclusiveLock);
1441 * For now the owner cannot be specified on create. Use effective user ID.
1443 ownerId = GetUserId();
1446 * Check that the foreign server exists and that we have USAGE on it. Also
1447 * get the actual FDW for option validation etc.
1449 server = GetForeignServerByName(stmt->servername, false);
1450 aclresult = pg_foreign_server_aclcheck(server->serverid, ownerId, ACL_USAGE);
1451 if (aclresult != ACLCHECK_OK)
1452 aclcheck_error(aclresult, ACL_KIND_FOREIGN_SERVER, server->servername);
1454 fdw = GetForeignDataWrapper(server->fdwid);
1457 * Insert tuple into pg_foreign_table.
1459 memset(values, 0, sizeof(values));
1460 memset(nulls, false, sizeof(nulls));
1462 values[Anum_pg_foreign_table_ftrelid - 1] = ObjectIdGetDatum(relid);
1463 values[Anum_pg_foreign_table_ftserver - 1] = ObjectIdGetDatum(server->serverid);
1464 /* Add table generic options */
1465 ftoptions = transformGenericOptions(ForeignTableRelationId,
1466 PointerGetDatum(NULL),
1470 if (PointerIsValid(DatumGetPointer(ftoptions)))
1471 values[Anum_pg_foreign_table_ftoptions - 1] = ftoptions;
1473 nulls[Anum_pg_foreign_table_ftoptions - 1] = true;
1475 tuple = heap_form_tuple(ftrel->rd_att, values, nulls);
1477 CatalogTupleInsert(ftrel, tuple);
1479 heap_freetuple(tuple);
1481 /* Add pg_class dependency on the server */
1482 myself.classId = RelationRelationId;
1483 myself.objectId = relid;
1484 myself.objectSubId = 0;
1486 referenced.classId = ForeignServerRelationId;
1487 referenced.objectId = server->serverid;
1488 referenced.objectSubId = 0;
1489 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1491 heap_close(ftrel, RowExclusiveLock);
1495 * Import a foreign schema
1498 ImportForeignSchema(ImportForeignSchemaStmt *stmt)
1500 ForeignServer *server;
1501 ForeignDataWrapper *fdw;
1502 FdwRoutine *fdw_routine;
1503 AclResult aclresult;
1507 /* Check that the foreign server exists and that we have USAGE on it */
1508 server = GetForeignServerByName(stmt->server_name, false);
1509 aclresult = pg_foreign_server_aclcheck(server->serverid, GetUserId(), ACL_USAGE);
1510 if (aclresult != ACLCHECK_OK)
1511 aclcheck_error(aclresult, ACL_KIND_FOREIGN_SERVER, server->servername);
1513 /* Check that the schema exists and we have CREATE permissions on it */
1514 (void) LookupCreationNamespace(stmt->local_schema);
1516 /* Get the FDW and check it supports IMPORT */
1517 fdw = GetForeignDataWrapper(server->fdwid);
1518 if (!OidIsValid(fdw->fdwhandler))
1520 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1521 errmsg("foreign-data wrapper \"%s\" has no handler",
1523 fdw_routine = GetFdwRoutine(fdw->fdwhandler);
1524 if (fdw_routine->ImportForeignSchema == NULL)
1526 (errcode(ERRCODE_FDW_NO_SCHEMAS),
1527 errmsg("foreign-data wrapper \"%s\" does not support IMPORT FOREIGN SCHEMA",
1530 /* Call FDW to get a list of commands */
1531 cmd_list = fdw_routine->ImportForeignSchema(stmt, server->serverid);
1533 /* Parse and execute each command */
1534 foreach(lc, cmd_list)
1536 char *cmd = (char *) lfirst(lc);
1537 import_error_callback_arg callback_arg;
1538 ErrorContextCallback sqlerrcontext;
1539 List *raw_parsetree_list;
1543 * Setup error traceback support for ereport(). This is so that any
1544 * error in the generated SQL will be displayed nicely.
1546 callback_arg.tablename = NULL; /* not known yet */
1547 callback_arg.cmd = cmd;
1548 sqlerrcontext.callback = import_error_callback;
1549 sqlerrcontext.arg = (void *) &callback_arg;
1550 sqlerrcontext.previous = error_context_stack;
1551 error_context_stack = &sqlerrcontext;
1554 * Parse the SQL string into a list of raw parse trees.
1556 raw_parsetree_list = pg_parse_query(cmd);
1559 * Process each parse tree (we allow the FDW to put more than one
1560 * command per string, though this isn't really advised).
1562 foreach(lc2, raw_parsetree_list)
1564 RawStmt *rs = castNode(RawStmt, lfirst(lc2));
1565 CreateForeignTableStmt *cstmt = (CreateForeignTableStmt *) rs->stmt;
1569 * Because we only allow CreateForeignTableStmt, we can skip parse
1570 * analysis, rewrite, and planning steps here.
1572 if (!IsA(cstmt, CreateForeignTableStmt))
1574 "foreign-data wrapper \"%s\" returned incorrect statement type %d",
1575 fdw->fdwname, (int) nodeTag(cstmt));
1577 /* Ignore commands for tables excluded by filter options */
1578 if (!IsImportableForeignTable(cstmt->base.relation->relname, stmt))
1581 /* Enable reporting of current table's name on error */
1582 callback_arg.tablename = cstmt->base.relation->relname;
1584 /* Ensure creation schema is the one given in IMPORT statement */
1585 cstmt->base.relation->schemaname = pstrdup(stmt->local_schema);
1587 /* No planning needed, just make a wrapper PlannedStmt */
1588 pstmt = makeNode(PlannedStmt);
1589 pstmt->commandType = CMD_UTILITY;
1590 pstmt->canSetTag = false;
1591 pstmt->utilityStmt = (Node *) cstmt;
1592 pstmt->stmt_location = rs->stmt_location;
1593 pstmt->stmt_len = rs->stmt_len;
1595 /* Execute statement */
1596 ProcessUtility(pstmt,
1598 PROCESS_UTILITY_SUBCOMMAND, NULL,
1599 None_Receiver, NULL);
1601 /* Be sure to advance the command counter between subcommands */
1602 CommandCounterIncrement();
1604 callback_arg.tablename = NULL;
1607 error_context_stack = sqlerrcontext.previous;
1612 * error context callback to let us supply the failing SQL statement's text
1615 import_error_callback(void *arg)
1617 import_error_callback_arg *callback_arg = (import_error_callback_arg *) arg;
1618 int syntaxerrposition;
1620 /* If it's a syntax error, convert to internal syntax error report */
1621 syntaxerrposition = geterrposition();
1622 if (syntaxerrposition > 0)
1625 internalerrposition(syntaxerrposition);
1626 internalerrquery(callback_arg->cmd);
1629 if (callback_arg->tablename)
1630 errcontext("importing foreign table \"%s\"",
1631 callback_arg->tablename);