]> granicus.if.org Git - postgresql/blobdiff - src/backend/commands/foreigncmds.c
Improve internationalization of messages involving type names
[postgresql] / src / backend / commands / foreigncmds.c
index 21d52e06ba035fa095ce029e0fc5f49970c2d3ff..72c819317e0081a4f52e1d6a2bf75af76be30bbd 100644 (file)
@@ -3,7 +3,7 @@
  * foreigncmds.c
  *       foreign-data wrapper/server creation/manipulation commands
  *
- * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
  *
  *
  * IDENTIFICATION
@@ -14,9 +14,9 @@
 #include "postgres.h"
 
 #include "access/heapam.h"
-#include "access/xact.h"
+#include "access/htup_details.h"
 #include "access/reloptions.h"
-#include "catalog/catalog.h"
+#include "access/xact.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_type.h"
 #include "catalog/pg_user_mapping.h"
 #include "commands/defrem.h"
+#include "foreign/fdwapi.h"
 #include "foreign/foreign.h"
 #include "miscadmin.h"
 #include "parser/parse_func.h"
+#include "tcop/utility.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
 
+typedef struct
+{
+       char       *tablename;
+       char       *cmd;
+} import_error_callback_arg;
+
+/* Internal functions */
+static void import_error_callback(void *arg);
+
+
 /*
  * Convert a DefElem list to the text array format that is used in
- * pg_foreign_data_wrapper, pg_foreign_server, and pg_user_mapping.
+ * pg_foreign_data_wrapper, pg_foreign_server, pg_user_mapping, and
+ * pg_foreign_table.
+ *
  * Returns the array in the form of a Datum, or PointerGetDatum(NULL)
  * if the list is empty.
  *
@@ -79,7 +93,7 @@ optionListToArray(List *options)
 
 
 /*
- * Transform a list of DefElem into text array format. This is substantially
+ * Transform a list of DefElem into text array format.  This is substantially
  * the same thing as optionListToArray(), except we recognize SET/ADD/DROP
  * actions for modifying an existing list of options, which is passed in
  * Datum form as oldOptions.  Also, if fdwvalidator isn't InvalidOid
@@ -88,7 +102,8 @@ optionListToArray(List *options)
  * Returns the array in the form of a Datum, or PointerGetDatum(NULL)
  * if the list is empty.
  *
- * This is used by CREATE/ALTER of FOREIGN DATA WRAPPER/SERVER/USER MAPPING.
+ * This is used by CREATE/ALTER of FOREIGN DATA WRAPPER/SERVER/USER MAPPING/
+ * FOREIGN TABLE.
  */
 Datum
 transformGenericOptions(Oid catalogId,
@@ -122,7 +137,7 @@ transformGenericOptions(Oid catalogId,
 
                /*
                 * It is possible to perform multiple SET/DROP actions on the same
-                * option.      The standard permits this, as long as the options to be
+                * option.  The standard permits this, as long as the options to be
                 * added are unique.  Note that an unspecified action is taken to be
                 * ADD.
                 */
@@ -165,52 +180,48 @@ transformGenericOptions(Oid catalogId,
 
        result = optionListToArray(resultOptions);
 
-       if (OidIsValid(fdwvalidator) && DatumGetPointer(result) != NULL)
-               OidFunctionCall2(fdwvalidator, result, ObjectIdGetDatum(catalogId));
-
-       return result;
-}
-
-
-/*
- * Convert the user mapping user name to OID
- */
-static Oid
-GetUserOidFromMapping(const char *username, bool missing_ok)
-{
-       if (!username)
-               /* PUBLIC user mapping */
-               return InvalidOid;
+       if (OidIsValid(fdwvalidator))
+       {
+               Datum           valarg = result;
 
-       if (strcmp(username, "current_user") == 0)
-               /* map to the owner */
-               return GetUserId();
+               /*
+                * Pass a null options list as an empty array, so that validators
+                * don't have to be declared non-strict to handle the case.
+                */
+               if (DatumGetPointer(valarg) == NULL)
+                       valarg = PointerGetDatum(construct_empty_array(TEXTOID));
+               OidFunctionCall2(fdwvalidator, valarg, ObjectIdGetDatum(catalogId));
+       }
 
-       /* map to provided user */
-       return get_role_oid(username, missing_ok);
+       return result;
 }
 
 
 /*
- * Change foreign-data wrapper owner.
+ * Internal workhorse for changing a data wrapper's owner.
  *
  * Allow this only for superusers; also the new owner must be a
  * superuser.
  */
-void
-AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId)
+static void
+AlterForeignDataWrapperOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
 {
-       HeapTuple       tup;
-       Relation        rel;
-       Oid                     fdwId;
        Form_pg_foreign_data_wrapper form;
+       Datum           repl_val[Natts_pg_foreign_data_wrapper];
+       bool            repl_null[Natts_pg_foreign_data_wrapper];
+       bool            repl_repl[Natts_pg_foreign_data_wrapper];
+       Acl                *newAcl;
+       Datum           aclDatum;
+       bool            isNull;
+
+       form = (Form_pg_foreign_data_wrapper) GETSTRUCT(tup);
 
        /* Must be a superuser to change a FDW owner */
        if (!superuser())
                ereport(ERROR,
                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                 errmsg("permission denied to change owner of foreign-data wrapper \"%s\"",
-                                               name),
+                                               NameStr(form->fdwname)),
                                 errhint("Must be superuser to change owner of a foreign-data wrapper.")));
 
        /* New owner must also be a superuser */
