From 32a8bc339f47788293228a0f61f4f244ee969a51 Mon Sep 17 00:00:00 2001
From: Shigeru Hanada <hanada@metrosystems.co.jp>
Date: Mon, 4 Apr 2011 19:07:42 +0900
Subject: [PATCH 1/5] Implement COMMENT ON USER MAPPING.

Privilege to COMMENT is similar to ALTER/DROP USER MAPPING.
Needs updates for regression tests and documents.
---
 src/backend/catalog/aclchk.c        |   43 +++++++++++++++++++++++++++++++++++
 src/backend/catalog/objectaddress.c |   31 +++++++++++++++++++++++++
 src/backend/foreign/foreign.c       |   36 +++++++++++++++++++++++++++++
 src/backend/parser/gram.y           |    8 ++++++
 src/include/foreign/foreign.h       |    2 +
 src/include/nodes/parsenodes.h      |    1 +
 src/include/utils/acl.h             |    1 +
 7 files changed, 122 insertions(+), 0 deletions(-)

diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index aa3d59d..bbc6362 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -44,6 +44,7 @@
 #include "catalog/pg_type.h"
 #include "catalog/pg_ts_config.h"
 #include "catalog/pg_ts_dict.h"
+#include "catalog/pg_user_mapping.h"
 #include "commands/dbcommands.h"
 #include "commands/proclang.h"
 #include "commands/tablespace.h"
@@ -4643,6 +4644,48 @@ pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid)
 }
 
 /*
+ * Ownership check for a user mapping (specified by OID).
+ *
+ * User mappings don't have owner, so we treat some users as the owner:
+ *  (1) owner of the foreign server
+ *  (2) user whose name matches the user name of the mapping exactly, and has
+ *      USAGE privilege on the server.
+ */
+bool
+pg_user_mapping_ownercheck(Oid um_oid, Oid roleid)
+{
+	HeapTuple	tuple;
+	Oid			userId;
+	Oid			serverId;
+
+	/* Superusers bypass all permission checking. */
+	if (superuser_arg(roleid))
+		return true;
+
+	tuple = SearchSysCache1(USERMAPPINGOID, ObjectIdGetDatum(um_oid));
+	if (!HeapTupleIsValid(tuple))
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("user mapping with OID %u does not exist",
+						um_oid)));
+
+	userId = ((Form_pg_user_mapping) GETSTRUCT(tuple))->umuser;
+	serverId = ((Form_pg_user_mapping) GETSTRUCT(tuple))->umserver;
+
+	ReleaseSysCache(tuple);
+
+	if (userId == roleid)
+	{
+		AclResult	aclresult;
+
+		aclresult = pg_foreign_server_aclcheck(serverId, roleid, ACL_USAGE);
+		return (aclresult == ACLCHECK_OK);
+	}
+	else
+		return pg_foreign_server_ownercheck(serverId, roleid);
+}
+
+/*
  * Ownership check for a database (specified by OID).
  */
 bool
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 0d21d31..c322fd8 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -48,6 +48,7 @@
 #include "catalog/pg_ts_parser.h"
 #include "catalog/pg_ts_template.h"
 #include "catalog/pg_type.h"
+#include "catalog/pg_user_mapping.h"
 #include "commands/dbcommands.h"
 #include "commands/defrem.h"
 #include "commands/extension.h"
@@ -232,6 +233,22 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
 			address.objectId = get_ts_config_oid(objname, false);
 			address.objectSubId = 0;
 			break;
+		case OBJECT_USER_MAPPING:
+			{
+				char   *username;
+				char   *servername;
+
+				Assert(list_length(objname) == 2);
+
+				username = strVal(linitial(objname));
+				servername = strVal(lsecond(objname));
+				address.classId = UserMappingRelationId;
+				address.objectId = get_user_mapping_oid(username,
+														servername,
+														false);
+				address.objectSubId = 0;
+			}
+			break;
 		default:
 			elog(ERROR, "unrecognized objtype: %d", (int) objtype);
 			/* placate compiler, in case it thinks elog might return */
