diff --git a/doc/src/sgml/ref/comment.sgml b/doc/src/sgml/ref/comment.sgml
index bc848b3..1cdc49f 100644
--- a/doc/src/sgml/ref/comment.sgml
+++ b/doc/src/sgml/ref/comment.sgml
@@ -33,6 +33,7 @@ COMMENT ON
DATABASE object_name |
DOMAIN object_name |
EXTENSION object_name |
+ FOREIGN DATA WRAPPER object_name |
FOREIGN TABLE object_name |
FUNCTION function_name ( [ [ argmode ] [ argname ] argtype [, ...] ] ) |
INDEX object_name |
@@ -45,6 +46,7 @@ COMMENT ON
RULE rule_name ON table_name |
SCHEMA object_name |
SEQUENCE object_name |
+ SERVER object_name |
TABLESPACE object_name |
TEXT SEARCH CONFIGURATION object_name |
TEXT SEARCH DICTIONARY object_name |
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 48fa6d4..7b3c63a 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -4589,6 +4589,33 @@ pg_ts_config_ownercheck(Oid cfg_oid, Oid roleid)
}
/*
+ * Ownership check for a foreign data wrapper (specified by OID).
+ */
+bool
+pg_foreign_data_wrapper_ownercheck(Oid srv_oid, Oid roleid)
+{
+ HeapTuple tuple;
+ Oid ownerId;
+
+ /* Superusers bypass all permission checking. */
+ if (superuser_arg(roleid))
+ return true;
+
+ tuple = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(srv_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("foreign data wrapper with OID %u does not exist",
+ srv_oid)));
+
+ ownerId = ((Form_pg_foreign_data_wrapper) GETSTRUCT(tuple))->fdwowner;
+
+ ReleaseSysCache(tuple);
+
+ return has_privs_of_role(roleid, ownerId);
+}
+
+/*
* Ownership check for a foreign server (specified by OID).
*/
bool
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 880b95d..ca2dea0 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -30,6 +30,8 @@
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
#include "catalog/pg_extension.h"
+#include "catalog/pg_foreign_data_wrapper.h"
+#include "catalog/pg_foreign_server.h"
#include "catalog/pg_language.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_largeobject_metadata.h"
@@ -140,6 +142,8 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
case OBJECT_ROLE:
case OBJECT_SCHEMA:
case OBJECT_LANGUAGE:
+ case OBJECT_FDW:
+ case OBJECT_FOREIGN_SERVER:
address = get_object_address_unqualified(objtype, objname);
break;
case OBJECT_TYPE:
@@ -295,6 +299,12 @@ get_object_address_unqualified(ObjectType objtype, List *qualname)
case OBJECT_LANGUAGE:
msg = gettext_noop("language name cannot be qualified");
break;
+ case OBJECT_FDW:
+ msg = gettext_noop("foreign data wrapper name cannot be qualified");
+ break;
+ case OBJECT_FOREIGN_SERVER:
+ msg = gettext_noop("server name cannot be qualified");
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
msg = NULL; /* placate compiler */
@@ -340,6 +350,16 @@ get_object_address_unqualified(ObjectType objtype, List *qualname)
address.objectId = get_language_oid(name, false);
address.objectSubId = 0;
break;
+ case OBJECT_FDW:
+ address.classId = ForeignDataWrapperRelationId;
+ address.objectId = get_foreign_data_wrapper_oid(name, false);
+ address.objectSubId = 0;
+ break;
+ case OBJECT_FOREIGN_SERVER:
+ address.classId = ForeignServerRelationId;
+ address.objectId = get_foreign_server_oid(name, false);
+ address.objectSubId = 0;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
/* placate compiler, which doesn't know elog won't return */
@@ -655,6 +675,12 @@ object_exists(ObjectAddress address)
case CastRelationId:
indexoid = CastOidIndexId;
break;
+ case ForeignDataWrapperRelationId:
+ cache = FOREIGNDATAWRAPPEROID;
+ break;
+ case ForeignServerRelationId:
+ cache = FOREIGNSERVEROID;
+ break;
case TSParserRelationId:
cache = TSPARSEROID;
break;
@@ -758,6 +784,11 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EXTENSION,
NameListToString(objname));
break;
+ case OBJECT_FDW:
+ if (!pg_foreign_data_wrapper_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FDW,
+ NameListToString(objname));
+ break;
case OBJECT_FOREIGN_SERVER:
if (!pg_foreign_server_ownercheck(address.objectId, roleid))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
@@ -838,7 +869,6 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
errmsg("must have CREATEROLE privilege")));
}
break;
- case OBJECT_FDW:
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
/* We treat these object types as being owned by superusers */
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index acd40c1..c5cd51e 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -1395,3 +1395,42 @@ CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
heap_close(ftrel, RowExclusiveLock);
}
+
+/*
+ * get_foreign_data_wrapper_oid - given a FDW name, look up the OID
+ *
+ * If missing_ok is false, throw an error if name not found. If true, just
+ * return InvalidOid.
+ */
+Oid
+get_foreign_data_wrapper_oid(const char *fdwname, bool missing_ok)
+{
+ Oid oid;
+
+ oid = GetSysCacheOid1(FOREIGNDATAWRAPPERNAME, CStringGetDatum(fdwname));
+ if (!OidIsValid(oid) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("foreign data wrapper \"%s\" does not exist",
+ fdwname)));
+ return oid;
+}
+
+/*
+ * get_foreign_server_oid - given a FDW name, look up the OID
+ *
+ * If missing_ok is false, throw an error if name not found. If true, just
+ * return InvalidOid.
+ */
+Oid
+get_foreign_server_oid(const char *servername, bool missing_ok)
+{
+ Oid oid;
+
+ oid = GetSysCacheOid1(FOREIGNSERVERNAME, CStringGetDatum(servername));
+ if (!OidIsValid(oid) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("foreign server \"%s\" does not exist", servername)));
+ return oid;
+}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 27fdcca..a22ab66 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4787,11 +4787,12 @@ opt_restart_seqs:
* the object associated with the comment. The form of the statement is:
*
* COMMENT ON [ [ DATABASE | DOMAIN | INDEX | SEQUENCE | TABLE | TYPE | VIEW |
- * COLLATION | CONVERSION | LANGUAGE | OPERATOR CLASS | LARGE OBJECT |
- * CAST | COLUMN | SCHEMA | TABLESPACE | EXTENSION | ROLE |
- * TEXT SEARCH PARSER | TEXT SEARCH DICTIONARY |
- * TEXT SEARCH TEMPLATE | TEXT SEARCH CONFIGURATION |
- * FOREIGN TABLE ] |
+ * COLLATION | CONVERSION | LANGUAGE | OPERATOR CLASS |
+ * LARGE OBJECT | CAST | COLUMN | SCHEMA | TABLESPACE |
+ * EXTENSION | ROLE | TEXT SEARCH PARSER |
+ * TEXT SEARCH DICTIONARY | TEXT SEARCH TEMPLATE |
+ * TEXT SEARCH CONFIGURATION | FOREIGN TABLE |
+ * FOREIGN DATA WRAPPER | SERVER ] |
* AGGREGATE (arg1, ...) |
* FUNCTION (arg1, arg2, ...) |
* OPERATOR (leftoperand_typ, rightoperand_typ) |
@@ -4971,6 +4972,8 @@ comment_type:
| EXTENSION { $$ = OBJECT_EXTENSION; }
| ROLE { $$ = OBJECT_ROLE; }
| FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; }
+ | SERVER { $$ = OBJECT_FOREIGN_SERVER; }
+ | FOREIGN DATA_P WRAPPER { $$ = OBJECT_FDW; }
;
comment_text:
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index 157ee39..25c4018 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -167,6 +167,8 @@ extern Datum transformGenericOptions(Oid catalogId,
Datum oldOptions,
List *options,
Oid fdwvalidator);
+extern Oid get_foreign_data_wrapper_oid(const char *fdwname, bool missing_ok);
+extern Oid get_foreign_server_oid(const char *servername, bool missing_ok);
/* support routines in commands/define.c */
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index e96323e..b28b764 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -315,6 +315,7 @@ extern bool pg_collation_ownercheck(Oid coll_oid, Oid roleid);
extern bool pg_conversion_ownercheck(Oid conv_oid, Oid roleid);
extern bool pg_ts_dict_ownercheck(Oid dict_oid, Oid roleid);
extern bool pg_ts_config_ownercheck(Oid cfg_oid, Oid roleid);
+extern bool pg_foreign_data_wrapper_ownercheck(Oid srv_oid, Oid roleid);
extern bool pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid);
extern bool pg_extension_ownercheck(Oid ext_oid, Oid roleid);
extern bool has_createrole_privilege(Oid roleid);
diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out
index a747334..c05bcab 100644
--- a/src/test/regress/expected/foreign_data.out
+++ b/src/test/regress/expected/foreign_data.out
@@ -14,6 +14,7 @@ CREATE ROLE regress_test_role_super SUPERUSER;
CREATE ROLE regress_test_indirect;
CREATE ROLE unprivileged_role;
CREATE FOREIGN DATA WRAPPER dummy;
+COMMENT ON FOREIGN DATA WRAPPER dummy IS 'useless';
CREATE FOREIGN DATA WRAPPER postgresql VALIDATOR postgresql_fdw_validator;
-- At this point we should have 2 built-in wrappers and no servers.
SELECT fdwname, fdwhandler::regproc, fdwvalidator::regproc, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3;
@@ -211,6 +212,7 @@ DROP ROLE regress_test_role_super;
CREATE FOREIGN DATA WRAPPER foo;
CREATE SERVER s1 FOREIGN DATA WRAPPER foo;
+COMMENT ON SERVER s1 IS 'foreign server';
CREATE USER MAPPING FOR current_user SERVER s1;
\dew+
List of foreign-data wrappers
diff --git a/src/test/regress/sql/foreign_data.sql b/src/test/regress/sql/foreign_data.sql
index 3f39785..0d12b98 100644
--- a/src/test/regress/sql/foreign_data.sql
+++ b/src/test/regress/sql/foreign_data.sql
@@ -21,6 +21,7 @@ CREATE ROLE regress_test_indirect;
CREATE ROLE unprivileged_role;
CREATE FOREIGN DATA WRAPPER dummy;
+COMMENT ON FOREIGN DATA WRAPPER dummy IS 'useless';
CREATE FOREIGN DATA WRAPPER postgresql VALIDATOR postgresql_fdw_validator;
-- At this point we should have 2 built-in wrappers and no servers.
@@ -99,6 +100,7 @@ DROP ROLE regress_test_role_super;
CREATE FOREIGN DATA WRAPPER foo;
CREATE SERVER s1 FOREIGN DATA WRAPPER foo;
+COMMENT ON SERVER s1 IS 'foreign server';
CREATE USER MAPPING FOR current_user SERVER s1;
\dew+
\des+