@@ -218,9 +229,59 @@ AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId)
                ereport(ERROR,
                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                 errmsg("permission denied to change owner of foreign-data wrapper \"%s\"",
-                                               name),
+                                               NameStr(form->fdwname)),
                errhint("The owner of a foreign-data wrapper must be a superuser.")));
 
+       if (form->fdwowner != newOwnerId)
+       {
+               memset(repl_null, false, sizeof(repl_null));
+               memset(repl_repl, false, sizeof(repl_repl));
+
+               repl_repl[Anum_pg_foreign_data_wrapper_fdwowner - 1] = true;
+               repl_val[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(newOwnerId);
+
+               aclDatum = heap_getattr(tup,
+                                                               Anum_pg_foreign_data_wrapper_fdwacl,
+                                                               RelationGetDescr(rel),
+                                                               &isNull);
+               /* Null ACLs do not require changes */
+               if (!isNull)
+               {
+                       newAcl = aclnewowner(DatumGetAclP(aclDatum),
+                                                                form->fdwowner, newOwnerId);
+                       repl_repl[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
+                       repl_val[Anum_pg_foreign_data_wrapper_fdwacl - 1] = PointerGetDatum(newAcl);
+               }
+
+               tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null,
+                                                               repl_repl);
+
+               simple_heap_update(rel, &tup->t_self, tup);
+               CatalogUpdateIndexes(rel, tup);
+
+               /* Update owner dependency reference */
+               changeDependencyOnOwner(ForeignDataWrapperRelationId,
+                                                               HeapTupleGetOid(tup),
+                                                               newOwnerId);
+       }
+
+       InvokeObjectPostAlterHook(ForeignDataWrapperRelationId,
+                                                         HeapTupleGetOid(tup), 0);
+}
+
+/*
+ * Change foreign-data wrapper owner -- by name
+ *
+ * Note restrictions in the "_internal" function, above.
+ */
+ObjectAddress
+AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId)
+{
+       Oid                     fdwId;
+       HeapTuple       tup;
+       Relation        rel;
+       ObjectAddress address;
+
        rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
 
        tup = SearchSysCacheCopy1(FOREIGNDATAWRAPPERNAME, CStringGetDatum(name));
@@ -231,49 +292,59 @@ AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId)
                                 errmsg("foreign-data wrapper \"%s\" does not exist", name)));
 
        fdwId = HeapTupleGetOid(tup);
-       form = (Form_pg_foreign_data_wrapper) GETSTRUCT(tup);
 
-       if (form->fdwowner != newOwnerId)
-       {
-               form->fdwowner = newOwnerId;
-
-               simple_heap_update(rel, &tup->t_self, tup);
-               CatalogUpdateIndexes(rel, tup);
+       AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId);
 
-               /* Update owner dependency reference */
-               changeDependencyOnOwner(ForeignDataWrapperRelationId,
-                                                               fdwId,
-                                                               newOwnerId);
-       }
+       ObjectAddressSet(address, ForeignDataWrapperRelationId, fdwId);
 
        heap_freetuple(tup);
 
        heap_close(rel, RowExclusiveLock);
-}
 
+       return address;
+}
 
 /*
- * Change foreign server owner
+ * Change foreign-data wrapper owner -- by OID
+ *
+ * Note restrictions in the "_internal" function, above.
  */
 void