@@ -682,6 +699,9 @@ object_exists(ObjectAddress address)
 		case ForeignServerRelationId:
 			cache = FOREIGNSERVEROID;
 			break;
+		case UserMappingRelationId:
+			cache = USERMAPPINGOID;
+			break;
 		case TSParserRelationId:
 			cache = TSPARSEROID;
 			break;
@@ -795,6 +815,17 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
 				aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
 							   NameListToString(objname));
 			break;
+		case OBJECT_USER_MAPPING:
+			if (!pg_user_mapping_ownercheck(address.objectId, roleid))
+			{
+				char	   *username = strVal(linitial(objname));
+				ereport(ERROR,
+						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+						 errmsg("must be owner of user mapping FOR %s SERVER %s",
+								username ? username : "public",
+								strVal(lsecond(objname)))));
+			}
+			break;
 		case OBJECT_LANGUAGE:
 			if (!pg_language_ownercheck(address.objectId, roleid))
 				aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE,
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index cda90a6..a84358e 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -538,3 +538,39 @@ get_foreign_server_oid(const char *servername, bool missing_ok)
 				 errmsg("server \"%s\" does not exist", servername)));
 	return oid;
 }
+
+/*
+ * get_user_mapping_oid - given a USER name and SERVER name, look up the OID
+ *
+ * If missing_ok is false, throw an error if name not found.  If true, just
+ * return InvalidOid.
+ */
+Oid
+get_user_mapping_oid(const char *username, const char *servername,
+					 bool missing_ok)
+{
+	Oid			useroid;
+	Oid			serveroid;
+	Oid			oid;
+
+	/* Determine umuser of the mapping */
+	if (username == NULL)
+		useroid = 0;
+	else if (strcmp(username, "current_user") == 0)
+		useroid = GetUserId();
+	else
+		useroid = get_role_oid(username, false);
+
+	/* Determine umserver of the mapping */
+	serveroid = get_foreign_server_oid(servername, false);
+
+	oid = GetSysCacheOid2(USERMAPPINGUSERSERVER,
+						  ObjectIdGetDatum(useroid),
+						  ObjectIdGetDatum(serveroid));
+	if (!OidIsValid(oid) && !missing_ok)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("user mapping \"%s\" for the server does not exist",
+						MappingUserName(useroid))));
+	return oid;
+}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index a22ab66..c234259 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4954,6 +4954,14 @@ CommentStmt:
 					n->comment = $8;
 					$$ = (Node *) n;
 				}
+			| COMMENT ON USER MAPPING FOR auth_ident SERVER name IS comment_text
+				{
+					CommentStmt *n = makeNode(CommentStmt);
+					n->objtype = OBJECT_USER_MAPPING;
+					n->objname = list_make2(makeString($6), makeString($8));
+					n->comment = $10;
+					$$ = (Node *) n;
+				}
 		;
 
 comment_type:
diff --git a/src/include/foreign/foreign.h b/src/include/foreign/foreign.h
index 2fda9e3..f94d940 100644
--- a/src/include/foreign/foreign.h
+++ b/src/include/foreign/foreign.h
@@ -78,5 +78,7 @@ extern ForeignTable *GetForeignTable(Oid relid);
 
 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);
+extern Oid get_user_mapping_oid(const char *username, const char *servername,
+								bool missing_ok);
 
 #endif   /* FOREIGN_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index d9eac76..c464c49 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1133,6 +1133,7 @@ typedef enum ObjectType
 	OBJECT_TSPARSER,
 	OBJECT_TSTEMPLATE,
 	OBJECT_TYPE,
+	OBJECT_USER_MAPPING,
 	OBJECT_VIEW
 } ObjectType;
 
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index b28b764..a74bad5 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -317,6 +317,7 @@ 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_user_mapping_ownercheck(Oid um_oid, Oid roleid);
 extern bool pg_extension_ownercheck(Oid ext_oid, Oid roleid);
 extern bool has_createrole_privilege(Oid roleid);
 
-- 
1.7.3

