From b32355c36314ffc801b458b2d672f0d84f230fa5 Mon Sep 17 00:00:00 2001
From: Masahiko Sawada <sawada.mshk@gmail.com>
Date: Fri, 31 Aug 2018 16:54:53 +0900
Subject: [PATCH 1/2] PoC: Support ROUTINE MAPPING.

---
 src/backend/catalog/Makefile             |   2 +-
 src/backend/catalog/dependency.c         |   9 ++
 src/backend/catalog/objectaddress.c      |  45 +++++++
 src/backend/commands/alter.c             |   1 +
 src/backend/commands/event_trigger.c     |   2 +
 src/backend/commands/foreigncmds.c       | 216 +++++++++++++++++++++++++++++++
 src/backend/commands/tablecmds.c         |   1 +
 src/backend/foreign/foreign.c            |  61 +++++++++
 src/backend/parser/gram.y                | 115 +++++++++++++++-
 src/backend/tcop/utility.c               |  32 +++++
 src/backend/utils/cache/lsyscache.c      |   3 +-
 src/backend/utils/cache/syscache.c       |  34 +++++
 src/include/catalog/dependency.h         |   1 +
 src/include/catalog/indexing.h           |   7 +
 src/include/catalog/pg_routine_mapping.h |  45 +++++++
 src/include/commands/defrem.h            |   4 +
 src/include/foreign/foreign.h            |  10 ++
 src/include/nodes/nodes.h                |   3 +
 src/include/nodes/parsenodes.h           |  29 +++++
 src/include/parser/kwlist.h              |   2 +-
 src/include/utils/syscache.h             |   3 +
 21 files changed, 616 insertions(+), 9 deletions(-)
 create mode 100644 src/include/catalog/pg_routine_mapping.h

diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 0865240..0ba23e5 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -42,7 +42,7 @@ CATALOG_HEADERS := \
 	pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
 	pg_ts_parser.h pg_ts_template.h pg_extension.h \
 	pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
-	pg_foreign_table.h pg_policy.h pg_replication_origin.h \
+	pg_routine_mapping.h pg_foreign_table.h pg_policy.h pg_replication_origin.h \
 	pg_default_acl.h pg_init_privs.h pg_seclabel.h pg_shseclabel.h \
 	pg_collation.h pg_partitioned_table.h pg_range.h pg_transform.h \
 	pg_sequence.h pg_publication.h pg_publication_rel.h pg_subscription.h \
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 4f1d365..046c839 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -48,6 +48,7 @@
 #include "catalog/pg_publication.h"
 #include "catalog/pg_publication_rel.h"
 #include "catalog/pg_rewrite.h"
+#include "catalog/pg_routine_mapping.h"
 #include "catalog/pg_statistic_ext.h"
 #include "catalog/pg_subscription.h"
 #include "catalog/pg_tablespace.h"
@@ -163,6 +164,7 @@ static const Oid object_classes[] = {
 	ForeignDataWrapperRelationId,	/* OCLASS_FDW */
 	ForeignServerRelationId,	/* OCLASS_FOREIGN_SERVER */
 	UserMappingRelationId,		/* OCLASS_USER_MAPPING */
+	RoutineMappingRelationId,	/* OCLASS_ROUTINE_MAPPING */
 	DefaultAclRelationId,		/* OCLASS_DEFACL */
 	ExtensionRelationId,		/* OCLASS_EXTENSION */
 	EventTriggerRelationId,		/* OCLASS_EVENT_TRIGGER */
@@ -1252,6 +1254,10 @@ doDeletion(const ObjectAddress *object, int flags)
 			RemoveUserMappingById(object->objectId);
 			break;
 
+		case OCLASS_ROUTINE_MAPPING:
+			RemoveRoutineMappingById(object->objectId);
+			break;
+
 		case OCLASS_DEFACL:
 			RemoveDefaultACLById(object->objectId);
 			break;
@@ -2514,6 +2520,9 @@ getObjectClass(const ObjectAddress *object)
 		case UserMappingRelationId:
 			return OCLASS_USER_MAPPING;
 
+		case RoutineMappingRelationId:
+			return OCLASS_ROUTINE_MAPPING;
+
 		case DefaultAclRelationId:
 			return OCLASS_DEFACL;
 
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 7db942d..2a40731 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -47,6 +47,7 @@
 #include "catalog/pg_publication.h"
 #include "catalog/pg_publication_rel.h"
 #include "catalog/pg_rewrite.h"
+#include "catalog/pg_routine_mapping.h"
 #include "catalog/pg_statistic_ext.h"
 #include "catalog/pg_subscription.h"
 #include "catalog/pg_tablespace.h"
@@ -3339,6 +3340,25 @@ getObjectDescription(const ObjectAddress *object)
 				break;
 			}
 