-AlterForeignServerOwner(const char *name, Oid newOwnerId)
+AlterForeignDataWrapperOwner_oid(Oid fwdId, Oid newOwnerId)
 {
        HeapTuple       tup;
        Relation        rel;
-       Oid                     srvId;
-       AclResult       aclresult;
-       Form_pg_foreign_server form;
 
-       rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
+       rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
 
-       tup = SearchSysCacheCopy1(FOREIGNSERVERNAME, CStringGetDatum(name));
+       tup = SearchSysCacheCopy1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fwdId));
 
        if (!HeapTupleIsValid(tup))
                ereport(ERROR,
                                (errcode(ERRCODE_UNDEFINED_OBJECT),
-                                errmsg("server \"%s\" does not exist", name)));
+                 errmsg("foreign-data wrapper with OID %u does not exist", fwdId)));
+
+       AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId);
+
+       heap_freetuple(tup);
+
+       heap_close(rel, RowExclusiveLock);
+}
+
+/*
+ * Internal workhorse for changing a foreign server's owner
+ */
+static void
+AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
+{
+       Form_pg_foreign_server form;
+       Datum           repl_val[Natts_pg_foreign_server];
+       bool            repl_null[Natts_pg_foreign_server];
+       bool            repl_repl[Natts_pg_foreign_server];
+       Acl                *newAcl;
+       Datum           aclDatum;
+       bool            isNull;
 
-       srvId = HeapTupleGetOid(tup);
        form = (Form_pg_foreign_server) GETSTRUCT(tup);
 
        if (form->srvowner != newOwnerId)
@@ -281,10 +352,15 @@ AlterForeignServerOwner(const char *name, Oid newOwnerId)
                /* Superusers can always do it */
                if (!superuser())
                {
+                       Oid                     srvId;
+                       AclResult       aclresult;
+
+                       srvId = HeapTupleGetOid(tup);
+
                        /* Must be owner */
                        if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
                                aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
-                                                          name);
+                                                          NameStr(form->srvname));
 
                        /* Must be able to become new owner */
                        check_is_member_of_role(GetUserId(), newOwnerId);
@@ -299,7 +375,27 @@ AlterForeignServerOwner(const char *name, Oid newOwnerId)
                        }
                }
 
-               form->srvowner = newOwnerId;
+               memset(repl_null, false, sizeof(repl_null));
+               memset(repl_repl, false, sizeof(repl_repl));
+
+               repl_repl[Anum_pg_foreign_server_srvowner - 1] = true;
+               repl_val[Anum_pg_foreign_server_srvowner - 1] = ObjectIdGetDatum(newOwnerId);
+
+               aclDatum = heap_getattr(tup,
+                                                               Anum_pg_foreign_server_srvacl,
+                                                               RelationGetDescr(rel),
+                                                               &isNull);
+               /* Null ACLs do not require changes */
+               if (!isNull)
+               {
+                       newAcl = aclnewowner(DatumGetAclP(aclDatum),
+                                                                form->srvowner, newOwnerId);
+                       repl_repl[Anum_pg_foreign_server_srvacl - 1] = true;
+                       repl_val[Anum_pg_foreign_server_srvacl - 1] = PointerGetDatum(newAcl);
+               }
+
+               tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null,
+                                                               repl_repl);
 
                simple_heap_update(rel, &tup->t_self, tup);
                CatalogUpdateIndexes(rel, tup);
@@ -309,11 +405,67 @@ AlterForeignServerOwner(const char *name, Oid newOwnerId)
                                                                newOwnerId);
        }
 
+       InvokeObjectPostAlterHook(ForeignServerRelationId,
+                                                         HeapTupleGetOid(tup), 0);
+}
+
+/*
+ * Change foreign server owner -- by name
+ */
+ObjectAddress
+AlterForeignServerOwner(const char *name, Oid newOwnerId)
+{
+       Oid                     servOid;
+       HeapTuple       tup;
+       Relation        rel;
+       ObjectAddress address;
+
+       rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
+
+       tup = SearchSysCacheCopy1(FOREIGNSERVERNAME, CStringGetDatum(name));
+
+       if (!HeapTupleIsValid(tup))
+               ereport(ERROR,
+                               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                errmsg("server \"%s\" does not exist", name)));
+
+       servOid = HeapTupleGetOid(tup);
+
+       AlterForeignServerOwner_internal(rel, tup, newOwnerId);
+
+       ObjectAddressSet(address, ForeignServerRelationId, servOid);
+
        heap_freetuple(tup);
 
        heap_close(rel, RowExclusiveLock);
+
+       return address;
 }
 
+/*
+ * Change foreign server owner -- by OID
+ */
+void
+AlterForeignServerOwner_oid(Oid srvId, Oid newOwnerId)
+{
+       HeapTuple       tup;
+       Relation        rel;
+
+       rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
+
+       tup = SearchSysCacheCopy1(FOREIGNSERVEROID, ObjectIdGetDatum(srvId));
+
+       if (!HeapTupleIsValid(tup))
+               ereport(ERROR,
+                               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                errmsg("foreign server with OID %u does not exist", srvId)));
+
+       AlterForeignServerOwner_internal(rel, tup, newOwnerId);
+
+       heap_freetuple(tup);
+
+       heap_close(rel, RowExclusiveLock);
+}
 
 /*
  * Convert a handler function name passed from the parser to an Oid.
@@ -322,19 +474,20 @@ static Oid
 lookup_fdw_handler_func(DefElem *handler)
 {
        Oid                     handlerOid;
+       Oid                     funcargtypes[1];        /* dummy */
 
        if (handler == NULL || handler->arg == NULL)
                return InvalidOid;
 
        /* handlers have no arguments */
-       handlerOid = LookupFuncName((List *) handler->arg, 0, NULL, false);
+       handlerOid = LookupFuncName((List *) handler->arg, 0, funcargtypes, false);
 
        /* check that handler has correct return type */
        if (get_func_rettype(handlerOid) != FDW_HANDLEROID)
                ereport(ERROR,
                                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                                errmsg("function %s must return type \"fdw_handler\"",
-                                               NameListToString((List *) handler->arg))));
+                                errmsg("function %s must return type \"%s\"",
+                                               NameListToString((List *) handler->arg), "fdw_handler")));
 
        return handlerOid;
 }
@@ -405,7 +558,7 @@ parse_func_options(List *func_options,
 /*
  * Create a foreign-data wrapper
  */
-void
+ObjectAddress
 CreateForeignDataWrapper(CreateFdwStmt *stmt)
 {
        Relation        rel;
@@ -422,6 +575,8 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt)
        ObjectAddress myself;
        ObjectAddress referenced;
 
+       rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
+
        /* Must be super user */
        if (!superuser())
                ereport(ERROR,
@@ -445,8 +600,6 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt)
        /*
         * Insert tuple into pg_foreign_data_wrapper.
         */
-       rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
-
        memset(values, 0, sizeof(values));
        memset(nulls, false, sizeof(nulls));
 
@@ -505,20 +658,21 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt)
        recordDependencyOnOwner(ForeignDataWrapperRelationId, fdwId, ownerId);
 
        /* dependency on extension */
-       recordDependencyOnCurrentExtension(&myself);
+       recordDependencyOnCurrentExtension(&myself, false);
 
        /* Post creation hook for new foreign data wrapper */
-       InvokeObjectAccessHook(OAT_POST_CREATE,
-                                                  ForeignDataWrapperRelationId, fdwId, 0);
+       InvokeObjectPostCreateHook(ForeignDataWrapperRelationId, fdwId, 0);
 
        heap_close(rel, RowExclusiveLock);
+
+       return myself;
 }
 
 
 /*
  * Alter foreign-data wrapper
  */