+		case OCLASS_ROUTINE_MAPPING:
+			{
+				HeapTuple	tup;
+				Oid			funcid;
+				Form_pg_routine_mapping rmform;
+
+				tup = SearchSysCache1(ROUTINEMAPPINGOID,
+									  ObjectIdGetDatum(object->objectId));
+				if (!HeapTupleIsValid(tup))
+					elog(ERROR, "cache lookup failed for routine mapping %u",
+						 object->objectId);
+				rmform = (Form_pg_routine_mapping) GETSTRUCT(tup);
+				funcid = rmform->rmproc;
+
+				ReleaseSysCache(tup);
+				appendStringInfoString(&buffer, NameStr(rmform->rmname));
+				break;
+			}
+
 		case OCLASS_DEFACL:
 			{
 				Relation	defaclrel;
@@ -4038,6 +4058,10 @@ getObjectTypeDescription(const ObjectAddress *object)
 			appendStringInfoString(&buffer, "user mapping");
 			break;
 
+		case OCLASS_ROUTINE_MAPPING:
+			appendStringInfoString(&buffer, "routine mapping");
+			break;
+
 		case OCLASS_DEFACL:
 			appendStringInfoString(&buffer, "default acl");
 			break;
@@ -4897,6 +4921,27 @@ getObjectIdentityParts(const ObjectAddress *object,
 				break;
 			}
 
+		case OCLASS_ROUTINE_MAPPING:
+			{
+				HeapTuple	tup;
+				Form_pg_routine_mapping rmform;
+
+				tup = SearchSysCache1(ROUTINEMAPPINGOID,
+									  ObjectIdGetDatum(object->objectId));
+				if (!HeapTupleIsValid(tup))
+					elog(ERROR, "cache lookup failed for routine mapping %u",
+						 object->objectId);
+				rmform = (Form_pg_routine_mapping) GETSTRUCT(tup);
+
+				ReleaseSysCache(tup);
+
+				if (objname)
+					*objname = list_make1(pstrdup(NameStr(rmform->rmname)));
+
+				appendStringInfoString(&buffer, NameStr(rmform->rmname));
+				break;
+			}
+
 		case OCLASS_DEFACL:
 			{
 				Relation	defaclrel;
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index eff325c..5352470 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -623,6 +623,7 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
 		case OCLASS_FDW:
 		case OCLASS_FOREIGN_SERVER:
 		case OCLASS_USER_MAPPING:
+		case OCLASS_ROUTINE_MAPPING:
 		case OCLASS_DEFACL:
 		case OCLASS_EXTENSION:
 		case OCLASS_EVENT_TRIGGER:
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index eecc85d..dd174cd 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -110,6 +110,7 @@ static event_trigger_support_data event_trigger_support[] = {
 	{"PUBLICATION", true},
 	{"ROLE", false},
 	{"ROUTINE", true},
+	{"ROUTINE MAPPING", true},
 	{"RULE", true},
 	{"SCHEMA", true},
 	{"SEQUENCE", true},
@@ -1202,6 +1203,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
 		case OCLASS_FDW:
 		case OCLASS_FOREIGN_SERVER:
 		case OCLASS_USER_MAPPING:
+		case OCLASS_ROUTINE_MAPPING:
 		case OCLASS_DEFACL:
 		case OCLASS_EXTENSION:
 		case OCLASS_POLICY:
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index e5dd995..e84f011 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -26,6 +26,7 @@
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "catalog/pg_user_mapping.h"
+#include "catalog/pg_routine_mapping.h"
 #include "commands/defrem.h"
 #include "foreign/fdwapi.h"
 #include "foreign/foreign.h"
@@ -1662,3 +1663,218 @@ import_error_callback(void *arg)
 		errcontext("importing foreign table \"%s\"",
 				   callback_arg->tablename);
 }
+
+/*
+ * Create rouitne mapping
+ */
+ObjectAddress
+CreateRoutineMapping(CreateRoutineMappingStmt *stmt)
+{
+	Relation	rel;
+	Datum		options;
+	Datum		values[Natts_pg_routine_mapping];
+	bool		nulls[Natts_pg_routine_mapping];
+	Oid			rmId;
+	Oid			funcId;
+	NameData	procname;
+	HeapTuple	tuple;
+	ObjectAddress myself;
+	ObjectAddress referenced;
+	ForeignServer		*srv;
+	ForeignDataWrapper	*fdw;
+
+	rel = heap_open(RoutineMappingRelationId, RowExclusiveLock);
+
+	funcId = LookupFuncWithArgs(stmt->objtype, stmt->func, false);
+
+	/* @@@: acl check */
+
+	if (GetRoutineMappingByName(stmt->name, true) != NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_DUPLICATE_OBJECT),
+				 errmsg("routine mapping \"%s\" already exists",
+						stmt->name)));
+
+	srv = GetForeignServerByName(stmt->servername, false);
+	rmId = GetSysCacheOid2(ROUTINEMAPPINGPROCSERVER,
+						   ObjectIdGetDatum(funcId),
+						   ObjectIdGetDatum(srv->serverid));
+
+	if (OidIsValid(rmId))
+	{
+		if (stmt->if_not_exists)
+		{
+			ereport(NOTICE,
+					(errcode(ERRCODE_DUPLICATE_OBJECT),
+					 errmsg("rouitne mapping for \"%s\" already exists for server %s, skipping",
+							NameListToString(stmt->func->objname), stmt->servername)));
+			return InvalidObjectAddress;
+		}
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_DUPLICATE_OBJECT),
+					 errmsg("routine mapping for \"%s\" already exists for server %s",
+							NameListToString(stmt->func->objname), stmt->servername)));
+	}
+
+	fdw = GetForeignDataWrapper(srv->fdwid);
+
+	memset(values, 0, sizeof(values));
+	memset(nulls, false, sizeof(values));
+
+	namestrcpy(&procname, stmt->name);
+	values[Anum_pg_routine_mapping_rmname - 1] = NameGetDatum(&procname);
+	values[Anum_pg_routine_mapping_rmproc - 1] = ObjectIdGetDatum(funcId);
+	values[Anum_pg_routine_mapping_rmserver - 1] = ObjectIdGetDatum(srv->serverid);
+
+	options = transformGenericOptions(RoutineMappingRelationId,
+									  PointerGetDatum(NULL),
+									  stmt->options,
+									  fdw->fdwvalidator);
+
+	if (PointerIsValid(DatumGetPointer(options)))
+		values[Anum_pg_routine_mapping_rmoptions - 1] = options;
+	else
+		nulls[Anum_pg_routine_mapping_rmoptions - 1] = true;
+
+	tuple = heap_form_tuple(rel->rd_att, values, nulls);
+
+	rmId = CatalogTupleInsert(rel, tuple);
+
+	/* Add dependency on the server and proc */
+	myself.classId = RoutineMappingRelationId;
+	myself.objectId = rmId;
+	myself.objectSubId = 0;
+
+	referenced.classId = ForeignServerRelationId;
+	referenced.objectId = srv->serverid;
+	referenced.objectSubId = 0;
+	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+	/* Post creation hook for new user mapping */
+	InvokeObjectPostCreateHook(RoutineMappingRelationId, rmId, 0);
+
+	heap_close(rel, RowExclusiveLock);
+
+	return myself;
+}
+
+Oid
+RemoveRoutineMapping(DropRoutineMappingStmt *stmt)
+{
+	RoutineMapping	*rm;
+	ObjectAddress	object;
+	Oid				rmId;
+
+	rm = GetRoutineMappingByName(stmt->name, true);
+
+	if (!rm)
+	{
+		if (!stmt->missing_ok)
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_OBJECT),
+					 errmsg("routine mapping \"%s\" does not exist",
+							stmt->name)));
+
+		ereport(NOTICE,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 (errmsg("routine mapping \"%s\" done not exist, skipping",
+						 stmt->name))));
+		return InvalidOid;
+	}
+
+	rmId = rm->rmid;
+
+	object.classId = RoutineMappingRelationId;
+	object.objectId = rm->rmid;
+	object.objectSubId = 0;
+
+	performDeletion(&object, DROP_CASCADE, 0);
+
+	return rmId;
+}
+
+void
+RemoveRoutineMappingById(Oid rmId)
+{
+	HeapTuple	tp;
+	Relation	rel;
+
+	rel = heap_open(RoutineMappingRelationId, RowExclusiveLock);
+
+	tp = SearchSysCache1(ROUTINEMAPPINGOID, ObjectIdGetDatum(rmId));
+
+	if (!HeapTupleIsValid(tp))
+		elog(ERROR, "cache lookup failed for routine mapping %u", rmId);
+
+	CatalogTupleDelete(rel, &tp->t_self);
+
+	ReleaseSysCache(tp);
+
+	heap_close(rel, RowExclusiveLock);
+}
+
+ObjectAddress
+AlterRoutineMapping(AlterRoutineMappingStmt *stmt)
+{
+	RoutineMapping	*rm;
+	Relation		rel;
+	Datum			values[Natts_pg_routine_mapping];
+	bool			nulls[Natts_pg_routine_mapping];
+	bool			repl[Natts_pg_user_mapping];
+	HeapTuple		tp;
+	ObjectAddress	address;
+
+	rel = heap_open(RoutineMappingRelationId, RowExclusiveLock);
+
+	rm = GetRoutineMappingByName(stmt->name, false);
+
+	tp = SearchSysCacheCopy1(ROUTINEMAPPINGOID, ObjectIdGetDatum(rm->rmid));
+
+	memset(values, 0, sizeof(values));
+	memset(nulls, false, sizeof(nulls));
+	memset(repl, false, sizeof(repl));
+
+	if (stmt->options)
+	{
+		ForeignServer	*server;
+		ForeignDataWrapper *fdw;
+		Datum	datum;
+		bool	isnull;
+
+		server = GetForeignServer(rm->serverid);
+		fdw = GetForeignDataWrapper(server->fdwid);
+
+		datum = SysCacheGetAttr(ROUTINEMAPPINGPROCSERVER,
+								tp,
+								Anum_pg_routine_mapping_rmoptions,
+								&isnull);
+
+		if (isnull)
+			datum = PointerGetDatum(NULL);
+
+		datum = transformGenericOptions(RoutineMappingRelationId,
+										datum,
+										stmt->options,
+										fdw->fdwvalidator);
+		if (PointerIsValid(DatumGetPointer(datum)))
+			values[Anum_pg_routine_mapping_rmoptions - 1] = datum;
+		else
+			nulls[Anum_pg_routine_mapping_rmoptions - 1] = true;
+
+		repl[Anum_pg_routine_mapping_rmoptions - 1] = true;
+	}
+
+	tp = heap_modify_tuple(tp, RelationGetDescr(rel),
+						   values, nulls, repl);
+
+	CatalogTupleUpdate(rel, &tp->t_self, tp);
+
+	ObjectAddressSet(address, RoutineMappingRelationId, rm->rmid);
+
+	heap_freetuple(tp);
+
+	heap_close(rel, RowExclusiveLock);
+
+	return address;
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index f46af41..687d67d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9648,6 +9648,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 			case OCLASS_FDW:
 			case OCLASS_FOREIGN_SERVER:
 			case OCLASS_USER_MAPPING:
+			case OCLASS_ROUTINE_MAPPING:
 			case OCLASS_DEFACL:
 			case OCLASS_EXTENSION:
 			case OCLASS_EVENT_TRIGGER:
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index eac78a5..0a435af 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -17,6 +17,7 @@
 #include "catalog/pg_foreign_data_wrapper.h"
 #include "catalog/pg_foreign_server.h"
 #include "catalog/pg_foreign_table.h"
+#include "catalog/pg_routine_mapping.h"
 #include "catalog/pg_user_mapping.h"
 #include "foreign/fdwapi.h"
 #include "foreign/foreign.h"
@@ -465,6 +466,66 @@ IsImportableForeignTable(const char *tablename,
 	return false;				/* shouldn't get here */
 }
 
+/*
+ * GetRoutineMapping - look up the routine mapping.
+ */
+RoutineMapping *
+GetRoutineMapping(Oid rmid)
+{
+	Form_pg_routine_mapping	rmform;
+	RoutineMapping *rm;
+	HeapTuple		tp;
+	Datum			datum;
+	bool			isnull;
+
+	tp = SearchSysCache1(ROUTINEMAPPINGOID, ObjectIdGetDatum(rmid));
+
+	if (!HeapTupleIsValid(tp))
+		elog(ERROR, "cache lookup failed for routine mapping");
+
+	rmform = (Form_pg_routine_mapping) GETSTRUCT(tp);
+
+	rm = (RoutineMapping *) palloc(sizeof(RoutineMapping));
+	rm->rmid = rmid;
+	rm->rmname = pstrdup(NameStr(rmform->rmname));
+	rm->procid = rmform->rmproc;
+	rm->serverid = rmform->rmserver;
+
+	datum = SysCacheGetAttr(ROUTINEMAPPINGPROCSERVER,
+							tp,
+							Anum_pg_routine_mapping_rmoptions,
+							&isnull);
+
+	if (isnull)
+		rm->options = NIL;
+	else
+		rm->options = untransformRelOptions(datum);
+
+	ReleaseSysCache(tp);
+
+	return rm;
+}
+
+/*
+ * GetRoutineMappingByName - look up the routine map by name.
+ */
+RoutineMapping *
+GetRoutineMappingByName(const char *rmname, bool missing_ok)
+{
+	Oid	rmid;
+
+	rmid = GetSysCacheOid1(ROUTINEMAPPINGNAME, CStringGetDatum(rmname));
+
+	if (!OidIsValid(rmid) && !missing_ok)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("routine mapping \"%s\" does not exist", rmname)));
+
+	if (!OidIsValid(rmid))
+		return NULL;
+
+	return GetRoutineMapping(rmid);
+}
 
 /*
  * deflist_to_tuplestore - Helper function to convert DefElem list to
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4bd2223..0275a63 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -250,7 +250,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 		AlterObjectDependsStmt AlterObjectSchemaStmt AlterOwnerStmt
 		AlterOperatorStmt AlterSeqStmt AlterSystemStmt AlterTableStmt
 		AlterTblSpcStmt AlterExtensionStmt AlterExtensionContentsStmt AlterForeignTableStmt
-		AlterCompositeTypeStmt AlterUserMappingStmt
+		AlterCompositeTypeStmt AlterUserMappingStmt AlterRoutineMappingStmt
 		AlterRoleStmt AlterRoleSetStmt AlterPolicyStmt
 		AlterDefaultPrivilegesStmt DefACLAction
 		AnalyzeStmt CallStmt ClosePortalStmt ClusterStmt CommentStmt
@@ -260,10 +260,11 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 		CreateSchemaStmt CreateSeqStmt CreateStmt CreateStatsStmt CreateTableSpaceStmt
 		CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
 		CreateAssertStmt CreateTransformStmt CreateTrigStmt CreateEventTrigStmt
-		CreateUserStmt CreateUserMappingStmt CreateRoleStmt CreatePolicyStmt
-		CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
+		CreateUserStmt CreateUserMappingStmt CreateRoutineMappingStmt CreateRoleStmt
+		CreatePolicyStmt CreatedbStmt
+		DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
 		DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
-		DropAssertStmt DropCastStmt DropRoleStmt
+		DropAssertStmt DropCastStmt DropRoleStmt DropRoutineMappingStmt
 		DropdbStmt DropTableSpaceStmt
 		DropTransformStmt
 		DropUserMappingStmt ExplainStmt FetchStmt
@@ -846,6 +847,7 @@ stmt :
 			| AlterPublicationStmt
 			| AlterRoleSetStmt
 			| AlterRoleStmt
+			| AlterRoutineMappingStmt
 			| AlterSubscriptionStmt
 			| AlterTSConfigurationStmt
 			| AlterTSDictionaryStmt
@@ -886,6 +888,7 @@ stmt :
 			| CreateTransformStmt
 			| CreateTrigStmt
 			| CreateEventTrigStmt
+			| CreateRoutineMappingStmt
 			| CreateRoleStmt
 			| CreateUserStmt
 			| CreateUserMappingStmt
@@ -907,6 +910,7 @@ stmt :
 			| DropTableSpaceStmt
 			| DropTransformStmt
 			| DropRoleStmt
+			| DropRoutineMappingStmt
 			| DropUserMappingStmt
 			| DropdbStmt
 			| ExecuteStmt
@@ -5224,6 +5228,107 @@ AlterUserMappingStmt: ALTER USER MAPPING FOR auth_ident SERVER name alter_generi
 
 /*****************************************************************************
  *
+ *		QUERY:
+ *             CREATE ROUTINE MAPPING [IF NOT EXISTS] name
+ *			   		FOR [FUNCTION|PROCEDUER] <function_with_args>
+ *					SERVER name [OPTIONS]
+ *
+ *****************************************************************************/
+
+CreateRoutineMappingStmt: CREATE ROUTINE MAPPING name
+							FOR FUNCTION function_with_argtypes
+							SERVER name create_generic_options
+			{
+				CreateRoutineMappingStmt *n = makeNode(CreateRoutineMappingStmt);
+				n->name = $4;
+				n->objtype = OBJECT_FUNCTION;
+				n->func = $7;
+				n->servername = $9;
+				n->options = $10;
+				n->if_not_exists = false;
+				$$ = (Node *) n;
+			}
+						|  CREATE ROUTINE MAPPING IF_P NOT EXISTS name
+							FOR FUNCTION function_with_argtypes
+							SERVER name create_generic_options
+			{
+				CreateRoutineMappingStmt *n = makeNode(CreateRoutineMappingStmt);
+				n->name = $7;
+				n->objtype = OBJECT_FUNCTION;
+				n->func = $10;
+				n->servername = $12;
+				n->options = $13;
+				n->if_not_exists = true;
+				$$ = (Node *) n;
+			}
+						| CREATE ROUTINE MAPPING name
+							FOR PROCEDURE function_with_argtypes
+							SERVER name create_generic_options
+			{
+				CreateRoutineMappingStmt *n = makeNode(CreateRoutineMappingStmt);
+				n->name = $4;
+				n->objtype = OBJECT_PROCEDURE;
+				n->func = $7;
+				n->servername = $9;
+				n->options = $10;
+				n->if_not_exists = false;
+				$$ = (Node *) n;
+			}
+						| CREATE ROUTINE MAPPING IF_P NOT EXISTS name
+							FOR PROCEDURE function_with_argtypes
+							SERVER name create_generic_options
+			{
+				CreateRoutineMappingStmt *n = makeNode(CreateRoutineMappingStmt);
+				n->name = $7;
+				n->objtype = OBJECT_PROCEDURE;
+				n->func = $10;
+				n->servername = $12;
+				n->options = $13;
+				n->if_not_exists = true;
+				$$ = (Node *) n;
+			}
+	;
+
+/*****************************************************************************
+ *
+ *		QUERY:
+ *             ALTER ROUTINE MAPPING name OPTION
+
+ *****************************************************************************/
+AlterRoutineMappingStmt: ALTER ROUTINE MAPPING name alter_generic_options
+			{
+				AlterRoutineMappingStmt *n = makeNode(AlterRoutineMappingStmt);
+				n->name = $4;
+				n->options = $5;
+				$$ = (Node *) n;
+			}
+	;
+
+/*****************************************************************************
+ *
+ *		QUERY:
+ *             DROP ROUTINE MAPPING name
+
+ *****************************************************************************/
+
+DropRoutineMappingStmt: DROP ROUTINE MAPPING name
+			{
+				DropRoutineMappingStmt *n = makeNode(DropRoutineMappingStmt);
+				n->name = $4;
+				n->missing_ok = false;
+				$$ = (Node *) n;
+			}
+						| DROP ROUTINE MAPPING IF_P EXISTS name
+			{
+				DropRoutineMappingStmt *n = makeNode(DropRoutineMappingStmt);
+				n->name = $6;
+				n->missing_ok = true;
+				$$ = (Node *) n;
+			}
+	;
+
+/*****************************************************************************
+ *
  *		QUERIES:
  *				CREATE POLICY name ON table
  *					[AS { PERMISSIVE | RESTRICTIVE } ]
@@ -15140,7 +15245,6 @@ unreserved_keyword:
 			| LOCK_P
 			| LOCKED
 			| LOGGED
-			| MAPPING
 			| MATCH
 			| MATERIALIZED
 			| MAXVALUE
@@ -15451,6 +15555,7 @@ reserved_keyword:
 			| LIMIT
 			| LOCALTIME
 			| LOCALTIMESTAMP
+			| MAPPING
 			| NOT
 			| NULL_P
 			| OFFSET
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index b5804f6..68a8a51 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -211,6 +211,9 @@ check_xact_readonly(Node *parsetree)
 		case T_CreateUserMappingStmt:
 		case T_AlterUserMappingStmt:
 		case T_DropUserMappingStmt:
+		case T_CreateRoutineMappingStmt:
+		case T_AlterRoutineMappingStmt:
+		case T_DropRoutineMappingStmt:
 		case T_AlterTableSpaceOptionsStmt:
 		case T_CreateForeignTableStmt:
 		case T_ImportForeignSchemaStmt:
@@ -1421,6 +1424,20 @@ ProcessUtilitySlow(ParseState *pstate,
 				commandCollected = true;
 				break;
 
+			case T_CreateRoutineMappingStmt:
+				address = CreateRoutineMapping((CreateRoutineMappingStmt *) parsetree);
+				break;
+
+			case T_AlterRoutineMappingStmt:
+				address = AlterRoutineMapping((AlterRoutineMappingStmt *) parsetree);
+				break;
+
+			case T_DropRoutineMappingStmt:
+				RemoveRoutineMapping((DropRoutineMappingStmt *) parsetree);
+				/* no commands stashed for DROP */
+				commandCollected = true;
+				break;
+
 			case T_CompositeTypeStmt:	/* CREATE TYPE (composite) */
 				{
 					CompositeTypeStmt *stmt = (CompositeTypeStmt *) parsetree;
@@ -2250,6 +2267,18 @@ CreateCommandTag(Node *parsetree)
 			tag = "IMPORT FOREIGN SCHEMA";
 			break;
 
+		case T_CreateRoutineMappingStmt:
+			tag = "CREATE ROUTINE MAPPING";
+			break;
+
+		case T_AlterRoutineMappingStmt:
+			tag = "ALTER ROUTINE MAPPING";
+			break;
+
+		case T_DropRoutineMappingStmt:
+			tag = "DROP ROUTINE MAPPING";
+			break;
+
 		case T_DropStmt:
 			switch (((DropStmt *) parsetree)->removeType)
 			{
@@ -3006,6 +3035,9 @@ GetCommandLogLevel(Node *parsetree)
 		case T_CreateUserMappingStmt:
 		case T_AlterUserMappingStmt:
 		case T_DropUserMappingStmt:
+		case T_CreateRoutineMappingStmt:
+		case T_AlterRoutineMappingStmt:
+		case T_DropRoutineMappingStmt:
 		case T_ImportForeignSchemaStmt:
 			lev = LOGSTMT_DDL;
 			break;
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index bba595a..a585101 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -2838,8 +2838,7 @@ type_is_collatable(Oid typid)
  * Currently this is only consulted for individual tables, not for inheritance
  * trees, so we don't need an "inh" parameter.
  *
- * Calling a hook at this point looks somewhat strange, but is required
- * because the optimizer calls this function without any other way for
+ * Calling a hook at this point looks somewhat strange, but is required * because the optimizer calls this function without any other way for
  * plug-ins to control the result.
  */
 int32
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 2b38178..355cffc 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -59,6 +59,7 @@
 #include "catalog/pg_shdepend.h"
 #include "catalog/pg_shdescription.h"
 #include "catalog/pg_shseclabel.h"
+#include "catalog/pg_routine_mapping.h"
 #include "catalog/pg_replication_origin.h"
 #include "catalog/pg_statistic.h"
 #include "catalog/pg_statistic_ext.h"
@@ -705,6 +706,39 @@ static const struct cachedesc cacheinfo[] = {
 		},
 		16
 	},
+	{RoutineMappingRelationId,	/* ROUTINEMAPPINGOID */
+		RoutineMappingOidIndexId,
+		1,
+		{
+			ObjectIdAttributeNumber,
+			0,
+			0,
+			0
+		},
+		2
+	},
+	{RoutineMappingRelationId,	/* ROUTINEMAPPINGPROCSERVER */
+		RoutineMappingProcServerIndexId,
+		2,
+		{
+			Anum_pg_routine_mapping_rmproc,
+			Anum_pg_routine_mapping_rmserver,
+			0,
+			0
+		},
+		2
+	},
+	{RoutineMappingRelationId,	/* ROUTINEMAPINGNAME */
+		RoutineMappingNameIndexId,
+		1,
+		{
+			Anum_pg_routine_mapping_rmname,
+			0,
+			0,
+			0
+		},
+		2
+	},
 	{RewriteRelationId,			/* RULERELNAME */
 		RewriteRelRulenameIndexId,
 		2,
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 46c271a..f027521 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -173,6 +173,7 @@ typedef enum ObjectClass
 	OCLASS_FDW,					/* pg_foreign_data_wrapper */
 	OCLASS_FOREIGN_SERVER,		/* pg_foreign_server */
 	OCLASS_USER_MAPPING,		/* pg_user_mapping */
+	OCLASS_ROUTINE_MAPPING,		/* pg_routine_mapping */
 	OCLASS_DEFACL,				/* pg_default_acl */
 	OCLASS_EXTENSION,			/* pg_extension */
 	OCLASS_EVENT_TRIGGER,		/* pg_event_trigger */
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 2491582..539e410 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -299,6 +299,13 @@ DECLARE_UNIQUE_INDEX(pg_user_mapping_oid_index, 174, on pg_user_mapping using bt
 DECLARE_UNIQUE_INDEX(pg_user_mapping_user_server_index, 175, on pg_user_mapping using btree(umuser oid_ops, umserver oid_ops));
 #define UserMappingUserServerIndexId	175
 
+DECLARE_UNIQUE_INDEX(pg_routine_mapping_oid_index, 6021, on pg_routine_mapping using btree(oid oid_ops));
+#define RoutineMappingOidIndexId	6021
+DECLARE_UNIQUE_INDEX(pg_routine_mapping_proc_server_index, 6022, on pg_routine_mapping using btree(rmproc oid_ops, rmserver oid_ops));
+#define RoutineMappingProcServerIndexId	6022
+DECLARE_UNIQUE_INDEX(pg_routine_mapping_name_index, 6023, on pg_routine_mapping using btree(rmname name_ops));
+#define RoutineMappingNameIndexId	6023
+
 DECLARE_UNIQUE_INDEX(pg_foreign_table_relid_index, 3119, on pg_foreign_table using btree(ftrelid oid_ops));
 #define ForeignTableRelidIndexId 3119
 
diff --git a/src/include/catalog/pg_routine_mapping.h b/src/include/catalog/pg_routine_mapping.h
new file mode 100644
index 0000000..cf3e7cb
--- /dev/null
+++ b/src/include/catalog/pg_routine_mapping.h
@@ -0,0 +1,45 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_routine_mapping.h
+ *	  definition of the "routine mapping" system catalog (pg_routine_mapping)
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_routine_mapping.h
+ *
+ * NOTES
+ *	  The Catalog.pm module reads this file and derives schema
+ *	  information.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_ROUTINE_MAPPING_H
+#define PG_ROUTINE_MAPPING_H
+
+#include "catalog/genbki.h"
+#include "catalog/pg_routine_mapping_d.h"
+
+/* ----------------
+ *		pg_routine_mapping definition.  cpp turns this into
+ *		typedef struct FormData_pg_routine_mapping
+ * ----------------
+ */
+CATALOG(pg_routine_mapping,6020,RoutineMappingRelationId)
+{
+	NameData	rmname;
+	Oid			rmproc;
+	Oid			rmserver;
+#ifdef CATALOG_VARLEN
+	text		rmoptions[1];
+#endif
+} FormData_pg_routine_mapping;
+
+/* ----------------
+ *		Form_pg_routine_mapping corresponds to a pointer to a tuple with
+ *		the format of pg_routine_mapping relation.
+ * ----------------
+ */
+typedef FormData_pg_routine_mapping *Form_pg_routine_mapping;
+
+#endif			/* PG_ROUTINE_MAPPING_H */
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index 6b83723..3df9d49 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -150,6 +150,10 @@ extern Datum transformGenericOptions(Oid catalogId,
 						Datum oldOptions,
 						List *options,
 						Oid fdwvalidator);
+extern ObjectAddress CreateRoutineMapping(CreateRoutineMappingStmt *stmt);
+extern ObjectAddress AlterRoutineMapping(AlterRoutineMappingStmt *stmt);
+extern Oid RemoveRoutineMapping(DropRoutineMappingStmt *stmt);
+extern void RemoveRoutineMappingById(Oid rmId);
 
 /* commands/amcmds.c */
 extern ObjectAddress CreateAccessMethod(CreateAmStmt *stmt);
diff --git a/src/include/foreign/foreign.h b/src/include/foreign/foreign.h
index 3ca12e6..05393dd 100644
--- a/src/include/foreign/foreign.h
+++ b/src/include/foreign/foreign.h
@@ -68,6 +68,14 @@ typedef struct ForeignTable
 	List	   *options;		/* ftoptions as DefElem list */
 } ForeignTable;
 
+typedef struct RoutineMapping
+{
+	Oid			rmid;			/* Oid of routine mapping */
+	char		*rmname;
+	Oid			procid;			/* local function oid */
+	Oid			serverid;		/* server oid */
+	List		*options;		/* rmoptions as DefElem list */
+} RoutineMapping;
 
 extern ForeignServer *GetForeignServer(Oid serverid);
 extern ForeignServer *GetForeignServerByName(const char *name, bool missing_ok);
@@ -76,6 +84,8 @@ extern ForeignDataWrapper *GetForeignDataWrapper(Oid fdwid);
 extern ForeignDataWrapper *GetForeignDataWrapperByName(const char *name,
 							bool missing_ok);
 extern ForeignTable *GetForeignTable(Oid relid);
+extern RoutineMapping *GetRoutineMapping(Oid rmid);
+extern RoutineMapping *GetRoutineMappingByName(const char *rmname, bool missing_ok);
 
 extern List *GetForeignColumnOptions(Oid relid, AttrNumber attnum);
 
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 697d3d7..70aff71 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -419,6 +419,9 @@ typedef enum NodeTag
 	T_CreateStatsStmt,
 	T_AlterCollationStmt,
 	T_CallStmt,
+	T_CreateRoutineMappingStmt,
+	T_AlterRoutineMappingStmt,
+	T_DropRoutineMappingStmt,
 
 	/*
 	 * TAGS FOR PARSE TREE NODES (parsenodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 07ab1a3..98f761f 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2316,6 +2316,35 @@ typedef struct ImportForeignSchemaStmt
 	List	   *options;		/* list of options to pass to FDW */
 } ImportForeignSchemaStmt;
 
+/* ----------------------
+ *		Create/Drop ROUTINE MAPPING Statements
+ * ----------------------
+ */
+typedef struct CreateRoutineMappingStmt
+{
+	NodeTag			type;
+	char			*name;	/* routine mapping name */
+	ObjectType		objtype;
+	ObjectWithArgs	*func;			/* name and args of function */
+	char			*servername;	/* server name */
+	List			*options;		/* generic option to server */
+	bool			if_not_exists;
+} CreateRoutineMappingStmt;
+
+typedef struct AlterRoutineMappingStmt
+{
+	NodeTag			type;
+	char			*name;
+	List			*options;
+} AlterRoutineMappingStmt;
+
+typedef struct DropRoutineMappingStmt
+{
+	NodeTag			type;
+	char			*name;
+	bool			missing_ok;
+} DropRoutineMappingStmt;
+
 /*----------------------
  *		Create POLICY Statement
  *----------------------
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 23db401..52752d7 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -243,7 +243,7 @@ PG_KEYWORD("location", LOCATION, UNRESERVED_KEYWORD)
 PG_KEYWORD("lock", LOCK_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("locked", LOCKED, UNRESERVED_KEYWORD)
 PG_KEYWORD("logged", LOGGED, UNRESERVED_KEYWORD)
-PG_KEYWORD("mapping", MAPPING, UNRESERVED_KEYWORD)
+PG_KEYWORD("mapping", MAPPING, RESERVED_KEYWORD)
 PG_KEYWORD("match", MATCH, UNRESERVED_KEYWORD)
 PG_KEYWORD("materialized", MATERIALIZED, UNRESERVED_KEYWORD)
 PG_KEYWORD("maxvalue", MAXVALUE, UNRESERVED_KEYWORD)
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 4f33358..96282ea 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -84,6 +84,9 @@ enum SysCacheIdentifier
 	RELOID,
 	REPLORIGIDENT,
 	REPLORIGNAME,
+	ROUTINEMAPPINGOID,
+	ROUTINEMAPPINGPROCSERVER,
+	ROUTINEMAPPINGNAME,
 	RULERELNAME,
 	SEQRELID,
 	STATEXTNAMENSP,
-- 
2.10.5