-void
+ObjectAddress
 AlterForeignDataWrapper(AlterFdwStmt *stmt)
 {
        Relation        rel;
@@ -534,6 +688,9 @@ AlterForeignDataWrapper(AlterFdwStmt *stmt)
        bool            validator_given;
        Oid                     fdwhandler;
        Oid                     fdwvalidator;
+       ObjectAddress myself;
+
+       rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
 
        /* Must be super user */
        if (!superuser())
@@ -581,8 +738,9 @@ AlterForeignDataWrapper(AlterFdwStmt *stmt)
                repl_repl[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = true;
 
                /*
-                * It could be that the options for the FDW, SERVER and USER MAPPING
-                * are no longer valid with the new validator.  Warn about this.
+                * It could be that existing options for the FDW or dependent SERVER,
+                * USER MAPPING or FOREIGN TABLE objects are no longer valid according
+                * to the new validator.  Warn about this.
                 */
                if (OidIsValid(fdwvalidator))
                        ereport(WARNING,
@@ -625,9 +783,6 @@ AlterForeignDataWrapper(AlterFdwStmt *stmt)
        }
 
        /* Everything looks good - update the tuple */
-
-       rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
-
        tp = heap_modify_tuple(tp, RelationGetDescr(rel),
                                                   repl_val, repl_null, repl_repl);
 
@@ -636,10 +791,11 @@ AlterForeignDataWrapper(AlterFdwStmt *stmt)
 
        heap_freetuple(tp);
 
+       ObjectAddressSet(myself, ForeignDataWrapperRelationId, fdwId);
+
        /* Update function dependencies if we changed them */
        if (handler_given || validator_given)
        {
-               ObjectAddress myself;
                ObjectAddress referenced;
 
                /*
@@ -652,9 +808,6 @@ AlterForeignDataWrapper(AlterFdwStmt *stmt)
                                                                                DEPENDENCY_NORMAL);
 
                /* And build new ones. */
-               myself.classId = ForeignDataWrapperRelationId;
-               myself.objectId = fdwId;
-               myself.objectSubId = 0;
 
                if (OidIsValid(fdwhandler))
                {
@@ -673,51 +826,11 @@ AlterForeignDataWrapper(AlterFdwStmt *stmt)
                }
        }
 
-       heap_close(rel, RowExclusiveLock);
-}
-
-
-/*
- * Drop foreign-data wrapper
- */
-void
-RemoveForeignDataWrapper(DropFdwStmt *stmt)
-{
-       Oid                     fdwId;
-       ObjectAddress object;
-
-       fdwId = get_foreign_data_wrapper_oid(stmt->fdwname, true);
-
-       if (!superuser())
-               ereport(ERROR,
-                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-                         errmsg("permission denied to drop foreign-data wrapper \"%s\"",
-                                        stmt->fdwname),
-                         errhint("Must be superuser to drop a foreign-data wrapper.")));
+       InvokeObjectPostAlterHook(ForeignDataWrapperRelationId, fdwId, 0);
 
-       if (!OidIsValid(fdwId))
-       {
-               if (!stmt->missing_ok)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_UNDEFINED_OBJECT),
-                                        errmsg("foreign-data wrapper \"%s\" does not exist",
-                                                       stmt->fdwname)));
-
-               /* IF EXISTS specified, just note it */
-               ereport(NOTICE,
-                         (errmsg("foreign-data wrapper \"%s\" does not exist, skipping",
-                                         stmt->fdwname)));
-               return;
-       }
-
-       /*
-        * Do the deletion
-        */
-       object.classId = ForeignDataWrapperRelationId;
-       object.objectId = fdwId;
-       object.objectSubId = 0;
+       heap_close(rel, RowExclusiveLock);
 
-       performDeletion(&object, stmt->behavior);
+       return myself;
 }
 
 
@@ -748,7 +861,7 @@ RemoveForeignDataWrapperById(Oid fdwId)
 /*
  * Create a foreign server
  */
-void
+ObjectAddress
 CreateForeignServer(CreateForeignServerStmt *stmt)
 {
        Relation        rel;
@@ -763,6 +876,8 @@ CreateForeignServer(CreateForeignServerStmt *stmt)
        ObjectAddress referenced;
        ForeignDataWrapper *fdw;
 
+       rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
+
        /* For now the owner cannot be specified on create. Use effective user ID. */
        ownerId = GetUserId();
 
@@ -788,8 +903,6 @@ CreateForeignServer(CreateForeignServerStmt *stmt)
        /*
         * Insert tuple into pg_foreign_server.
         */
-       rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
-
        memset(values, 0, sizeof(values));
        memset(nulls, false, sizeof(nulls));
 
@@ -847,19 +960,21 @@ CreateForeignServer(CreateForeignServerStmt *stmt)
        recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId);
 
        /* dependency on extension */
-       recordDependencyOnCurrentExtension(&myself);
+       recordDependencyOnCurrentExtension(&myself, false);
 
        /* Post creation hook for new foreign server */
-       InvokeObjectAccessHook(OAT_POST_CREATE, ForeignServerRelationId, srvId, 0);
+       InvokeObjectPostCreateHook(ForeignServerRelationId, srvId, 0);
 
        heap_close(rel, RowExclusiveLock);
+
+       return myself;
 }
 
 
 /*
  * Alter foreign server
  */
-void
+ObjectAddress
 AlterForeignServer(AlterForeignServerStmt *stmt)
 {
        Relation        rel;
@@ -869,6 +984,9 @@ AlterForeignServer(AlterForeignServerStmt *stmt)
        bool            repl_repl[Natts_pg_foreign_server];
        Oid                     srvId;
        Form_pg_foreign_server srvForm;
+       ObjectAddress address;
+
+       rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
 
        tp = SearchSysCacheCopy1(FOREIGNSERVERNAME,
                                                         CStringGetDatum(stmt->servername));
@@ -935,57 +1053,21 @@ AlterForeignServer(AlterForeignServerStmt *stmt)
        }
 
        /* Everything looks good - update the tuple */
-
-       rel = heap_open(ForeignServerRelationId, RowExclusiveLock);
-
        tp = heap_modify_tuple(tp, RelationGetDescr(rel),
                                                   repl_val, repl_null, repl_repl);
 
        simple_heap_update(rel, &tp->t_self, tp);
        CatalogUpdateIndexes(rel, tp);
 
-       heap_freetuple(tp);
-
-       heap_close(rel, RowExclusiveLock);
-}
-
-
-/*
- * Drop foreign server
- */
-void
-RemoveForeignServer(DropForeignServerStmt *stmt)
-{
-       Oid                     srvId;
-       ObjectAddress object;
+       InvokeObjectPostAlterHook(ForeignServerRelationId, srvId, 0);
 
-       srvId = get_foreign_server_oid(stmt->servername, true);
+       ObjectAddressSet(address, ForeignServerRelationId, srvId);
 
-       if (!OidIsValid(srvId))
-       {
-               /* Server not found, complain or notice */
-               if (!stmt->missing_ok)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_UNDEFINED_OBJECT),
-                                 errmsg("server \"%s\" does not exist", stmt->servername)));
-
-               /* IF EXISTS specified, just note it */
-               ereport(NOTICE,
-                               (errmsg("server \"%s\" does not exist, skipping",
-                                               stmt->servername)));
-               return;
-       }
-
-       /* Only allow DROP if the server is owned by the user. */
-       if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
-               aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
-                                          stmt->servername);
+       heap_freetuple(tp);
 
-       object.classId = ForeignServerRelationId;
-       object.objectId = srvId;
-       object.objectSubId = 0;
+       heap_close(rel, RowExclusiveLock);
 
-       performDeletion(&object, stmt->behavior);
+       return address;
 }
 
 
@@ -1043,7 +1125,7 @@ user_mapping_ddl_aclcheck(Oid umuserid, Oid serverid, const char *servername)
 /*
  * Create user mapping
  */
-void
+ObjectAddress
 CreateUserMapping(CreateUserMappingStmt *stmt)
 {
        Relation        rel;
@@ -1057,8 +1139,14 @@ CreateUserMapping(CreateUserMappingStmt *stmt)
        ObjectAddress referenced;
        ForeignServer *srv;
        ForeignDataWrapper *fdw;
+       RoleSpec   *role = (RoleSpec *) stmt->user;
 
-       useId = GetUserOidFromMapping(stmt->username, false);
+       rel = heap_open(UserMappingRelationId, RowExclusiveLock);
+
+       if (role->roletype == ROLESPEC_PUBLIC)
+               useId = ACL_ID_PUBLIC;
+       else
+               useId = get_rolespec_oid(stmt->user, false);
 
        /* Check that the server exists. */
        srv = GetForeignServerByName(stmt->servername, false);
@@ -1083,8 +1171,6 @@ CreateUserMapping(CreateUserMappingStmt *stmt)
        /*
         * Insert tuple into pg_user_mapping.
         */
-       rel = heap_open(UserMappingRelationId, RowExclusiveLock);
-
        memset(values, 0, sizeof(values));
        memset(nulls, false, sizeof(nulls));
 
@@ -1127,19 +1213,21 @@ CreateUserMapping(CreateUserMappingStmt *stmt)
        }
 
        /* dependency on extension */
-       recordDependencyOnCurrentExtension(&myself);
+       recordDependencyOnCurrentExtension(&myself, false);
 
        /* Post creation hook for new user mapping */
-       InvokeObjectAccessHook(OAT_POST_CREATE, UserMappingRelationId, umId, 0);
+       InvokeObjectPostCreateHook(UserMappingRelationId, umId, 0);
 
        heap_close(rel, RowExclusiveLock);
+
+       return myself;
 }
 
 
 /*
  * Alter user mapping
  */
-void
+ObjectAddress
 AlterUserMapping(AlterUserMappingStmt *stmt)
 {
        Relation        rel;
@@ -1150,8 +1238,16 @@ AlterUserMapping(AlterUserMappingStmt *stmt)
        Oid                     useId;
        Oid                     umId;
        ForeignServer *srv;
+       ObjectAddress address;
+       RoleSpec   *role = (RoleSpec *) stmt->user;
+
+       rel = heap_open(UserMappingRelationId, RowExclusiveLock);
+
+       if (role->roletype == ROLESPEC_PUBLIC)
+               useId = ACL_ID_PUBLIC;
+       else
+               useId = get_rolespec_oid(stmt->user, false);
 
-       useId = GetUserOidFromMapping(stmt->username, false);
        srv = GetForeignServerByName(stmt->servername, false);
 
        umId = GetSysCacheOid2(USERMAPPINGUSERSERVER,
@@ -1208,45 +1304,53 @@ AlterUserMapping(AlterUserMappingStmt *stmt)
        }
 
        /* Everything looks good - update the tuple */
-
-       rel = heap_open(UserMappingRelationId, RowExclusiveLock);
-
        tp = heap_modify_tuple(tp, RelationGetDescr(rel),
                                                   repl_val, repl_null, repl_repl);
 
        simple_heap_update(rel, &tp->t_self, tp);
        CatalogUpdateIndexes(rel, tp);
 
+       ObjectAddressSet(address, UserMappingRelationId, umId);
+
        heap_freetuple(tp);
 
        heap_close(rel, RowExclusiveLock);
+
+       return address;
 }
 
 
 /*
  * Drop user mapping
  */
-void
+Oid
 RemoveUserMapping(DropUserMappingStmt *stmt)
 {
        ObjectAddress object;
        Oid                     useId;
        Oid                     umId;
        ForeignServer *srv;
+       RoleSpec   *role = (RoleSpec *) stmt->user;
 
-       useId = GetUserOidFromMapping(stmt->username, stmt->missing_ok);
-       srv = GetForeignServerByName(stmt->servername, true);
-
-       if (stmt->username && !OidIsValid(useId))
+       if (role->roletype == ROLESPEC_PUBLIC)
+               useId = ACL_ID_PUBLIC;
+       else
        {
-               /*
-                * IF EXISTS specified, role not found and not public. Notice this and
-                * leave.
-                */
-               elog(NOTICE, "role \"%s\" does not exist, skipping", stmt->username);
-               return;
+               useId = get_rolespec_oid(stmt->user, stmt->missing_ok);
+               if (!OidIsValid(useId))
+               {
+                       /*
+                        * IF EXISTS specified, role not found and not public. Notice this
+                        * and leave.
+                        */
+                       elog(NOTICE, "role \"%s\" does not exist, skipping",
+                                role->rolename);
+                       return InvalidOid;
+               }
        }
 
+       srv = GetForeignServerByName(stmt->servername, true);
+
        if (!srv)
        {
                if (!stmt->missing_ok)
@@ -1256,7 +1360,7 @@ RemoveUserMapping(DropUserMappingStmt *stmt)
                                                        stmt->servername)));
                /* IF EXISTS, just note it */
                ereport(NOTICE, (errmsg("server does not exist, skipping")));
-               return;
+               return InvalidOid;
        }
 
        umId = GetSysCacheOid2(USERMAPPINGUSERSERVER,
@@ -1275,7 +1379,7 @@ RemoveUserMapping(DropUserMappingStmt *stmt)
                ereport(NOTICE,
                (errmsg("user mapping \"%s\" does not exist for the server, skipping",
                                MappingUserName(useId))));
-               return;
+               return InvalidOid;
        }
 
        user_mapping_ddl_aclcheck(useId, srv->serverid, srv->servername);
@@ -1287,7 +1391,9 @@ RemoveUserMapping(DropUserMappingStmt *stmt)
        object.objectId = umId;
        object.objectSubId = 0;
 
-       performDeletion(&object, DROP_CASCADE);
+       performDeletion(&object, DROP_CASCADE, 0);
+
+       return umId;
 }
 
 
@@ -1334,11 +1440,13 @@ CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
        ForeignServer *server;
 
        /*
-        * Advance command counter to ensure the pg_attribute tuple visible; the
-        * tuple might be updated to add constraints in previous step.
+        * Advance command counter to ensure the pg_attribute tuple is visible;
+        * the tuple might be updated to add constraints in previous step.
         */
        CommandCounterIncrement();
 
+       ftrel = heap_open(ForeignTableRelationId, RowExclusiveLock);
+
        /*
         * For now the owner cannot be specified on create. Use effective user ID.
         */
@@ -1358,8 +1466,6 @@ CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
        /*
         * Insert tuple into pg_foreign_table.
         */
-       ftrel = heap_open(ForeignTableRelationId, RowExclusiveLock);
-
        memset(values, 0, sizeof(values));
        memset(nulls, false, sizeof(nulls));
 
@@ -1395,3 +1501,133 @@ CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
 
        heap_close(ftrel, RowExclusiveLock);
 }
+
+/*
+ * Import a foreign schema
+ */
+void
+ImportForeignSchema(ImportForeignSchemaStmt *stmt)
+{
+       ForeignServer *server;
+       ForeignDataWrapper *fdw;
+       FdwRoutine *fdw_routine;
+       AclResult       aclresult;
+       List       *cmd_list;
+       ListCell   *lc;
+
+       /* Check that the foreign server exists and that we have USAGE on it */
+       server = GetForeignServerByName(stmt->server_name, false);
+       aclresult = pg_foreign_server_aclcheck(server->serverid, GetUserId(), ACL_USAGE);
+       if (aclresult != ACLCHECK_OK)
+               aclcheck_error(aclresult, ACL_KIND_FOREIGN_SERVER, server->servername);
+
+       /* Check that the schema exists and we have CREATE permissions on it */
+       (void) LookupCreationNamespace(stmt->local_schema);
+
+       /* Get the FDW and check it supports IMPORT */
+       fdw = GetForeignDataWrapper(server->fdwid);
+       if (!OidIsValid(fdw->fdwhandler))
+               ereport(ERROR,
+                               (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                                errmsg("foreign-data wrapper \"%s\" has no handler",
+                                               fdw->fdwname)));
+       fdw_routine = GetFdwRoutine(fdw->fdwhandler);
+       if (fdw_routine->ImportForeignSchema == NULL)
+               ereport(ERROR,
+                               (errcode(ERRCODE_FDW_NO_SCHEMAS),
+                                errmsg("foreign-data wrapper \"%s\" does not support IMPORT FOREIGN SCHEMA",
+                                               fdw->fdwname)));
+
+       /* Call FDW to get a list of commands */
+       cmd_list = fdw_routine->ImportForeignSchema(stmt, server->serverid);
+
+       /* Parse and execute each command */
+       foreach(lc, cmd_list)
+       {
+               char       *cmd = (char *) lfirst(lc);
+               import_error_callback_arg callback_arg;
+               ErrorContextCallback sqlerrcontext;
+               List       *raw_parsetree_list;
+               ListCell   *lc2;
+
+               /*
+                * Setup error traceback support for ereport().  This is so that any
+                * error in the generated SQL will be displayed nicely.
+                */
+               callback_arg.tablename = NULL;  /* not known yet */
+               callback_arg.cmd = cmd;
+               sqlerrcontext.callback = import_error_callback;
+               sqlerrcontext.arg = (void *) &callback_arg;
+               sqlerrcontext.previous = error_context_stack;
+               error_context_stack = &sqlerrcontext;
+
+               /*
+                * Parse the SQL string into a list of raw parse trees.
+                */
+               raw_parsetree_list = pg_parse_query(cmd);
+
+               /*
+                * Process each parse tree (we allow the FDW to put more than one
+                * command per string, though this isn't really advised).
+                */
+               foreach(lc2, raw_parsetree_list)
+               {
+                       CreateForeignTableStmt *cstmt = lfirst(lc2);
+
+                       /*
+                        * Because we only allow CreateForeignTableStmt, we can skip parse
+                        * analysis, rewrite, and planning steps here.
+                        */
+                       if (!IsA(cstmt, CreateForeignTableStmt))
+                               elog(ERROR,
+                                        "foreign-data wrapper \"%s\" returned incorrect statement type %d",
+                                        fdw->fdwname, (int) nodeTag(cstmt));
+
+                       /* Ignore commands for tables excluded by filter options */
+                       if (!IsImportableForeignTable(cstmt->base.relation->relname, stmt))
+                               continue;
+
+                       /* Enable reporting of current table's name on error */
+                       callback_arg.tablename = cstmt->base.relation->relname;
+
+                       /* Ensure creation schema is the one given in IMPORT statement */
+                       cstmt->base.relation->schemaname = pstrdup(stmt->local_schema);
+
+                       /* Execute statement */
+                       ProcessUtility((Node *) cstmt,
+                                                  cmd,
+                                                  PROCESS_UTILITY_SUBCOMMAND, NULL,
+                                                  None_Receiver, NULL);
+
+                       /* Be sure to advance the command counter between subcommands */
+                       CommandCounterIncrement();
+
+                       callback_arg.tablename = NULL;
+               }
+
+               error_context_stack = sqlerrcontext.previous;
+       }
+}
+
+/*
+ * error context callback to let us supply the failing SQL statement's text
+ */
+static void
+import_error_callback(void *arg)
+{
+       import_error_callback_arg *callback_arg = (import_error_callback_arg *) arg;
+       int                     syntaxerrposition;
+
+       /* If it's a syntax error, convert to internal syntax error report */
+       syntaxerrposition = geterrposition();
+       if (syntaxerrposition > 0)
+       {
+               errposition(0);
+               internalerrposition(syntaxerrposition);
+               internalerrquery(callback_arg->cmd);
+       }
+
+       if (callback_arg->tablename)
+               errcontext("importing foreign table \"%s\"",
+                                  callback_arg->tablename);
+}